Pandas进阶壹 巩固基础

Pandas进阶壹 巩固基础

pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了《利用Python进行数据分析》

一、Python列表操作进阶

1. 列表推导式

可以使用for循环推导列表,此处for可以是多层嵌套表达,原理和多层循环之后向列表中插入一个值相同,因此列表推导式for前面的表达式可以使用正常表达式的所有操作

d = {
     'a':1, 'b':2}
[i+'_'+str(d[i])+'_'+j+'_'+str(k) for i in d.keys() for j in ['p', 'q'] for k in [4, 5]]
['a_1_p_4',
 'a_1_p_5',
 'a_1_q_4',
 'a_1_q_5',
 'b_2_p_4',
 'b_2_p_5',
 'b_2_q_4',
 'b_2_q_5']

为列表推导式加入条件语句

# 可以用条件表达式语法糖简洁地写出两个列表两两对比的较大值
[i if i>j else j for i in ['a', 't', 'm'] for j in ['b', 'e', 'z']]
['b', 'e', 'z', 't', 't', 'z', 'm', 'm', 'z']

注意如果只写if,没有else的话条件语句应该放在for之后
如果有else的话应该放在for之前
否则是错误写法

[i for i in range(7) if i<5]
[0, 1, 2, 3, 4]
[i if i<5 else 8 for i in range(7)]
[0, 1, 2, 3, 4, 8, 8]

列表推导式同样可以用在字典、集合上,如下:

# 由两个列表生成字典
{
     i:j for i in ['a', 'b', 'c'] for j in [1, 2, 3]}
{'a': 3, 'b': 3, 'c': 3}
{
     i for i in [1,1,1,2,3,4,4]}
{1, 2, 3, 4}

列表推导式本身也可以嵌套

from collections import Counter
words = ['collections', 'error', 'employee']
[[ch for ch in word] for word in words]
[['c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', 's'],
 ['e', 'r', 'r', 'o', 'r'],
 ['e', 'm', 'p', 'l', 'o', 'y', 'e', 'e']]

2. 匿名函数与map方法

[(lambda x: 2*x)(i) for i in range(5)]
[0, 2, 4, 6, 8]

通过lambda函数可以自定义排序方式

employees = [
    ['Alan Turing', 25, 10000],
    ['Alan Turing', 30, 1000],
    ['John Hopkins', 18, 1000],
    ['Mikhail Tal', 40, 15000],
]
employees.sort(key=lambda x:[x[2], x[0], x[1]])
employees
[['Alan Turing', 30, 1000],
 ['John Hopkins', 18, 1000],
 ['Alan Turing', 25, 10000],
 ['Mikhail Tal', 40, 15000]]

3. zip对象

这里总结一些学习之后我想到的zip的用法

  1. 矩阵转置
m = [[1,2,3],[4,5,6],[7,8,9]]
list(zip(*m))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
  1. 通过两个列表构造字典
dict(zip(['a', 'b', 'c'], [1, 2, 3]))
{'a': 1, 'b': 2, 'c': 3}

4. itertools模块

import itertools

names = ['alan', 'adam', 'wes', 'will']
nameid = [1, 3, 5]

利用groupby分组

for ch, words in itertools.groupby(names, lambda x: x[0]):
    print(ch, list(words))
a ['alan', 'adam']
w ['wes', 'will']

利用permutations求一个列表的所有排列

print(list(itertools.permutations(nameid, 3)))
print(list(itertools.permutations(nameid, 2)))
[(1, 3, 5), (1, 5, 3), (3, 1, 5), (3, 5, 1), (5, 1, 3), (5, 3, 1)]
[(1, 3), (1, 5), (3, 1), (3, 5), (5, 1), (5, 3)]

利用combinations求一个列表的所有组合

list(itertools.combinations(names, 2))
[('alan', 'adam'),
 ('alan', 'wes'),
 ('alan', 'will'),
 ('adam', 'wes'),
 ('adam', 'will'),
 ('wes', 'will')]

二、Numpy基础

远昊大佬写的很全面了,我在这里记录一些自己的理解和补充

先贴下原文中的函数帮助自己回顾

矩阵构造:

  1. np.array(list):构造numpy 数组
  2. np.linspace(起始, 终止, 样本个数)
  3. np.arange() 和range类似
  4. np.zeros(shape) 全0矩阵
  5. np.eye(n) n维单位矩阵
  6. np.full(shape, value) 构造值全为value

随机数:

  1. np.random.rand() 构造均匀分布的随机数组
  2. np.random.randn() 构造正态分布的随机数组
  3. np.random.choice() 在列表中随机选取,可以设置有无放回,也可以设置选取的概率

变形与合并:

  1. T 转置
  2. np.r_(array1, array2) 上下合并
  3. np.c_(array1, array2) 左右合并
  4. x.reshape(shape, order=‘C’/‘F’) 默认C代表按行重排,F代表按列重排
  5. x.reshape(-1) 将array展开

切片与索引:

  1. np.ix_() 指定索引的行列

常用函数:

  1. np.where 指定满足条件和不满足条件的填充值
  2. np.nonzero, np.argmin, np.argmax 返回非零值、最小最大值的索引
  3. np.cumprod, np.cumsum 累乘,累加
  4. 统计函数max, min, mean, median, std, var, sum, quantile
  5. cov协方差, corrcoef相关系数

线性代数:

  1. 向量内积 a.dot(b) a@b
  2. linalg包
import numpy as np

1. range 与 np.arange
arange和python range的区别是arange步长可以为小数,如:

try:
    range(1, 3.6, 0.2)
except Exception as e:
    print(e)
'float' object cannot be interpreted as an integer
np.arange(1,3.6,0.2)
array([1. , 1.2, 1.4, 1.6, 1.8, 2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4])

2.np.full
np.full的原理是用了broadcast,可以传一行进行广播,也可以传一列进行广播

np.full((2,3), [[1],[2]]) # 按列广播
array([[1, 1, 1],
       [2, 2, 2]])
np.full((2,3), [1,2,3]) # 按行广播
array([[1, 2, 3],
       [1, 2, 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() # 排除数值误差
True
#自己想的做法(是错的)
res = np.zeros((M1.shape[0],M2.shape[1]))
res = [[res[i][j]+M1[i][k]*M2[k][j]  for j in range(M2.shape[1]) for k in range(M1.shape[1])] for i in range(M1.shape[0])]
res
[[0.2957157379191776,
  0.241770264213699,
  0.9332774207010959,
  0.30630566767322404,
  0.15187789479179117,
  0.16530291729334373,
  0.2668142522248828,
  0.1131105799514749,
  0.5682066531753045,
  0.00980039554047657,
  0.3719339829279964,
  0.9637859016065992],
 [0.6218645045085477,
  0.2489711985400718,
  0.9544693752777721,
  0.6441342067084365,
  0.15640145665156052,
  0.16905645491995966,
  0.5610871911084889,
  0.11647948828475355,
  0.5811089363733815,
  0.020609380344957975,
  0.3830117397130221,
  0.9856706130497891]]
#看到远昊大佬打出了sum的时候恍然大悟
res = np.zeros((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_{m×n} Am×n ,现在对 A A A 中的每一个元素进行更新生成矩阵 B B B ,更新方法是 B i j = A i j ∑ k = 1 n 1 A i k B_{ij}=A_{ij}\sum_{k=1}^n\frac{1}{A_{ik}} Bij=Aijk=1nAik1 ,例如下面的矩阵为 A A A ,则 B 2 , 2 = 5 × ( 1 4 + 1 5 + 1 6 ) = 37 12 B_{2,2}=5\times(\frac{1}{4}+\frac{1}{5}+\frac{1}{6})=\frac{37}{12} B2,2=5×(41+51+61)=1237 ,请利用 Numpy 高效实现。
KaTeX parse error: No such environment: split at position 7: \begin{̲s̲p̲l̲i̲t̲}̲A=\left[ \begin…

37/12
1+1/2+1/3
1.8333333333333333

我的答案,发现与大佬不一致,而且我构造原数组的方式好low

a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a*(((1/a).T.sum(0)))
b
array([[ 1.83333333,  1.23333333,  1.13690476],
       [ 7.33333333,  3.08333333,  2.27380952],
       [12.83333333,  4.93333333,  3.41071429]])

我的思路是将1/原矩阵转置后按行求和,再和原矩阵相乘
错误的地方在于不管是按行求和还是按列求和,结果都是一个向量,因此要显式地将向量构造成列向量,再利用广播机制相乘

#验证我的错误:从如下代码看出,sum(1)虽然是按列求和,但结果并不是列向量
print(a.sum(0))
print(a.sum(1))
[12 15 18]
[ 6 15 24]
a*(1/a).sum(1).reshape(-1,1)
array([[1.83333333, 3.66666667, 5.5       ],
       [2.46666667, 3.08333333, 3.7       ],
       [2.65277778, 3.03174603, 3.41071429]])

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=1mj=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=1mj=1nBij(AijBij)2
请利用Numpy对给定的矩阵 A A A计算 χ 2 \chi^2 χ2

np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
A
array([[15, 10, 13, 13, 17],
       [19, 13, 15, 12, 14],
       [17, 16, 18, 18, 11],
       [16, 17, 17, 18, 11],
       [15, 19, 18, 19, 14],
       [13, 10, 13, 15, 10],
       [12, 13, 18, 11, 13],
       [13, 13, 17, 10, 11]])
#代码稍有不同,但结果是一样的,算作自己完全答对!
B = (A.sum(1).reshape(-1,1) * A.sum(0).reshape(1,-1))/B.sum()
kai = ((A-B)**2/B).sum()
kai
331776.02052460413
B = (A.sum(0) * A.sum(1).reshape(-1,1))/B.sum()
kai = ((A-B)**2/B).sum()
kai
B 
array([[0.02450973, 0.0226715 , 0.02634796, 0.02369274, 0.02062902],
       [0.02631192, 0.02433852, 0.02828531, 0.02543485, 0.02214586],
       [0.02883498, 0.02667235, 0.0309976 , 0.02787381, 0.02426944],
       [0.02847454, 0.02633895, 0.03061013, 0.02752539, 0.02396607],
       [0.03063716, 0.02833938, 0.03293495, 0.02961592, 0.02578628],
       [0.02198667, 0.02033767, 0.02363567, 0.02125378, 0.01850545],
       [0.02414929, 0.0223381 , 0.02596049, 0.02334432, 0.02032566],
       [0.02306798, 0.02133788, 0.02479808, 0.02229905, 0.01941555]])

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=1mj=1nBiUj22Zij,其中 ∥ a ∥ 2 2 \|\mathbf{a}\|_2^2 a22表示向量 a a a的分量平方和 ∑ i a i 2 \sum_i a_i^2 iai2

现有某人根据如下给定的样例数据计算 R R R的值,请充分利用Numpy中的函数,基于此问题改进这段代码的性能。

%time
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)

CPU times: user 4 µs, sys: 1 µs, total: 5 µs
Wall time: 7.15 µs





100566

我真是个小天才,琢磨了半天竟然真做对了!
我的思路是R的表达式中首先Z可以忽略,主要是求前面的部分,前面的部分可以用(a-b)平方 = a平方 + b平方 -2ab的原理拆解,然后实现这个表达即可
发现与答案一致cool
我解题的方法是首先在草稿纸上构造了一个B是(2,2) U是(2,1)的矩阵,想了想计算过程,然后测试一下前面的拆解是否正确,发现是正确的,然后就改造成高维的再实现一下,改造的过程主要会出一些维度不对的报错,随时用np.shape()查看就可纠错

%time
t = (((B**2).sum(1).reshape(-1,1) + (U**2).sum(0).reshape(1,-1)) - (B.dot(U)*2)) * Z
t.sum()
CPU times: user 2 µs, sys: 1 µs, total: 3 µs
Wall time: 5.96 µs





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函数)

#这个题没做出来,看了答案之后一步一步拆解看看过程是怎样的
a = np.array([3,2,1,2,3,4,6])
np.diff(np.nonzero(np.r_[1, np.diff(a)!=1, 1])).max()
4
#这一步想出来了,找连续上升用diff可以判断
np.diff(a)!=1
array([ True,  True, False, False, False,  True])
#这里发现布尔数组和数值数组合并的时候布尔数组会自动变成0和1,这里实际是在计算连续上升的值的区间,0表示在一个区间
np.r_[1, np.diff(a)!=1, 1]
array([1, 1, 1, 0, 0, 0, 1, 1])
#通过判断0把区间转化成了区间端点,然后求差值就可以知道区间的长度,再求最大值
np.nonzero(np.r_[1, np.diff(a)!=1, 1])
(array([0, 1, 2, 6, 7]),)

你可能感兴趣的:(pandas,datawhale,数据分析,numpy,python)