标题
这是参加datawhale的学习打卡活动的学习笔记
学习链接:
http://joyfulpandas.datawhale.club/Content/%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88.html
python基础
for 推导式
在列表中用for语句简单地生成列表
def myfun():
return '我好帅'
L=[myfun() for i in range(5)]
out: ['我好帅', '我好帅', '我好帅', '我好帅', '我好帅']
if 条件赋值
还在用传统条件判断赋值?直接使用条件赋值
# def myfun(a):
# if a>0:
# return 1
# else:
# return 0
def myfun(a):
return 1 if a>0 else 0
myfun(3)
out: 1
匿名函数与map映射
对于简单的,且使用频率低的函数,我们可以直接使用lambda创建匿名函数
fun = lambda x: 2*x
fun(3)
out: 6
当然,一般我们都会直接使用,而不是将匿名函数赋值给一个变量
a =(lambda x: 2*x)(3)
a
out: 6
有了匿名函数,以及map函数,我们就可以用简洁的语法创建一个列表
list(map(lambda x:2*x, range(5)))
out: [0, 2, 4, 6, 8]
zip打包与enumberate
zip打包是将多个可迭代对象打包成一个可迭代的元组
a=[i+1 for i in range(5)]
b=[2*i for i in range(5)]
print(a)
print(b)
for i in zip(a,b):
print(i)
out: [1, 2, 3, 4, 5]
[0, 2, 4, 6, 8]
(1, 0)
(2, 2)
(3, 4)
(4, 6)
(5, 8)
同时,我们也可以用zip函数快速创建字典
a=[i+1 for i in range(5)]
b=[2*i for i in range(5)]
dict(zip(a,b))
out:{1: 0, 2: 2, 3: 4, 4: 6, 5: 8}
zip方法也提供了解压缩的方法
a=[i+1 for i in range(5)]
b=[2*i for i in range(5)]
print(f'a:{a}')
print(f'b:{b}')
c=list(zip(a,b))
d,e=list(zip(*c))
print(f'd:{d}')
print(f'e:{e}')
out: a:[1, 2, 3, 4, 5]
b:[0, 2, 4, 6, 8]
d:(1, 2, 3, 4, 5)
e:(0, 2, 4, 6, 8)
out:{1: 0, 2: 2, 3: 4, 4: 6, 5: 8}
在迭代时,我们常用另一个打包方法enumberate,它是一个特殊的打包函数
L = list('abcd')
for index, value in enumerate(L):
print(index, value)
out:0 a
1 b
2 c
3 d
numpy基础
数组创建和操作
创建向量常用函数 | 功能 |
---|---|
linspace | 等差数列,起始、终止(包含)、样本个数 |
arange | 等差数列,起始、终止(不包含)、步长 |
ones | 创建全为1的向量 |
zeros | 创建全为0的向量 |
创建矩阵常用函数 | 功能 |
---|---|
eye | 创建单位阵 |
empty | 创建空矩阵,申请空间但是不赋值 |
full | 用某个数填满矩阵 |
print(np.linspace(1,10,5))
# [ 1. 3.25 5.5 7.75 10. ]
print(np.arange(1,10,2))
# [1 3 5 7 9]
print(np.eye(3,4,-1))
'''
[[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 1. 0. 0.]]
'''
print(np.ones(5))
# [1. 1. 1. 1. 1.]
print(np.zeros(5))
# [0. 0. 0. 0. 0.]
print(np.full((3,4),5))
'''
[[5 5 5 5]
[5 5 5 5]
[5 5 5 5]]
'''
数组变形与合并
用到的方法:
方法 | 作用 |
---|---|
.T | 转置 |
r_ | 水平拼接 |
c_ | 垂直拼接 |
reshape | 修改矩阵形状 |
需要特别注意reshape函数的一个参数order
,它被用来控制填充的顺序
C是按照行读取和填充,F是按照列读取和填充
a=np.arange(8).reshape(2,4)
print(a)
print(a.reshape(4,2,order='C'))# 按照行读取和填充
'''
[[0 1]
[2 3]
[4 5]
[6 7]]
'''
print(a.reshape(4,2,order='F'))# 按照列读取和填充
'''
[[0 2]
[4 6]
[1 3]
[5 7]]
'''
数组切片与索引
像python自带的列表一样,numpy的数组也可以继续切片或者索引操作。
a=np.arange(9).reshape(3,-1)
print(a[:,1])
# [1 4 7]
print(a[2,:])
# [6 7 8]
print(a[:,[0,2]])
'''
[[0 2]
[3 5]
[6 8]]
'''
常用函数
函数名 | 函数作用 |
---|---|
where | 条件函数,可以指定满足条件与不满足条件位置对应的填充值 |
nonzero, argmax, argmin | 这三个函数返回的都是索引,nonzero返回非零数的索引,argmax, argmin分别返回最大和最小数的索引 |
any | any指当序列至少 存在一个 True或非零元素时返回True,否则返回False, |
all | all指当序列元素 全为 True或非零元素时返回True,否则返回False |
cumprod | 累乘 |
cumsum | 累加 |
diff | 表示和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1 |
max, min, mean, median, std, var, sum, quantile | 分别表示最大值,最小值,平均值,中位数,标准差,方差,求和,分位数,如果数据中有nan,可以在统计函数前面加nan来排除nan |
矩阵运算
方法 | 作用 |
---|---|
dot | 计算向量的内积 |
np.linalg.norm | 计算范数,通过ord 参数计算不同的范数 |
@ | 矩阵乘法 |
ord参数具体如下
ord | norm for matrices | norm for vectors |
---|---|---|
None | Frobenius norm | 2-norm |
‘fro’ | Frobenius norm F-范数 | / |
‘nuc’ | nuclear norm | / |
inf | max(sum(abs(x), axis=1)) | max(abs(x)) |
-inf | min(sum(abs(x), axis=1)) | min(abs(x)) |
0 | / | sum(x != 0) |
1 | max(sum(abs(x), axis=0)) | as below |
-1 | min(sum(abs(x), axis=0)) | as below |
2 | 2-norm (largest sing. value) 最大奇异值 | as below |
-2 | smallest singular value 最小奇异值 | as below |
other | / | sum(abs(x)**ord)**(1./ord) |
练习题
Ex1:利用列表推导式写矩阵乘法¶
一般的矩阵乘法根据公式,可以由三重循环写出,请将其改写为列表推导式的形式。
答案:
import numpy as np
M1 = np.random.rand(2,3)
M2 = np.random.rand(3,4)
print(M1)
print(M2)
# 取向量 取数 求和
res=np.array([[np.sum([i*j for i,j in zip(a,b)]) for b in M2.T]for a in M1])
(np.abs((M1@M2-res)<1e-15)).all()
外层循环是第一个向量的行,内层循环是第二个向量转置的行,再利用求和求解最终的结果
Ex2:更新矩阵
由于每一行的元素乘的系数都一样,考虑将其存储在一个数组中,观察系数,是由A中元素全部取倒数,然后对行进行求和得到的。最终只需要将原矩阵乘上相应的系数就好了
import numpy as np
a=np.arange(9).reshape(3,3)+1
c=np.array([np.sum(1/a,axis=1)]).T
b=a*c
b
Ex3:卡方统计量
设矩阵
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
和矩阵乘法类似,注意迭代顺序,将行作为外层循环,列作为内层循环,得到B矩阵。
有了B矩阵,计算卡方统计量就变得非常简单了,只需要将A,B看成一个整体去计算就好
答案:
sum_A=np.sum(A)
B=np.array([[np.sum(A[j,:])*np.sum(A[:,i])/sum_A for i in range(A.shape[1])] for j in range(A.shape[0])])
res=(B-A)**2/B
res
Ex4:改进矩阵计算的性能
设 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。
现有某人根据如下给定的样例数据计算 𝑅 的值,请充分利用Numpy中的函数,基于此问题改进这段代码的性能。
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)
答案:
(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z).sum()
Ex5:连续整数的最大长度
输入一个整数的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,5,6,7,8,10,11,12])
f = lambda x:np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])).max()
f([0,1,2,5,6])
f([-5,-4,-3,-2,-1,0])