Data Whale第20期组队学习 Pandas学习—预备知识
一、Python基础
1.1 列表推导式和条件赋值
使用以下代码生成一个数字序列:
num=[]
def my_function(x):
return x**3
for i in range(5):
num.append(my_function(i))
print("num=",num)
# num= [0, 1, 8, 27, 64]
print("num=",[my_function(i) for i in range(5)])# 简化写法
# num= [0, 1, 8, 27, 64]
# 列表表达式还支持多层嵌套
print([i+'+'+j for i in['a','b'] for j in ['c','d']])#第一个 for 为外层循环,第二个for为内层循环
# ['a+c', 'a+d', 'b+c', 'b+d']
# 带有 if 选择的条件赋值
animal='cat' if 5>4 else 'dog'
print("animal=",animal)
# animal= cat
# 截断列表中超过10的元素,即超过10的用20代替,小于10的保留原来的值:
num1=[]
for j in range(5,15):
num1.append(j)
print([k if k<=10 else 20 for k in num1])
#[5, 6, 7, 8, 9, 10, 20, 20, 20, 20]
1.2 匿名函数与map方法
有一些函数的定义具有清晰简单的映射关系,可以使用匿名函数的方法简洁地表示。代码如下:
i_function= lambda y:2**y
print("i_function(5)=",i_function(5))
# i_function(5)= 32
multi_fuction =lambda x,y:x**y
print("multi_fuction(5,6)=",multi_fuction(5,6))
# multi_fuction(5,6)= 15625
Python 中提供了 map 函数来完成列表推导式的匿名函数映射,它返回的是一个 map 对象,需要通过 list 转为列表,代码如下:
# 单个输入值
print(list(map(lambda z:z**4,range(1,6,2))))
# [1, 81, 625]
#多个输入值的函数映射,可以通过追加迭代对象实现
print(list(map(lambda g,h:str(g)+'*'+h,range(1,6),list('qwert'))))
# ['1*q', '2*w', '3*e', '4*r', '5*t']
1.3 zip对象与enumerate方法
zip函数能够把多个可迭代对象打包成一个元组构成的可迭代对象,它返回了一个 zip 对象,通过 tuple, list 可以得到相应的打包结果。
list1,list2,list3=list('654'),list('cba'),list('321')
print(list(zip(list1,list2,list3)))
# [('6', 'c', '3'), ('5', 'b', '2'), ('4', 'a', '1')]
print(tuple(zip(list1,list2,list3)))
# (('6', 'c', '3'), ('5', 'b', '2'), ('4', 'a', '1'))
# 在循环迭代的时候使用zip 函数
for i,j,k in zip(list1,list2,list3):
print("i=",i,"j=",j,"k=",k)
# i= 6 j= c k= 3
# i= 5 j= b k= 2
# i= 4 j= a k= 1
# enumerate 是一种特殊的打包,它可以在迭代时绑定迭代元素的遍历序号
list4=list('world')
for index,value in enumerate(list4):
print("index=",index,"value=",value)
# index= 0 value= w
# index= 1 value= o
# index= 2 value= r
# index= 3 value= l
# index= 4 value= d
#或者使用zip函数实现
for index,value in zip(range(len(list4)),list4):
print("index=", index, "value=", value)
# index= 0 value= w
# index= 1 value= o
# index= 2 value= r
# index= 3 value= l
# index= 4 value= d
# 利用 zip 对象对两个列表建立字典映射
print(dict(zip(list1,list2)))
# '6': 'c', '5': 'b', '4': 'a'}
# Python 提供了 * 操作符和 zip 联合使用来进行解压操作
zipped = list(zip(list1,list2,list3,list4[:3]))
print("zipped=",zipped)
# zipped= [('6', 'c', '3', 'w'), ('5', 'b', '2', 'o'), ('4', 'a', '1', 'r')]
print(list(zip(*zipped)))
# [('6', '5', '4'), ('c', 'b', 'a'), ('3', '2', '1'), ('w', 'o', 'r')]
二、Numpy基础
2.1 使用numpy构造数组
numpy构造数组常见的方法是使用array函数构造
import numpy as np
print("数组=",np.array([i for i in range(1,15,3)]))
#数组= [ 1 4 7 10 13]
# 等差数列(linspace和arange)
print("等差数列=",np.linspace(1,10,20))# # 起始、终止(包含)、样本个数
# 等差数列= [ 1. 1.47368421 1.94736842 2.42105263 2.89473684 3.36842105
# 3.84210526 4.31578947 4.78947368 5.26315789 5.73684211 6.21052632
# 6.68421053 7.15789474 7.63157895 8.10526316 8.57894737 9.05263158
# 9.52631579 10. ]
print("数列=",np.arange(10,30,2))
# 数列= [10 12 14 16 18 20 22 24 26 28]
# 特殊矩阵
print("零矩阵=",np.zeros((3,4)))#零矩阵
# 零矩阵= [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]
print("单位矩阵=",np.eye(4)) #单位矩阵
# 单位矩阵= [[1. 0. 0. 0.]
# [0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]
print("偏移主对角线1个单位的伪单位矩阵=",np.eye(4,k=1))#偏移主对角线1个单位的伪单位矩阵
# 偏移主对角线1个单位的伪单位矩阵= [[0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]
# [0. 0. 0. 0.]]
print("矩阵=",np.full((3,2),1214))
# 矩阵= [[1214 1214]
# [1214 1214]
# [1214 1214]]
print("矩阵=",np.full((3,4),[1,2,1,4]))
# 矩阵= [[1 2 1 4]
# [1 2 1 4]
# [1 2 1 4]]
print("随机矩阵=",np.random.rand(4,4))
# 随机矩阵= [[0.03085034 0.7878708 0.39943908 0.61970093]
# [0.28603138 0.92718978 0.42037367 0.59112997]
# [0.56091549 0.90144402 0.67675756 0.14047721]
# [0.60607429 0.85724591 0.19962022 0.59205343]]
2.2 numpy 数组的变形和合并
1、矩阵的转置
print("矩阵转置=",np.zeros((2,4)).T)
# 矩阵转置= [[0. 0.]
# [0. 0.]
# [0. 0.]
# [0. 0.]]
2、矩阵合并(r_,c_)
相对于二维数组,r_表示上下合并,c_表示左右合并。
print("上下合并=",np.r_[np.zeros((3,4)),np.zeros((3,4))])
# 上下合并= [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]
print("左右合并=",np.c_[np.zeros((3,4)),np.zeros((3,4))])
# 左右合并= [[0. 0. 0. 0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0. 0. 0. 0.]]
3、维度变换(reshape)
reshape 可以将原数组按照新的维度重新排列,有两种使用模式,分别为 C 模式和 F 模式,分别以逐行和逐列的顺序进行填充读取。
print("上下合并=",np.r_[np.zeros((3,4)),np.zeros((3,4))])
# 上下合并= [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]
print("左右合并=",np.c_[np.zeros((3,4)),np.zeros((3,4))])
# 左右合并= [[0. 0. 0. 0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0. 0. 0. 0.]]
target=np.arange(1,11).reshape(2,5)
print("target=",target)
# target= [[ 1 2 3 4 5]
# [ 6 7 8 9 10]]
print("target=",target.reshape((5,2),order='C')) # # 行读取和填充
# target= [[ 1 2]
# [ 3 4]
# [ 5 6]
# [ 7 8]
# [ 9 10]]
print("target=",target.reshape((5,2),order='F')) # 列读取和填充
# target= [[ 1 8]
# [ 6 4]
# [ 2 9]
# [ 7 5]
# [ 3 10]]
# 由于被调用数组的大小是确定的, reshape 允许有一个维度存在空缺,此时只需填充-1即可
print("target=",target.reshape((5,-1)))
# target= [[ 1 2]
# [ 3 4]
# [ 5 6]
# [ 7 8]
# [ 9 10]]
print("数组=",np.ones((5,1)).reshape(-1))# 将 n*1 大小的数组转为1维数组
# 数组= [1. 1. 1. 1. 1.]
2.3 numpy数组的切片与索引
numpy数组的切片模式支持使用 slice 类型的 start🔚step 切片,或者直接传入列表指定某个维度的索引进行切片。
target1=np.arange(2,17).reshape(3,5)
print("target1=",target1)
# target1= [[ 2 3 4 5 6]
# [ 7 8 9 10 11]
# [12 13 14 15 16]]
print(target1[:-1,[0,3]])
# [[ 2 5]
# [ 7 10]]
还可以利用 np.ix_ 在对应的维度上使用布尔索引,但是不能使用 slice 切片。
print(target1[np.ix_([True,False,True],[False,True,True])])
# [[ 3 4]
# [13 14]]
print(target1[np.ix_([1,3],[True,False,True])])
# [[ 3 4]
# [13 14]]
2.4 numpy 常用函数
1、where
where是一种条件函数,可以指定满足条件与不满足条件位置对应的填充值。
x=np.array([-2,-1,0,1,2])
print(np.where(x>0,x,3))# 对应位置为True时填充a对应元素,否则填充3
# [3 3 3 1 2]
2、nonzero,argmax,argmin
nonzero,argmax,argmin函数返回的都是索引, nonzero 返回非零数的索引, argmax, argmin 分别返回最大和最小数的索引。
y=np.array([-6,-3,0,2,0,9,1])
print(np.nonzero(y))# 返回非零数的索引
# (array([0, 1, 3, 5, 6], dtype=int64),)
print(np.argmax(y))# 返回最大数的索引
# 5
print(np.argmin(y)) # 返回最小数的索引
# 0
3、any,all
any 指当序列至少 存在一个 True 或非零元素时返回 True ,否则返回 False all 指当序列元素 全为 True
或非零元素时返回 True ,否则返回 False
print(y.any())# 当序列至少 存在一个 True 或非零元素时返回 True ,否则返回 False
#True
print(y.all())# 当序列元素 全为 True 或非零元素时返回 True ,否则返回 False
#False
4、cumprod,cumsum,diff
cumprod, cumsum 分别表示累乘和累加函数,返回同长度的数组, diff
表示和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1
print(y.cumprod())# 累乘函数,返回同长度的数组
# [-6 18 0 0 0 0 0]
print(y.cumsum()) # 累加函数,返回同长度的数组
# [-6 -9 -9 -7 -7 2 3]
print(np.diff(y)) # 和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1
# [ 3 3 2 -2 9 -8]
5、numpy常用统计函数
常用的统计函数包括 max, min, mean, median, std, var, sum, quantile ,其中分位数计算是全局方法,因此不能通过 array.quantile 的方法调用。
target2=np.arange(12,27).reshape(3,5)
print(target2.max())# 最大值
# 26
print(target2.min())# 最小值
#12
print(target2.mean())#平均值
#19.0
print(np.median(target2))#中位数
#19.0
print(target2.std()) #标准差
#4.320493798938574
print(target2.var())#
#18.666666666666668
print(target2.sum()) #累加
#285
print(np.quantile(target2,0.5)) #分位数
#19.0
#需要注意含有缺失值的数组,它们返回的结果也是缺失值,如果需要略过缺失值,必须使用 nan* 类型的函数
target3=np.array([14,15,np.nan])
print("target3=",target3)# target3= [14. 15. nan]
print(target3.max()) #nan
print(np.nanmax(target3))# 15.0
print(np.quantile(target3,0.5)) # nan
2.5 向量与矩阵的计算
1、向量内积: dot
a ∗ b = ∑ i = 0 a i b i a*b=\sum_{i=0}^{}a_ib_i a∗b=i=0∑aibi
k=np.arange(12,17).reshape(1,5)
l=np.arange(12,17).reshape(5,1)
print("向量内积=",k.dot(l))
# 向量内积= [[990]]
2、向量范数和矩阵范数: np.linalg.norm
在矩阵范数的计算中,最重要的是 ord 参数,可选值如下:
ord | norm for matrices | norm for vectors |
---|---|---|
None | Frobenius norm | 2-norm |
‘fro’ | Frobenius norm | - |
‘nuc’ | nuclear norm | - |
inf | max(sum(abs(x), axis=1)) | max(abs(x)) |
-inf | max(sum(abs(x), axis=1)) | max(abs(x)) |
0 | - | sum(x != 0) |
1 | max(sum(abs(x), axis=0)) | as below |
-1 | max(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) |
参数 | 说明 | 计算方法 |
---|---|---|
默认 | 二范数: ℓ 2 \ell_2 ℓ2 | x 1 2 + x 2 2 + . . . + x n 2 \sqrt{x^2_1+x^2_2+...+x^2_n} x12+x22+...+xn2 |
ord=2 | 二范数: ℓ 2 \ell_2 ℓ2 | x 1 2 + x 2 2 + . . . + x n 2 \sqrt{x^2_1+x^2_2+...+x^2_n} x12+x22+...+xn2 |
ord=1 | 一范数: ℓ 1 \ell_1 ℓ1 | ∣ x 1 ∣ + ∣ x 2 ∣ + . . . + ∣ x n ∣ \vert{x_1}\vert+\vert{x_2}\vert+...+\vert{x_n}\vert ∣x1∣+∣x2∣+...+∣xn∣ |
ord=np.inf | 无穷范数: ℓ 2 \ell_2 ℓ2 | m a x ( ∣ x i ∣ ) max(\vert{x_i}\vert) max(∣xi∣) |
矩阵的范数:
1)ord=1:表示列和的最大值;ord=2:|λE-ATA|=0,求特征值,然后求最大特征值得算术平方根(matlab在线版,计算ans=ATA,[x,y]=eig(ans),sqrt(y),x是特征向量,y是特征值); ord=∞:表示行和的最大值; ord=None:默认情况下,是求整体的矩阵元素平方和,再开根号。(没仔细看,以为默认情况下就是矩阵的二范数,修正一下,默认情况下是求整个矩阵元素平方和再开根号)
2)axis:处理类型
axis=1表示按行向量处理,求多个行向量的范数
axis=0表示按列向量处理,求多个列向量的范数
axis=None表示矩阵范数。
3)keepding:是否保持矩阵的二维特性
True表示保持矩阵的二维特性,False相反
import numpy as np
x = np.array([
[1, 2, 9],
[3, 6, 7]])
print("默认参数(矩阵整体元素平方和开根号,不保留矩阵二维特性):",np.linalg.norm(x))
#默认参数(矩阵整体元素平方和开根号,不保留矩阵二维特性): 13.416407864998739
print("矩阵整体元素平方和开根号,保留矩阵二维特性:",np.linalg.norm(x,keepdims=True))
# 矩阵整体元素平方和开根号,保留矩阵二维特性: [[13.41640786]]
print("矩阵每个行向量求向量的2范数:",np.linalg.norm(x,axis=1,keepdims=True))
# 矩阵每个行向量求向量的2范数: [[9.2736185 ]
# [9.69535971]]
print("矩阵每个列向量求向量的2范数:",np.linalg.norm(x,axis=0,keepdims=True))
# 矩阵每个列向量求向量的2范数: [[ 3.16227766 6.32455532 11.40175425]]
print("矩阵1范数:",np.linalg.norm(x,ord=1,keepdims=True))
# 矩阵1范数: [[16.]]
print("矩阵2范数:",np.linalg.norm(x,ord=2,keepdims=True))
# 矩阵2范数: [[12.96543469]]
print("矩阵∞范数:",np.linalg.norm(x,ord=np.inf,keepdims=True))
# 矩阵∞范数: [[16.]]
print("矩阵每个行向量求向量的1范数:",np.linalg.norm(x,ord=1,axis=1,keepdims=True))
# 矩阵每个行向量求向量的1范数: [[12.]
# [16.]]
3、矩阵乘法(@)
[
A
m
∗
p
B
p
∗
n
]
=
∑
k
=
1
p
A
i
k
B
k
j
\left[{A}_m*_p{B}_p*_n\right]=\sum_{k=1}^p A_ikB_kj
[Am∗pBp∗n]=k=1∑pAikBkj
k=np.arange(2,17).reshape(3,5)
l=np.arange(22,37).reshape(5,3)
print("矩阵乘法=",k@l)
# 矩阵乘法= [[ 590 610 630]
# [1290 1335 1380]
# [1990 2060 2130]]
三、编程实践
例1、利用列表推导式写矩阵乘法
# 利用列表推导式写矩阵乘法
Mat1=np.random.rand(3,4)
Mat2=np.random.rand(4,3)
res=[[sum([Mat1[i][k]*Mat2[k][j] for k in range(Mat1.shape[1])]) for j in range(Mat2.shape[1]) ] for i in range(Mat1.shape[0])]
print(res) # [[0.6094751440030487, 0.7794174334232945, 1.0235658354677835], [0.6026038797348408, 0.7960478062763812, 1.0565907210550096], [0.7642209907651294, 0.8786058750405498, 1.1479216227691413]]
print(((Mat1@Mat2 -res)<1e-15).all())# True
例2、卡方统计量
#卡方统计量
np.random.seed(0)
M=np.random.randint(30,40,(8,5))
N=M.sum(0)*M.sum(1).reshape(-1,1)/M.sum()
res=((M-N)**2/N).sum()
print("res=",res)
# res= 4.857870907431414
参考文献
https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch1.html