joyful-pandas 01 - 预备知识
python基础+numpy基础
使用命令生成 jupyter nbconvert --to markdown E:PycharmProjectsTianChiProject 0_山枫叶纷飞competitions 08_joyful-pandas 1-python基础.ipynb 生成
# 下面举一个例子,截断列表中超过 5 的元素:
L=[x for x in range(0,10)]
[x if x<=5 else 5 for x in L]
[0, 1, 2, 3, 4, 5, 5, 5, 5, 5]
# 实现乘于2倍输出
L = range(5)
print([(lambda x:2*x)(i) for i in L])
print(list(map(lambda x:2*x, L)))
[0, 2, 4, 6, 8]
[0, 2, 4, 6, 8]
"""
zip 函数能够把多个可迭代对象打包成一个元组构成的可迭代对象,它返回了一个 zip 对象,通过 tuple, list
可以得到相应的打包结果:
"""
L1, L2, L3 = list('abc'), list('def'), list('hij')
list(zip(L1,L2,L3))
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
tuple(zip(L1,L2,L3))
(('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j'))
L = list('abcd')
for index, val in enumerate(L):
print(index, val)
0 a
1 b
2 c
3 d
for index, val in zip(range(len(L)), L):
print(index, val)
0 a
1 b
2 c
3 d
dict(zip(L1, L2))
{'a': 'd', 'b': 'e', 'c': 'f'}
zipped = list(zip(L1, L2, L3))
zipped
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
list(zip(*zipped))
[('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]
# 最一般的方法是通过 array 来构造
import numpy as np
np.array([1, 2, 3])
array([1, 2, 3])
np.linspace(1, 19, 10) # 起始、终止(包含)、样本个数
array([ 1., 3., 5., 7., 9., 11., 13., 15., 17., 19.])
np.array(1, 19, 10) # 起始、终止(不包含)、样本个数
np.eye(3,3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
np.full((2,3), 10) # 元组传入大小,10 表示填充数值
array([[10, 10, 10],
[10, 10, 10]])
np.full((2,3), [1, 2, 3]) # 通过传入列表填充每列的值
array([[1, 2, 3],
[1, 2, 3]])
# 最常用的随机生成函数为 rand, randn, randint, choice ,它们分别表示 0-1 均匀分布的随机数组、标准正态的随机数组、随机整数组和随机列表抽样.
np.random.rand(3, 3) # 注意这里传入的不是元组,每个维度大小分开输入
array([[0.30180431, 0.39218832, 0.37574252],
[0.41168547, 0.18685168, 0.80999859],
[0.72171395, 0.82255286, 0.27006587]])
np.zeros((2, 3)).T
array([[0., 0.],
[0., 0.],
[0., 0.]])
# 对于二维数组而言,r_ 和 c_ 分别表示上下合并和左右合并:
np.r_[np.zeros((2,3)),np.zeros((2,3))]
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
# Translates slice objects to concatenation along the second axis.
np.c_[np.zeros((2,3)),np.zeros((2,3))]
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
# reshape 能够帮助用户把原数组按照新的维度重新排列。在使用时有两种模式,分别为 C 模式和 F 模式,分别以逐行和逐列的顺序进行填充读取。
target = np.arange(8).reshape(2, 4)
target
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
target.reshape(4,2) # 默认按行进行读取和填充
# target.reshape(4,2) # order='C') , 按照行读取和填充 默认按行进行读取和填充
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
target.reshape((4,2), order='F') # 按照列读取和填充
array([[0, 2],
[4, 6],
[1, 3],
[5, 7]])
# 特别地,由于被调用数组的大小是确定的,reshape 允许有一个维度存在空缺,此时只需填充-1 即可:
target = np.ones((5, 1))
trans = target.reshape((-1))
trans
array([1., 1., 1., 1., 1.])
# 数组的切片模式支持使用 slice 类型的 start:end:step 切片,还可以直接传入列表指定某个维度的索引进行切片
target = np.arange(9).reshape(3,3)
target[:, [0,2]]
array([[0, 2],
[3, 5],
[6, 8]])
target[np.ix_([True, False, True], [True, False, True])]
array([[0, 2],
[6, 8]])
target[np.ix_([0,1], [True, False, True])]
array([[0, 2],
[3, 5]])
target[np.ix_([0,2], [True, False, True])]
array([[0, 2],
[6, 8]])
new = target.reshape(-1)
new[new%2==0]
array([0, 2, 4, 6, 8])
test = np.arange(4).reshape(-1,2)
print(test)
print(test.sum(0))
print(test.sum(1))
[[0 1]
[2 3]]
[2 4]
[1 5]
22
a = np.array([1,2,3])
b = np.array([1,3,5])
a.dot(b)
"""
矩阵范数的定义
一个在 的矩阵上的矩阵范数(matrix norm)是一个从 线性空间到实数域上的一个函数,
记为映射|| · ||,它对于任意的 矩阵A和B及所有实数a,满足以下四条性质:
||A||>=0;
||A||=0 iff A=O (零矩阵); (1和2可统称为正定性)
||aA||=|a| ||A||; (齐次性)
||A+B||<= ||A|| + ||B||. (三角不等式)
"""
martix_target = np.arange(4).reshape(-1,2)
martix_target
array([[0, 1],
[2, 3]])
np.linalg.norm(martix_target, 'fro')
3.7416573867739413
np.linalg.norm(martix_target, np.inf)
5.0
np.linalg.norm(martix_target, 2)
3.702459173643833
vector_target = np.arange(4)
vector_target
array([0, 1, 2, 3])
a = np.arange(4).reshape(-1,2)
b = np.arange(4).reshape(-1,2)
a@b
array([[ 2, 3],
[ 6, 11]])
1.3 练习
Ex1:利用列表推导式写矩阵乘法
一般的矩阵乘法根据公式,可以由三重循环写出,这里改写为列表推导式的形式来书写
# 如下为三重循环写出:
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() # 排除数值误差
M1 = np.random.rand(2,3)
M2 = np.random.rand(3,4)
res = np.empty((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
Ex2:更新矩阵
设矩阵 (A_{m×n}) ,现在对 (A) 中的每一个元素进行更新生成矩阵 (B) ,更新方法是 (B_{ij}=A_{ij}sum_{k=1}^nfrac{1}{A_{ik}}) ,例如下面的矩阵为 (A) ,则 (B_{2,2}=5 imes(frac{1}{4}+frac{1}{5}+frac{1}{6})=frac{37}{12}) ,请利用 Numpy
高效实现。
A=np.array([[1,2,3],[4,5,6],[7,8,9]])
# 做个cache
cacheA = [sum(1.0/x for x in A[j, :]) for j in range(A.shape[1]) ]
cacheA
[1.8333333333333333, 0.6166666666666667, 0.37896825396825395]
B = [
[ A[i][j] * cacheA[i] for j in range(A.shape[1])]
for i in range(A.shape[0])
]
B
[[1.8333333333333333, 3.6666666666666665, 5.5],
[2.466666666666667, 3.0833333333333335, 3.7],
[2.6527777777777777, 3.0317460317460316, 3.4107142857142856]]
Ex3:卡方统计量
设矩阵(A_{m imes n}),记(B_{ij} = frac{(sum_{i=1}^mA_{ij}) imes (sum_{j=1}^nA_{ij})}{sum_{i=1}^msum_{i=1}^nA_{ij}}),定义卡方值如下:
请利用Numpy
对给定的矩阵(A)计算(chi^2)
np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
# 计算步骤如下:
B = A.sum(0)*A.sum(1).reshape(-1, 1)/A.sum()
print(B)
res = ((A-B)**2/B).sum()
res
[[14.14211438 13.08145581 15.20277296 13.67071057 11.90294627]
[15.18197574 14.04332756 16.32062392 14.67590988 12.77816291]
[16.63778163 15.38994801 17.88561525 16.08318891 14.0034662 ]
[16.42980936 15.19757366 17.66204506 15.88214905 13.82842288]
[17.67764298 16.35181976 19.0034662 17.08838821 14.87868284]
[12.68630849 11.73483536 13.63778163 12.26343154 10.67764298]
[13.93414211 12.88908146 14.97920277 13.46967071 11.72790295]
[13.3102253 12.31195841 14.3084922 12.86655113 11.20277296]]
11.842696601945802
Ex4:改进矩阵计算的性能
设(Z)为(m×n)的矩阵,(B)和(U)分别是(m×p)和(p×n)的矩阵,(B_i)为(B)的第(i)行,(U_j)为(U)的第(j)列,下面定义(displaystyle R=sum_{i=1}^msum_{j=1}^n|B_i-U_j|_2^2Z_{ij}),其中(|mathbf{a}|_2^2)表示向量(a)的分量平方和(sum_i a_i^2)。
现有某人根据如下给定的样例数据计算(R)的值,请充分利用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)
100566
"""
改造后
"""
(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z).sum()
100566
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
函数)
# np.diff 意思如下:
# Calculate the n-th discrete difference along the given axis.
# np.r_ 按列进行合并 Translates slice objects to concatenation along the second axis.
func = lambda x:np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])).max()
func([1,2,5,6,7])
3
func([3,2,1,2,3,4,6])
4