我们都知道Python之所有能在数据科学领域占有一席之地,主要是数据分析三剑客:numpy、pandas、matplotlib这三个库的功劳。而在这三个库中,我觉得最核心,用的最多的还是pandas。不管是在平时处理数据还是打比赛中,都要求能够对pandas进行熟练的应用。基于此,笔者参加了Datawhale开源社区组织的Pandas学习,目标就是爆肝一个月,精通Pandas!
从本期开始,我们就开始来系统学习并梳理一下Pandas的知识。我们将按照下面的大纲分10期进行。我相信通过通过10期丰富的案例学习,掌握并熟练运用Pandas,水到渠成。
- Pandas基础
- 索引
- 分组
- 变形
- 连接
- 缺失数据
- 文本数据
- 分类数据
- 时序数据
- 综合练习
本期作为第一期,主要是熟悉一些基础知识,为后期的学习做准备。主要包括Python中的一些常用函数和numpy库的一些操作。
1.1 列表推导式
列表推导式是Python语言的一大特色,可以快速简洁的创建列表。
1.1.1 基本格式:
[* for i in k]: * 可以是一个函数,变量为i(也可以与i无关),k为一个可迭代对象,如列表。
应用: 1. 一句代码输出一个1到5的立方
- 一句代码创建一个列表,包含10个60-100的随机整数
# 一句代码输出一个1到5的立方
[i**3 for i in range(1,6)]
>>>[1, 8, 27, 64, 125]
# 一句代码创建一个列表,包含10个60-100的随机整数(模拟学生成绩)
import random
[random.randint(60,100) for _ in range(10)]
>>> [76, 89, 62, 83, 61, 80, 89, 99, 76, 78]
1.1.2 for循环嵌套
列表推导式中的for循环支持嵌套功能。
举例: 现有3个列表分别保存了顾客的姓名,衣服的颜色,尺码,用一句代码输出所有顾客和衣服颜色尺码的组合
names = ['zhangsan', 'lisi', 'wangba']
color = ['red', 'yellow']
size = ['S', 'M', 'L']
[name + '-' + c + '-' + s for name in names for c in color for s in size]
>>>
['zhangsan-red-S',
'zhangsan-red-M',
'zhangsan-red-L',
'zhangsan-yellow-S',
'zhangsan-yellow-M',
'zhangsan-yellow-L',
'lisi-red-S',
'lisi-red-M',
'lisi-red-L',
'lisi-yellow-S',
'lisi-yellow-M',
'lisi-yellow-L',
'wangba-red-S',
'wangba-red-M',
'wangba-red-L',
'wangba-yellow-S',
'wangba-yellow-M',
'wangba-yellow-L']
上面的代码等价于:
for name in names:
for c in color:
for s in size:
print(name + '-' + c + '-' + 's')
>>>
zhangsan-red-s
zhangsan-red-s
zhangsan-red-s
zhangsan-yellow-s
zhangsan-yellow-s
zhangsan-yellow-s
lisi-red-s
lisi-red-s
lisi-red-s
lisi-yellow-s
lisi-yellow-s
lisi-yellow-s
wangba-red-s
wangba-red-s
wangba-red-s
wangba-yellow-s
wangba-yellow-s
wangba-yellow-s
1.1.3 筛选功能
列表推导式中for循环后还可以加入if (或者if...else...)进行筛选 。
举例: 一句代码输出0-100内可以被7整除的整数
# 输出1-100内可以被7整除的数:
[i for i in range(1,101) if i%7 == 0]
>>>
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98]
综合上面的案例,我们可以清晰的看到列表推导式的简洁与优雅!同时也体现Python的强大之处。
1.2 lambda匿名函数
我们都知道函数在python世界中属于一等公民,具有很高的权限。对于经常需要重复使用的代码块,一般都要优先考虑通过函数来实现。但是当我们想要使用一个简单定义的,或者只需要调用一两次的函数时,取名并编写一个完整的函数块就显得多余。这时候lambda匿名函数就有了用武之地。
格式: lambda [arg1 [,arg2, ... argN]] : expression
这里的lamdbda是系统保留的关键字, [arg1 [,arg2, ... argN]]是参数列表,它的结构与Python中函数(function)的参数列表是一样的。expression是一个关于参数的表达式。表达式中出现的参数需要在argument_list中有定义,并且表达式只能是单行的。
举例:例如我们定义一个函数,将字符串中的所有字母大写输出
def str_capital(s):
return str.upper(s)
str_capital('datawhale')
>>>
'DATAWHALE'
如果改用匿名函数的写法:
upper = lambda x: str.upper(x)
upper('datawhale')
>>>
'DATAWHALE'
对比一下可以看到匿名函数有如下优点:
- 可以直接在使用的地方定义,如果需要修改,直接找到修改即可,方便以后代码的维护工作
- 语法结构简单,不用使用def 函数名(参数名):这种方式定义,直接使用lambda 参数:返回值 定义即可
但是需要注意的是lambda匿名函数让程序简洁,但是并不能让程序高效,这个也是很多程序员反对使用lambda的原因。
1.3 map()方法
在Python中,匿名函数lambda经常和map()、reduce()和filter()三个应用于序列的内置函数联合使用,用于对序列进行遍历、递归计算以及筛选。这其中,最常用的就是map方法。在Python中,map()函数的本质是一种映射,即对输入其中的可迭代对象(列表)中每个元素执行定义的映射。例如我们编写了一个将给定的字符串大写输出的函数,在使用该函数将若干字符串大写输出
def str_capital(s):
return str.upper(s)
L1 = ['I', 'like', 'Datawhale']
L2 = []
for s in L1:
L2.append(str_capital(s))
L2
>>>
['I', 'LIKE', 'DATAWHALE']
如果我们用map()替代for循环:
L3 = map(str_capital, L1)
list(L3)
>>>
['I', 'LIKE', 'DATAWHALE']
可以看到更加简洁!需要注意的是map()方法返回的是一个map()对象,需要用list()方法输出其中的元素。上面我们说了map经常和lambda匿名函数结合使用,如下:
L4 = map(lambda x: str.upper(x), L1)
list(L4)
>>>
['I', 'LIKE', 'DATAWHALE']
优雅!
1.4 zip方法
我们都知道zip是一个文件解压程序,同样的,在python中zip()函数就有点类似于解压缩包的感觉:传入一个列表或者其他可迭代对象,依次从中选取一个组成新的元组输出。下面举例:
a = [3,4,5,6]
b = ['a', 'b', 'c']
s1 = {'zhangsan': 20, 'lisi': 25}
print(zip(a))
print('*' * 10)
print(list(zip(a)))
print(list(zip(b)))
print(list(zip(s1)))
>>>
**********
[(3,), (4,), (5,), (6,)]
[('a',), ('b',), ('c',)]
[('zhangsan',), ('lisi',)]
可以看到zip的输出也是一个zip对象,需要用list查看其中的元素。
当zip()函数有两个参数时 ,如zip(a,b),则分别从a和b依次各取出一个元素组成元组,再将依次组成的元组组合成一个新的迭代器。如:
print(list(zip(a,b)))
>>>
[(3, 'a'), (4, 'b'), (5, 'c')]
这样设计有个特殊的用途,用于矩阵(二维数组)的加减和点乘,举例如下:
import numpy as np
m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
n = [[2, 2, 2], [3, 3, 3], [4, 4, 4]]
# 矩阵点乘
print('=*'*10 + "矩阵点乘" + '=*'*10)
print(np.array([x*y for a, b in zip(m, n) for x, y in zip(a, b)]).reshape(3,3))
# 矩阵相加,相减雷同
print('=*'*10 + "矩阵相加,相减" + '=*'*10)
print(np.array([x+y for a, b in zip(m, n) for x, y in zip(a, b)]).reshape(3,3))
>>>
=*=*=*=*=*=*=*=*=*=*矩阵点乘=*=*=*=*=*=*=*=*=*=*
[[ 2 4 6]
[12 15 18]
[28 32 36]]
=*=*=*=*=*=*=*=*=*=*矩阵相加,相减=*=*=*=*=*=*=*=*=*=*
[[ 3 4 5]
[ 7 8 9]
[11 12 13]]
知识链接:矩阵点乘
矩阵点乘: 对应元素相乘,要求两个矩阵的形状必须相同。这个要和矩阵叉乘区分开来。
2. Numpy复习回顾
pandas是基于numpy来实现高效计算的,因而在学习pandas之前有必要先把之前学习的numpy的知识温习一下,这里总结了一些numpy一些常用的知识点
2.1 np.array
np里面最基本的数据结构是array(数组),构造也非常简单,np.array即可。下面总结几种特殊的array
- 等差序列
- np.linspace(起始,终止(包含),样本个数): 适用于提前知道需要创建多少个样本的情况
- np.arange(起始,终止(不包含),步长): 适用于提前知道相邻间隔的情况
注意 np.arange和python数组中的range不要混淆了。range只能生成整数数列,而np.arange可以生成小数数列
import numpy as np
a = np.linspace(1,100,10)
b = np.arange(1,10,1.5)
print(a)
print(b)
>>>
[ 1. 12. 23. 34. 45. 56. 67. 78. 89. 100.]
[1. 2.5 4. 5.5 7. 8.5]
-
- 特殊矩阵,包括zeros/ones/eye/full等
直接上代码参考:
- 特殊矩阵,包括zeros/ones/eye/full等
print('3行4列全0矩阵')
print(np.zeros((3,4)))
print('*' * 10)
print('3行3列全1矩阵')
print(np.ones((3, 3)))
print('*' * 10)
print('3行3列的单位矩阵')
print(np.eye(3))
print('*' * 10)
print('指定维度的/数值填充矩阵')
print(np.full((2,3), 6))
>>>
3行4列全0矩阵
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
**********
3行3列全1矩阵
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
**********
3行3列的单位矩阵
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
**********
指定维度的/数值填充矩阵
[[6 6 6]
[6 6 6]]
-
- 随机矩阵
- np.random.rand() : 取值0-1之间的随机分布,这里不要传元组,直接指定不同维度的个数即可
- np.random.randn(): 0~1标准正态分布
- np.random.randint(low,high,size) :指定生成随机整数的最小值最大值和维度大小
- np.random.choice(): 可以从给定的列表中,以一定概率和方式抽取结果,当不指定概率时为均匀采样,默认抽取方式为有放回抽样
- np.random.seed(0) : 设置种子,就相当是设定了随机值,之后每次随机都一样
2. 练习题:
-
使用列表推导式完成矩阵乘法:
矩阵乘法定义:
一般的矩阵乘法根据公式,可以由三重循环写出:
使用列表推导式来替代for循环完成
# 先定义零个矩阵
M1 = np.random.randint(1,10,10).reshape(2,5)
M2 = np.random.randint(1,10,10).reshape(5,2)
print(M1)
print('-' * 5)
print(M2)
M1@M2 # 矩阵乘法
>>>
[[6 1 2 8 5]
[6 1 7 9 4]]
-----
[[6 2]
[7 7]
[1 4]
[7 1]
[8 3]]
array([[141, 50],
[145, 68]])
# 使用列表推导式来完成
[[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])]
>>>
[[141, 50], [145, 68]]
-
更新矩阵
设矩阵 Am×n ,现在对 A 中的每一个元素进行更新生成矩阵 B ,更新方法是
例如下面的矩阵为 A ,则 =5×(1/4+1/5+1/6)=37/12 ,请利用 Numpy 高效实现。
解答:
A = np.arange(1,10).reshape(3,3)
B = A*(1/A).sum(1).reshape(-1,1)
使用内置的函数
B = A.sum(0) * A.sum(1).reshape(-1,1) / A.sum()
print(B)
res = ((A-B) ** 2 / B).sum()
print(res)
参考:开源内容Joyful Pandas, 作者 DataWhale耿远昊
另外,更多精彩内容也可以微信搜索,并关注公众号:‘Python数据科学家之路“ ,期待您的到来和我交流