【1】第20期 学习者手册(Pandas)
【2】第一章 预备知识
1. Python 基础
1.1 列表推导式与条件赋值
问题1:生成 [0, 2, 4, 6, 8]
样式的数字序列
- 写法1:定义函数+循环
def my_func(x):
return x*2
list = []
for i in range(5):
list.append(my_func(i))
print(list)
- 写法2:定义函数+列表推导式
list = [my_func(i) for i in range(5)]
print(list)
列表推导式
[* for i in *]
,第一个*
表示映射函数,其输入为后面i
指代的内容,第二个*
表示迭代的对象。
问题2:用多层嵌套列表推导式生成 ['a_c', 'a_d', 'b_c', 'b_d']
[a+'_'+b for a in ['a', 'b'] for b in ['c', 'd']]
问题3:用带有 if
选择的条件赋值,即value = a if condition else b
实现截断列表中超过5的元素,即超过5的用5代替,小于5的保留原来的值
list = [1, 2, 3, 4, 5, 6, 7]
[i if i<=5 else 5 for i in list]
1.2 匿名函数与map方法
问题4:利用匿名函数生成 [0, 2, 4, 6, 8]
样式的数字序列
[(lambda x:x*2)(i) for i in range(5)]
问题5:利用匿名函数和map函数对匿名函数映射,生成 [0, 2, 4, 6, 8]
样式的数字序列
a = map(lambda x:x*2, range(5))
# 在python2中map()函数返回的是一个列表
# 但是在python3中返回的是一个迭代器(iteration)
for i in a:
print(i) # 0 2 4 6 8
list(map(lambda x: 2*x, range(5)))
问题6:利用匿名函数和map函数对匿名函数映射,生成[‘0_a’, ‘1_b’, ‘2_c’, ‘3_d’, ‘4_e’]
# list函数生成列表list('abcd') >>> ['a', 'b', 'c', 'd']
list(map(lambda x,y: str(x)+'_'+y, range(5), list('abcde')))
map()
根据提供的函数对指定序列做映射,语法:map(function, iterable, ...)
注:在做这道题时,总是报错’list’ object is not callable,后来才发现原来是做上面题时我命名了list变量,以后要注意 避免和函数名、方法名和关键词重复。不是第一次犯这种错误了 = =
【3】变量和函数同时使用了list,导致抛出异常
1.3 zip对象与enumerate方法
问题7:用zip打包多个可迭代对象
L1 = list('abcde')
L2 = range(4)
for a, b in zip(L1, L2):
print(a, b)
# 元素个数与最短的列表一致
>>>
a 0
b 1
c 2
d 3
问题8:用 enumerate
打包一个可迭代对象并绑定迭代元素的遍历序号
L = list('abcd')
for index, value in enumerate(L):
print(index, value)
enumerate()
函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标。语法:enumerate(sequence, [start=0])
,sequence
表示一个序列、迭代器或其他支持迭代对象,[start=0]
表示下标起始位置。
问题9:用 zip
压缩 L1
和 L2
,并解压
zipped = list(zip(L1, L2))
zipped
>>> [('a', 0), ('b', 1), ('c', 2), ('d', 3)]
list(zip(*zipped))
>>> [('a', 'b', 'c', 'd'), (0, 1, 2, 3)]
问题10:用 zip
建立 L1
和 L2
的字典映射
dict(zip(L1, L2))
>>> {'a': 0, 'b': 1, 'c': 2, 'd': 3}
2. Numpy基础
2.1 np数组的构造
问题11-1:用 array
构造 [1, 2, 3]
np.array([1,2,3])
>>> array([1, 2, 3])
问题11-2:利用 np.linspace, np.arange
构造等差序列
np.linspace(1,5,11) # [起始, 终止], 样本个数
>>> array([1, 2, 3])
np.arange(1,5,2) # [起始, 终止), 步长
>>> array([1, 3])
问题11-3:构造偏移主对角线1个单位的伪单位矩阵
np.array([1,2,3])
>>> array([1, 2, 3])
问题11-4:利用 np.random
生成随机矩阵
# (1)生成服从0-1均匀分布的三个随机数
np.random.rand(3)
# (2)生成元素服从0-1均匀分布的3*3数组
np.random.rand(3,3)
# (3)生成服从a-b均匀分布的三个随机数
a, b = 5, 15
(b-a) * np.random.rand(3) + a
=======================
# (1)生成服从N(0,1)标准正态分布的三个随机数
np.random.randn(3)
# (2)生成服从方差σ^2, 均值μ一元正态分布的三个随机数
sigma, mu = 2.5, 3
mu + np.random.randn(3) * sigma
=======================
# (1)生成随机整数 [low, high)
low, high, size = 5, 15, (2,2)
np.random.randint(low, high, size)
>>> array([[11, 8],
[14, 10]])
=======================
# (1)以给定概率取样
my_list = ['a', 'b', 'c', 'd']
np.random.choice(my_list, 2, replace=False, p=[0.1, 0.7, 0.1, 0.1])
>>> array(['c', 'd'], dtype='<U1')
# (2)当不指定概率时则为均匀采样,默认有放回抽样
np.random.choice(my_list, (3,3))
>>> array([['c', 'c', 'c'],
['c', 'a', 'd'],
['c', 'a', 'c']], dtype='<U1')
=======================
# random.permutation(x) 随机排列一个数组
np.random.permutation(my_list)
>>> array(['d', 'b', 'c', 'a'], dtype='<U1')
arr = np.arange(9).reshape((3,3))
>>> array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
np.random.permutation(arr)
# 如果x是多维数组,则沿其第一个坐标轴的索引随机排列数组
>>> array([[3, 4, 5],
[6, 7, 8],
[0, 1, 2]])
2.2 np数组的变形与合并
问题12-1:构造2*3
零矩阵,并求转置矩阵
np.zeros((2,3)).T
>>> array([[0., 0.],
[0., 0.],
[0., 0.]])
问题12-2:构造两个2*3
零矩阵,并分别上下合并、左右合并
# 上下合并
np.r_[np.zeros((2,3)), np.ones((2,3))]
>>> array([[0., 0., 0.],
[0., 0., 0.],
[1., 1., 1.],
[1., 1., 1.]])
# 左右合并
np.c_[np.zeros((2,3)), np.ones((2,3))]
>>> array([[0., 0., 0., 1., 1., 1.],
[0., 0., 0., 1., 1., 1.]])
==============
当两个一维数组合并时,均将其看作列向量,然后再进行合并操作
np.array([1,1])
>>> array([1, 1])
np.zeros(2)
>>> array([0., 0.])
np.r_[np.array([1,1]), np.zeros(2)]
>>> array([1., 1., 0., 0.])
np.c_[np.array([1,1]), np.zeros(2)]
>>> array([[1., 0.],
[1., 0.]])
==============
当一维数组和二维数组合并时,将一维数组看作列向量,在长度匹配的情况下只能实现左右合并c_
np.c_[np.array([0,0]), np.ones((2,3))]
>>> array([[0., 1., 1., 1.],
[0., 1., 1., 1.]])
np.r_[np.array([0,0]), np.ones((2,3))]
>>> all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)
问题12-3:构造2*4
矩阵,(1)并分别使用两种模式填充 转化为4*2
矩阵;(2)将原矩阵转化为一维数组
target = np.arange(8).reshape(2,4)
target
>>> array([[0, 1, 2, 3],
[4, 5, 6, 7]])
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]])
==============
target.reshape(-1)
>>> array([0, 1, 2, 3, 4, 5, 6, 7])
2.3 np数组的切片与索引
问题13:构造array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
,并使用布尔索引取值
target = np.arange(9).reshape(3,3)
target
>>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
target[np.ix_([True, False, True], [True, False, True])]
>>>
array([[0, 2],
[6, 8]])
target[np.ix_([1,2], [True, False, True])]
>>>
array([[3, 5],
[6, 8]])
2.4 常用函数
问题14-1:将[-1,1,-1,0]
中大于0的元素保留,小于等于0的元素填充为5
a = np.array([-1, 1, -1, 0])
np.where(a>0, a, 5)
>>> array([5, 1, 5, 5])
问题14-2:取[-2,-5,0,1,3,-1]
中非零数的索引、最大值索引、最小值索引
a = np.array([-2,-5,0,1,3,-1])
np.nonzero(a)
>>> (array([0, 1, 3, 4, 5], dtype=int64),)
a.argmax()
>>> 4
a.argmin()
>>> 1
问题14-3:判断[0,1]
中是否有非零元素;判断[0,1]
中是否全为非零元素
a = np.array([0,1])
a.any()
>>> True
==============
a.all()
>>> False
问题14-4:求[1,2,3]
累乘、累加,以及和前一个元素差值
a = np.array([1,2,3])
a.cumprod()
>>> array([1, 2, 6], dtype=int32)
a.cumsum()
>>> array([1, 3, 6], dtype=int32)
np.diff(a)
>>> array([1, 1])
问题14-5:略过[1, 2, np.nan]
中缺失值,返回最大值和0.5分位数
target = np.array([1, 2, np.nan])
np.nanmax(target)
>>> 2.0
np.nanquantile(target, 0.5)
>>> 1.5
问题14-6:构造array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
并求行和、列和
target = np.arange(1, 10).reshape(3,-1)
target
>>> array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 对行求和
target.sum(0)
>>> array([12, 15, 18])
# 对列求和
target.sum(1)
>>> array([ 6, 15, 24])
2.5 广播机制
# 标量与数组的操作
np.ones((2,2))
>>> array([[1., 1.],
[1., 1.]])
3 * np.ones((2,2)) +1
>>> array([[4., 4.],
[4., 4.]])
==============
# 二维数组之间的操作
# 3*2 与 1*2 逐元素运算----->将1*2扩充至3*2
res = np.ones((3,2))
res * np.array([[2,3]]) # 扩充第一维度为3
>>> array([[2., 3.],
[2., 3.],
[2., 3.]])
# 3*2 与 3*1 逐元素运算----->将3*1扩充至3*2
res * np.array([[2],[3],[4]])
>>> array([[2., 2.],
[3., 3.],
[4., 4.]])
==============
# 一维数组与二维数组的操作
# 先将(3,)视为(1,3),要与(2,1)逐元素相加----->将(1,3)扩充为(2,3) 将(2,1)扩充为(2,3)
np.ones(3) + np.ones((2,1))
>>> array([[2., 2., 2.],
[2., 2., 2.]])
2.6 向量与矩阵的计算
问题15-1:计算[1,2,3]
和[1,3,5]
的内积
a = np.array([1,2,3])
b = np.array([1,3,5])
a.dot(b)
>>> 22
问题15-2:计算向量范数和矩阵范数
np.linalg.norm
对应指定 ord
参数即可
问题15-3:计算array([[0, 1], [2, 3]])
和array([[-4, -3], [-2, -1]])
矩阵乘法
a = np.arange(4).reshape(-1,2)
b = np.arange(-4,0).reshape(-1,2)
a@b
>>> array([[ -2, -1],
[-14, -9]])
3. 练习
3.1 Ex1
我的思路
- 分别取出
M1
的行、M2
的列,相乘,再相加 np.array()
转化一下reshape
成结果应有的维度
my_res = np.array([(a*b).sum() for a in M1 for b in M2.T]).reshape(M1.shape[0], M2.shape[1])
答案思路
- 将三重循环写进列表推导式,先循环
j
把结果的第一行打印出来,再循环i
sum()
套在k
索引那层循环外,表示映射函数
[[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])]
3.2 Ex2
我的思路
# 错误版
a = np.arange(1,10).reshape(3,3)
[[a[i][j] * sum(1/a[i]) for i in range(a.shape[0])] for j in range(a.shape[1])]
>>>
[[1.8333333333333333, 2.466666666666667, 2.6527777777777777],
[3.6666666666666665, 3.0833333333333335, 3.0317460317460316],
[5.5, 3.7, 3.4107142857142856]]
# 啊啊啊 居然错了 = =
# [a[i][j] * sum(1/a[i]) for i in range(a.shape[0])] 这部分不应该单独框起来
np.array([a[i][j] * sum(1/a[i]) for i in range(a.shape[0]) for j in range(a.shape[1])]).reshape(a.shape[0],a.shape[1])
# 这样改就可以了,可是也太麻烦了...
答案思路
- 取倒数,跨列求和,返回 (3,)
- 与原矩阵对应元素求乘积,乘积之前记得
reshape
为二维的
A = np.arange(1,10).reshape(3,-1)
B = A*(1/A).sum(1).reshape(-1,1)
# 大佬的代码就是简洁...易懂...
3.3 Ex3
我的思路
- 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_{i j}=\frac{\left(\sum_{i=1}^{m} A_{i j}\right) \times\left(\sum_{j=1}^{n} A_{i j}\right)}{\sum_{i=1}^{m} \sum_{j=1}^{n} A_{i j}} Bij=∑i=1m∑j=1nAij(∑i=1mAij)×(∑j=1nAij) 中的 ( ∑ i = 1 m A i j ) \left(\sum_{i=1}^{m} A_{i j}\right) (∑i=1mAij) 为跨行求和, ( ∑ j = 1 n A i j ) \left(\sum_{j=1}^{n} A_{i j}\right) (∑j=1nAij) 为跨列求和
- 要利用广播对元素相乘,所以对元素
reshape
- 利用列表推导式求每一项 ( A i j − B i j ) 2 B i j \frac{\left(A_{i j}-B_{i j}\right)^{2}}{B_{i j}} Bij(Aij−Bij)2
- 对列表中元素求和即为卡方
sum_r = A.sum(axis=0) # 跨行求和
sum_r = sum_r.reshape(1,-1)
sum_c = A.sum(axis=1) # 跨列求和
sum_c = sum_c.reshape(-1,1)
total = A.sum()
B = sum_r * sum_c / total
list_result = [np.square(A[i][j]-B[i][j])/B[i][j] for i in range(A.shape[0]) for j in range(A.shape[1])]
sum(list_result)
>>> 11.842696601945798
答案思路
# 基本思路是一样的,只是我仿佛智障 明明直接可以运算的,我偏用列表推导式挨个循环一下,简直有毒...
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()
3.4 Ex4
答案思路
答案是将公式拆开,利用广播机制计算
(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2* B@U)*Z).sum()
3.5 Ex5
答案思路
- 以
[1,2,5,6,7]
为例,菜鸡如我只能想到用diff()
计算相邻差值(橘色块),那如何统计差值为1
的最大连续的个数呢,难住我了… - 大佬提供的思路是:找到连续数据的前后两个边界的位置,(下界位置-上界位置)就是连续数据的长度了,最后取
max()
就能得到最大的连续长度。 - 那用什么表示边界呢,可以将差值(橘色块),转化为
0/1
表示(粉色+绿色 首末也要有元素填充),最后通过nonzero()
返回边界的索引值,索引值做差就可以表示序列长度了。
# np.r_[1,np.diff(x)!=1,1] 构造0/1边界
# np.nonzero(np.r_[1,np.diff(x)!=1,1]) 返回 1 元素的位置
# np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])) 位置做差
f = lambda x:np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])).max()