01-预备知识
1.1 python基础
1.1.1 列表推导式与条件赋值
列表推导式结构:[
* for i in *
]
其中第一个*为映射函数,其输入为后面i指代的内容,第二个*表示要迭代的对象(集合)
L=[]
def my_func(x):
return 2*x
for i in range(5):
L.append(my_func(i))
L
# [0,2,4,6,8]
[my_func(i) for i in range(5)]
#[0,2,4,6,8]
# 条件赋值的语法糖形式
value = 'cat' if 2>1 else 'dog'
value # 'cat'
1.1.2 匿名函数与map方法
匿名函数:映射关系清晰,用户不关心函数的名字,只关心这种映射关系。给定输入的参数和函数内的表达式即可
# 输出[0,2,4,6,8]
my_func=lambda x: 2*x
[(lambda x: 2*x)(i) for i in range(5)] #注意(i)
格式:map(func,seq1[,seq2,…])
参数1:函数名,参数2:可迭代的序列
返回:包含每次function函数返回值的新列表。
将func作用于seq中的每一个元素,如果func为None,作用同zip()
- 使用map()函数,简化上式列表推导式的匿名函数映射
# 输出[0,2,4,6,8]
list(map(lambda x: 2*x , range(5)))
# 使用map进行多输入值的函数映射
list(map(lambda x, y: str(x)+'_'+y, range(5), list('abcde')))
1.1.3 zip对象与enumerate方法
zip() 作用:将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
返回值:zip对象
通过tuple,list可以得到相应的打包结果。
- 如果各个迭代器的元素个数不一致,则返回的列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表
- zip函数相当于对每一个参数(集合)各取一个元素,组成一个新的元组,而map函数相当于将集合中的元素按照fun规则进行运算
# 元素个数不同,打包前后
L1, L2, L3 = list('abcg'), list('def'), list('hijk')
zipped = list(zip(L1, L2, L3))
# [('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
list(zip(*zipped ))
# [('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]
# enumerate是一种特殊的打包,它可以在迭代时绑定迭代元素的遍历序号:
L = list('abcd')
for index, value in enumerate(L):
print(index, value)
0 a
1 b
2 c
3 d
# 用zip实现相同功能
for index, value in zip(range(len(L)), L):
print(index, value)
当需要对两个列表建立字典映射时,可以利用
zip
对象
dict(zip(L1, L2))
# {'a': 'd', 'b': 'e', 'c': 'f'}
1.2 Numpy基础
1.2.1 np数组
-
构造np数组,通过array来构造
import numpy as np np.array([1,2,3]) # array([1,2,3])
-
特殊数组的生成方式
等差序列 np.linspace,np.arange
np.linspace(1,5,11) # 起始、终止(包含)、样本个数。在1到5之间生成11个数 np.arange(1,5,2) # 起始,终止(不包含)、步长 np.arange(8) # 默认从零开始,终止(不包含),步长为1
特殊矩阵 zeros,eye,full,ones
np.zeros((2,3)) # 传入的是元组 # 生成两行三列的矩阵,元素均为0 np.eye(3) # 3*3的单位矩阵 np.full((2,3),10) # 生成两行三列的矩阵,元素均为10 np.full((2,3), [1,2,3]) # 通过传入列表填充每列的值 # array([[1, 2, 3], # [1, 2, 3]]) np.ones((2,2)) #array([[1., 1.], # [1., 1.]])
-
随机矩阵 np.random。常用的随机生成函数为 :
rand
:0-1均匀分布的随机数组randn
:标准正态的随机数组randint
:随机整数组choice
:随机列表抽样
np.random.rand(3)
# array([0.53278509, 0.00283235, 0.28780472]),服从0-1分布
np.random.rand(3, 3) # 注意这里传入的不是元组,每个维度大小分开输入
#array([[0.23718163, 0.60975732, 0.59081295],
# [0.66953989, 0.72213661, 0.71973408],
# [0.06509781, 0.95659778, 0.24687672]])
#randn类似
# randint可以指定生成随机整数的最小值最大值和维度的大小
low, high, size = 5, 15, (2,2)
np.random.randint(low, high, size)
#array([[14, 10],
# [14, 5]])
- choice
- 可以从给定的列表中,以一定概率和方式抽取结果,当不指定概率时为均匀采样,默认抽取方式为有放回抽样
- 当返回的元素个数与原列表相同时,等价于使用
permutation
函数,即打散原列表 - 默认是有放回抽样,所以抽样个数可以大于四个,当replace为False时,抽样个数需小于列表个数
my_list = ['a', 'b', 'c', 'd']
np.random.choice(my_list, 3, replace=False, p=[0.1, 0.7, 0.1 ,0.1])
# array(['b', 'a', 'c'], dtype='<U1')
# 传入元组,用my_list中的元素生成3*3的矩阵
np.random.choice(my_list, (3,3))
# 返回的元素个数与原列表相同
np.random.permutation(my_list)
随机种子:能够固定随机数的输出结果
np.random.seed(1)
np.random.rand()
# 0.417
np.random.seed(1)
np.random.rand()
# 0.417
1.2.2 np数组的运算
- 转置:T
- 合并操作:
- 对于二维数组而言,r_和c_分别表示上下合并和左右合并
np.r_[np.zeros((2,3)),np.zeros((2,3))]
- 维度变化:reshape
- 将原数组按照新的维度重新排列。
- 在使用时有两种模式,分别为C模式和F模式,分别以逐行和逐列的顺序进行填充读取。
- 由于被调用数组的大小是确定的,reshape允许有一个维度存在空缺,此时只需填充-1即可
target = np.arange(8).reshape(2,4) # 按照行读取和填充 target.reshape((4,2), order='C') # 按照列读取和填充,按列填充,按列读取,按列填充 target.reshape((4,2), order='F')
- 把任意维度的数组转换为一维数组:reshape(-1)
np.ones()
:用1填充数组
1.2.3 np数组的切片与索引
数组的切片模式支持使用
slice
类型的start:end:step
切片,还可以直接传入列表指定某个维度的索引进行切片:
np.ix_在对应的维度上使用布尔索引,但此时不能使用slice切片
1.2.4 常用函数
- where
where是一种条件函数,可以指定满足条件与不满足条件位置对应的填充值: - nonzero, argmax, argmin
这三个函数返回的都是索引,nonzero返回非零数的索引,argmax, argmin分别返回最大和最小数的索引 - any, all
any指当序列至少存在一个 True或非零元素时返回True,否则返回False
all指当序列元素 全为 True或非零元素时返回True,否则返回False - cumprod, cumsum, diff
cumprod, cumsum分别表示累乘和累加函数,返回同长度的数组,
diff表示和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1 - 统计函数
常用的统计函数包括max, min, mean, median, std, var, sum, quantile,其中分位数计算是全局方法,因此不能通过array.quantile的方法调用
- 对于含有缺失值的数组,它们返回的结果也是缺失值,如果需要略过缺失值,必须使用nan*类型的函数
- 二维Numpy数组中统计函数axis参数,能够进行某一维度下的统计特征计算,当axis=0时为列的统计指标,为1时为行的统计指标
1.2.5 广播机制
处理两个不同维度数组之间的操作
- 标量和数组的操作
- 当一个标量和数组进行运算时,标量会自动把大小扩充为数组大小,之后进行逐元素操作
- 二维数组之间的操作,对应元素相乘
- 当两个数组维度完全一致时,使用对应元素的操作
- 当其中的某个数组的维度是 𝑚×1 或者 1×𝑛 ,那么会扩充其具有 1 的维度为另一个数组对应维度的大小。例如, 1×2 数组和 3×2 数组做逐元素运算时会把第一个数组扩充为 3×2 ,扩充时的对应数值进行赋值。
- 但是,需要注意的是,如果第一个数组的维度是 1×3 ,第二个数组为3x2,那么由于在第二维上的大小不匹配且不为 1 ,此时报错。
- 一维数组与二维数组的操作
- 当一维数组 A k A_k Ak与二维数组 B m , n B_{m,n} Bm,n 操作时,等价于把一维数组视作 A 1 , k A_{1,k} A1,k 的二维数组,使用的广播法则与【b】中一致,即当 𝑘!=𝑛 且 𝑘,𝑛 都不是 1 时报错。
- 即不存在行/列向量,且第二维上大小不匹配时报错
1.2.6 向量与矩阵的计算
- 向量内积:
dot
a ⋅ b = ∑ i a i b i a\cdot b= \sum_ia_ib_i a⋅b=i∑aibi
a = np.array([1,2,3])
b = np.array([1,3,5])
a.dot(b)
# 22
- 向量范数和矩阵范数:
np.linalg.norm
- 矩阵乘法
@
[ A m × p B p × n ] i j = ∑ k = 1 p A i k B k j \rm [\mathbf{A}_{m\times p}\mathbf{B}_{p\times n}]_{ij} = \sum_{k=1}^p\mathbf{A}_{ik}\mathbf{B}_{kj} [Am×pBp×n]ij=k=1∑pAikBkj
1.3 练习
M1 = np.random.rand(2,3)
M2 = np.random.rand(3,4)
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])]
- 更新矩阵
A = np.arange(1,10).reshape(3,-1)
B = A*(1/A).sum(1).reshape(-1,1)
- 卡方统计量
np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
B = A.sum(0)*A.sum(1).reshape(-1, 1)/A.sum()
res = ((A-B)**2/B).sum()
- 改进矩阵的性能
(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z).sum()
Out[23]: 100566
- 连续整数的最大长度
f = lambda x:np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])).max()