pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了《利用Python进行数据分析》
可以使用for循环推导列表,此处for可以是多层嵌套表达,原理和多层循环之后向列表中插入一个值相同,因此列表推导式for前面的表达式可以使用正常表达式的所有操作
d = {
'a':1, 'b':2}
[i+'_'+str(d[i])+'_'+j+'_'+str(k) for i in d.keys() for j in ['p', 'q'] for k in [4, 5]]
['a_1_p_4',
'a_1_p_5',
'a_1_q_4',
'a_1_q_5',
'b_2_p_4',
'b_2_p_5',
'b_2_q_4',
'b_2_q_5']
为列表推导式加入条件语句
# 可以用条件表达式语法糖简洁地写出两个列表两两对比的较大值
[i if i>j else j for i in ['a', 't', 'm'] for j in ['b', 'e', 'z']]
['b', 'e', 'z', 't', 't', 'z', 'm', 'm', 'z']
注意如果只写if,没有else的话条件语句应该放在for之后
如果有else的话应该放在for之前
否则是错误写法
[i for i in range(7) if i<5]
[0, 1, 2, 3, 4]
[i if i<5 else 8 for i in range(7)]
[0, 1, 2, 3, 4, 8, 8]
列表推导式同样可以用在字典、集合上,如下:
# 由两个列表生成字典
{
i:j for i in ['a', 'b', 'c'] for j in [1, 2, 3]}
{'a': 3, 'b': 3, 'c': 3}
{
i for i in [1,1,1,2,3,4,4]}
{1, 2, 3, 4}
列表推导式本身也可以嵌套
from collections import Counter
words = ['collections', 'error', 'employee']
[[ch for ch in word] for word in words]
[['c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', 's'],
['e', 'r', 'r', 'o', 'r'],
['e', 'm', 'p', 'l', 'o', 'y', 'e', 'e']]
[(lambda x: 2*x)(i) for i in range(5)]
[0, 2, 4, 6, 8]
通过lambda函数可以自定义排序方式
employees = [
['Alan Turing', 25, 10000],
['Alan Turing', 30, 1000],
['John Hopkins', 18, 1000],
['Mikhail Tal', 40, 15000],
]
employees.sort(key=lambda x:[x[2], x[0], x[1]])
employees
[['Alan Turing', 30, 1000],
['John Hopkins', 18, 1000],
['Alan Turing', 25, 10000],
['Mikhail Tal', 40, 15000]]
这里总结一些学习之后我想到的zip的用法
m = [[1,2,3],[4,5,6],[7,8,9]]
list(zip(*m))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
dict(zip(['a', 'b', 'c'], [1, 2, 3]))
{'a': 1, 'b': 2, 'c': 3}
import itertools
names = ['alan', 'adam', 'wes', 'will']
nameid = [1, 3, 5]
利用groupby分组
for ch, words in itertools.groupby(names, lambda x: x[0]):
print(ch, list(words))
a ['alan', 'adam']
w ['wes', 'will']
利用permutations求一个列表的所有排列
print(list(itertools.permutations(nameid, 3)))
print(list(itertools.permutations(nameid, 2)))
[(1, 3, 5), (1, 5, 3), (3, 1, 5), (3, 5, 1), (5, 1, 3), (5, 3, 1)]
[(1, 3), (1, 5), (3, 1), (3, 5), (5, 1), (5, 3)]
利用combinations求一个列表的所有组合
list(itertools.combinations(names, 2))
[('alan', 'adam'),
('alan', 'wes'),
('alan', 'will'),
('adam', 'wes'),
('adam', 'will'),
('wes', 'will')]
远昊大佬写的很全面了,我在这里记录一些自己的理解和补充
先贴下原文中的函数帮助自己回顾
矩阵构造:
随机数:
变形与合并:
切片与索引:
常用函数:
线性代数:
import numpy as np
1. range 与 np.arange
arange和python range的区别是arange步长可以为小数,如:
try:
range(1, 3.6, 0.2)
except Exception as e:
print(e)
'float' object cannot be interpreted as an integer
np.arange(1,3.6,0.2)
array([1. , 1.2, 1.4, 1.6, 1.8, 2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4])
2.np.full
np.full的原理是用了broadcast,可以传一行进行广播,也可以传一列进行广播
np.full((2,3), [[1],[2]]) # 按列广播
array([[1, 1, 1],
[2, 2, 2]])
np.full((2,3), [1,2,3]) # 按行广播
array([[1, 2, 3],
[1, 2, 3]])
一般的矩阵乘法根据公式,可以由三重循环写出,请将其改写为列表推导式的形式。
M1 = np.random.rand(2,3)
M2 = np.random.rand(3,4)
res = np.empty((M1.shape[0],M2.shape[1]))
for i in range(M1.shape[0]):
for j in range(M2.shape[1]):
item = 0
for k in range(M1.shape[1]):
item += M1[i
][k] * M2[k][j]
res[i][j] = item
((M1@M2 - res) < 1e-15).all() # 排除数值误差
True
#自己想的做法(是错的)
res = np.zeros((M1.shape[0],M2.shape[1]))
res = [[res[i][j]+M1[i][k]*M2[k][j] for j in range(M2.shape[1]) for k in range(M1.shape[1])] for i in range(M1.shape[0])]
res
[[0.2957157379191776,
0.241770264213699,
0.9332774207010959,
0.30630566767322404,
0.15187789479179117,
0.16530291729334373,
0.2668142522248828,
0.1131105799514749,
0.5682066531753045,
0.00980039554047657,
0.3719339829279964,
0.9637859016065992],
[0.6218645045085477,
0.2489711985400718,
0.9544693752777721,
0.6441342067084365,
0.15640145665156052,
0.16905645491995966,
0.5610871911084889,
0.11647948828475355,
0.5811089363733815,
0.020609380344957975,
0.3830117397130221,
0.9856706130497891]]
#看到远昊大佬打出了sum的时候恍然大悟
res = np.zeros((M1.shape[0],M2.shape[1]))
res = [[sum([M1[i][k]*M2[k][j] for k in range(M1.shape[1])]) for j in range(M2.shape[1])] for i in range(M1.shape[0])]
((M1@M2 - res) < 1e-15).all() # 排除数值误差
True
设矩阵 A m × n A_{m×n} Am×n ,现在对 A A A 中的每一个元素进行更新生成矩阵 B B B ,更新方法是 B i j = A i j ∑ k = 1 n 1 A i k B_{ij}=A_{ij}\sum_{k=1}^n\frac{1}{A_{ik}} Bij=Aij∑k=1nAik1 ,例如下面的矩阵为 A A A ,则 B 2 , 2 = 5 × ( 1 4 + 1 5 + 1 6 ) = 37 12 B_{2,2}=5\times(\frac{1}{4}+\frac{1}{5}+\frac{1}{6})=\frac{37}{12} B2,2=5×(41+51+61)=1237 ,请利用 Numpy
高效实现。
KaTeX parse error: No such environment: split at position 7: \begin{̲s̲p̲l̲i̲t̲}̲A=\left[ \begin…
37/12
1+1/2+1/3
1.8333333333333333
我的答案,发现与大佬不一致,而且我构造原数组的方式好low
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a*(((1/a).T.sum(0)))
b
array([[ 1.83333333, 1.23333333, 1.13690476],
[ 7.33333333, 3.08333333, 2.27380952],
[12.83333333, 4.93333333, 3.41071429]])
我的思路是将1/原矩阵转置后按行求和,再和原矩阵相乘
错误的地方在于不管是按行求和还是按列求和,结果都是一个向量,因此要显式地将向量构造成列向量,再利用广播机制相乘
#验证我的错误:从如下代码看出,sum(1)虽然是按列求和,但结果并不是列向量
print(a.sum(0))
print(a.sum(1))
[12 15 18]
[ 6 15 24]
a*(1/a).sum(1).reshape(-1,1)
array([[1.83333333, 3.66666667, 5.5 ],
[2.46666667, 3.08333333, 3.7 ],
[2.65277778, 3.03174603, 3.41071429]])
设矩阵 A m × n A_{m\times n} Am×n,记 B i j = ( ∑ i = 1 m A i j ) × ( ∑ j = 1 n A i j ) ∑ i = 1 m ∑ j = 1 n A i j B_{ij} = \frac{(\sum_{i=1}^mA_{ij})\times (\sum_{j=1}^nA_{ij})}{\sum_{i=1}^m\sum_{j=1}^nA_{ij}} Bij=∑i=1m∑j=1nAij(∑i=1mAij)×(∑j=1nAij),定义卡方值如下:
χ 2 = ∑ i = 1 m ∑ j = 1 n ( A i j − B i j ) 2 B i j \chi^2 = \sum_{i=1}^m\sum_{j=1}^n\frac{(A_{ij}-B_{ij})^2}{B_{ij}} χ2=i=1∑mj=1∑nBij(Aij−Bij)2
请利用Numpy
对给定的矩阵 A A A计算 χ 2 \chi^2 χ2
np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
A
array([[15, 10, 13, 13, 17],
[19, 13, 15, 12, 14],
[17, 16, 18, 18, 11],
[16, 17, 17, 18, 11],
[15, 19, 18, 19, 14],
[13, 10, 13, 15, 10],
[12, 13, 18, 11, 13],
[13, 13, 17, 10, 11]])
#代码稍有不同,但结果是一样的,算作自己完全答对!
B = (A.sum(1).reshape(-1,1) * A.sum(0).reshape(1,-1))/B.sum()
kai = ((A-B)**2/B).sum()
kai
331776.02052460413
B = (A.sum(0) * A.sum(1).reshape(-1,1))/B.sum()
kai = ((A-B)**2/B).sum()
kai
B
array([[0.02450973, 0.0226715 , 0.02634796, 0.02369274, 0.02062902],
[0.02631192, 0.02433852, 0.02828531, 0.02543485, 0.02214586],
[0.02883498, 0.02667235, 0.0309976 , 0.02787381, 0.02426944],
[0.02847454, 0.02633895, 0.03061013, 0.02752539, 0.02396607],
[0.03063716, 0.02833938, 0.03293495, 0.02961592, 0.02578628],
[0.02198667, 0.02033767, 0.02363567, 0.02125378, 0.01850545],
[0.02414929, 0.0223381 , 0.02596049, 0.02334432, 0.02032566],
[0.02306798, 0.02133788, 0.02479808, 0.02229905, 0.01941555]])
设 Z Z Z为 m × n m×n m×n的矩阵, B B B和 U U U分别是 m × p m×p m×p和 p × n p×n p×n的矩阵, B i B_i Bi为 B B B的第 i i i行, U j U_j Uj为 U U U的第 j j j列,下面定义 R = ∑ i = 1 m ∑ j = 1 n ∥ B i − U j ∥ 2 2 Z i j \displaystyle R=\sum_{i=1}^m\sum_{j=1}^n\|B_i-U_j\|_2^2Z_{ij} R=i=1∑mj=1∑n∥Bi−Uj∥22Zij,其中 ∥ a ∥ 2 2 \|\mathbf{a}\|_2^2 ∥a∥22表示向量 a a a的分量平方和 ∑ i a i 2 \sum_i a_i^2 ∑iai2。
现有某人根据如下给定的样例数据计算 R R R的值,请充分利用Numpy
中的函数,基于此问题改进这段代码的性能。
%time
np.random.seed(0)
m, n, p = 100, 80, 50
B = np.random.randint(0, 2, (m, p))
U = np.random.randint(0, 2, (p, n))
Z = np.random.randint(0, 2, (m, n))
def solution(B=B, U=U, Z=Z):
L_res = []
for i in range(m):
for j in range(n):
norm_value = ((B[i]-U[:,j])**2).sum()
L_res.append(norm_value*Z[i][j])
return sum(L_res)
solution(B, U, Z)
CPU times: user 4 µs, sys: 1 µs, total: 5 µs
Wall time: 7.15 µs
100566
我真是个小天才,琢磨了半天竟然真做对了!
我的思路是R的表达式中首先Z可以忽略,主要是求前面的部分,前面的部分可以用(a-b)平方 = a平方 + b平方 -2ab的原理拆解,然后实现这个表达即可
发现与答案一致cool
我解题的方法是首先在草稿纸上构造了一个B是(2,2) U是(2,1)的矩阵,想了想计算过程,然后测试一下前面的拆解是否正确,发现是正确的,然后就改造成高维的再实现一下,改造的过程主要会出一些维度不对的报错,随时用np.shape()查看就可纠错
%time
t = (((B**2).sum(1).reshape(-1,1) + (U**2).sum(0).reshape(1,-1)) - (B.dot(U)*2)) * Z
t.sum()
CPU times: user 2 µs, sys: 1 µs, total: 3 µs
Wall time: 5.96 µs
100566
输入一个整数的Numpy
数组,返回其中递增连续整数子数组的最大长度,正向是指递增方向。例如,输入[1,2,5,6,7],[5,6,7]为具有最大长度的连续整数子数组,因此输出3;输入[3,2,1,2,3,4,6],[1,2,3,4]为具有最大长度的连续整数子数组,因此输出4。请充分利用Numpy
的内置函数完成。(提示:考虑使用nonzero, diff
函数)
#这个题没做出来,看了答案之后一步一步拆解看看过程是怎样的
a = np.array([3,2,1,2,3,4,6])
np.diff(np.nonzero(np.r_[1, np.diff(a)!=1, 1])).max()
4
#这一步想出来了,找连续上升用diff可以判断
np.diff(a)!=1
array([ True, True, False, False, False, True])
#这里发现布尔数组和数值数组合并的时候布尔数组会自动变成0和1,这里实际是在计算连续上升的值的区间,0表示在一个区间
np.r_[1, np.diff(a)!=1, 1]
array([1, 1, 1, 0, 0, 0, 1, 1])
#通过判断0把区间转化成了区间端点,然后求差值就可以知道区间的长度,再求最大值
np.nonzero(np.r_[1, np.diff(a)!=1, 1])
(array([0, 1, 2, 6, 7]),)