在数据分析和数据建模的过程中需要对数据进行清洗和整理等工作,有时需要对数据增删字段。本章将介绍Pandas对数据的复杂查询、数据类型转换、数据排序、数据的修改、数据迭代以及函数的使用。
目录
5.1 复杂查询
5.1.1 逻辑运算
5.1.2 逻辑筛选数据
5.1.3 函数筛选
5.1.4 比较函数
5.1.5 查询df.query()
5.1.6 筛选df.filter()
5.1.7 按数据类型查询
5.2 数据类型转换
5.2.1 推断类型
5.2.2 指定类型
5.2.3 类型转换astype()
5.2.4 转为时间类型
5.3 数据排序
5.3.1 索引排序
5.3.2 数值排序
5.3.3 混合排序
5.3.4 按值大小排序
类似于Python的逻辑运算,我们以DataFrame其中一列进行逻辑计算,会产生一个对应的由布尔值组成的Series,真假值由此位上的数据是否满足逻辑表达式决定。例如下例中索引为0的数据值为89,大于36,所以最后值为True。
# Q1成绩大于36
df.Q1 > 36
'''
0 True
1 False
2 True
3 True
4 True
...
95 True
96 False
97 True
98 False
99 False
Name: Q1, Length: 100, dtype: bool
'''
一个针对索引的逻辑表达式会产生一个array类型数组,该数组由布尔值组成。根据逻辑表达式,只有索引为1的值为True,其余全为False。
# 索引等于1
df.index == 1
'''
array([False, True, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False])
'''
再看一下关于DataFrame的逻辑运算,判断数值部分的所有值是否大于60,满足表达式的值显示为True,不满足表达式的值显示为False。
# df.loc[:,'Q1':'Q4']部分只取数字部分,否则会因字符无大于运算而报错
df.loc[:,'Q1':'Q4'] > 60
'''
Q1 Q2 Q3 Q4
0 True False False True
1 False False False False
2 False False False True
3 True True True True
4 True False True True
.. ... ... ... ...
95 False False True True
96 False False False False
97 True True False False
98 False True False True
99 False False False True
[100 rows x 4 columns]
'''
除了逻辑运算,Pandas还支持组合条件的Python位运算:
# Q1成绩不小于60分,并且是C组成员
~(df.Q1 < 60) & (df['team'] == 'C')
'''
0 False
1 False
2 False
3 True
4 False
...
95 False
96 False
97 True
98 False
99 False
Length: 100, dtype: bool
'''
切片([])、.loc[]和.iloc[]均支持上文所介绍的逻辑表达式。通过逻辑表达式进行复杂条件的数据筛选时需要注意,表达式输出的结果必须是一个布尔序列或者符合其格式要求的数据形式。例如,df.iloc[1+1]和df.iloc[lambda df: len(df)-1]计算出一个数值,符合索引的格式,df.iloc[df.index==8]返回的是一个布尔序列,df.iloc[df.index]返回的是一个索引,它们都是有效的表达式。
以下是切片([])的一些逻辑筛选的示例:
df[df['Q1'] == 8] # Q1等于8
df[~(df['Q1'] == 8)] # 不等于8
df[df.name == 'Ben'] # 姓名为Ben
df[df.Q1 > df.Q2]
以下是.loc[]和.lic[]的一些示例:
# 表达式与切片一致
df.loc[df['Q1'] > 90, 'Q1':] # Q1大于90,只显示Q1
df.loc[(df.Q1 > 80) & (df.Q2 < 15)] # and关系
df.loc[(df.Q1 > 90) | (df.Q2 < 90)] # or关系
df.loc[df['Q1'] == 8] # 等于8
df.loc[df.Q1 == 8] # 等于8
df.loc[df['Q1'] > 90, 'Q1':] # Q1大于90,显示Q1及其后所有列
需要注意的是在进行或(|)、与(&)、非(~)运算时,各个独立逻辑表达式需要用括号括起来。
any和all对逻辑计算后的布尔序列再进行判断,序列中所有值都为True时all才返回True,序列中只要有一个值为True时any就返回True。它们还可以传入axis参数的值,用于指定判断的方向,与Pandas的axis参数整体约定一样,默认为0列方向,传入1为行方向。利用这两个方法,我们可以对整体数据进行逻辑判断,例如:
# Q1、Q2成绩全为超过80分的
df[(df.loc[:,['Q1','Q2']] > 80).all(1)]
# Q1、Q2成绩至少有一个超过80分的
df[(df.loc[:,['Q1','Q2']] > 80).any(1)]
上例对两个列整体先做逻辑计算得到一个两列的布尔序列,然后用all和any在行方向上做逻辑计算。
可以在表达式处使用lambda函数,默认变量是其操作的对象。如果操作的对象是一个DataFrame,那么变量就是这个DataFrame;如果是一个Series,那么就是这个Series。可以看以下例子,s就是指df.Q1这个Series:
# 查询最大索引的值
df.Q1[lambda s: max(s.index)] # 值为21
# 计算最大值
max(df.Q1.index) # 99
df.Q1[df.index==99]
'''
99 21
Name: Q1, dtype: int64
'''
下面是一些示例:
df[lambda df: df['Q1'] == 8] # Q1为8的
df.loc[lambda df: df.Q1 == 8, 'Q1':'Q2'] # Q1为8的,显示 Q1、Q2
df.loc[:, lambda df: df.columns.str.len()==4] # 由真假值组成的序列
df.loc[:, lambda df: [i for i in df.columns if 'Q' in i]] # 列名列表
df.iloc[:3, lambda df: df.columns.str.len()==2] # 由真假值组成的序列
Pandas提供了一些比较函数,使我们可以将逻辑表达式替换为函数形式。
# 以下相当于 df[df.Q1 == 60]
df[df.Q1.eq(60)]
'''
name team Q1 Q2 Q3 Q4
20 Lucas A 60 41 77 62
'''
除了.eq(),还有:
df.ne() # 不等于 !=
df.le() # 小于等于 <=
df.lt() # 小于 <
df.ge() # 大于等于 >=
df.gt() # 大于 >
使用示例如下:
df[df.Q1.ne(89)] # Q1不等于89
df.loc[df.Q1.gt(90) & df.Q2.lt(90)] # and关系,Q1>90,Q2<90
这些函数可以传入一个定值、数列、布尔序列、Series或DataFrame,来与原数据比较。
另外还有一个. isin()函数,用于判断数据是否包含指定内容。可以传入一个列表,原数据只需要满足其中一个存在即可;也可以传入一个字典,键为列名,值为需要匹配的值,以实现按列个性化匹配存在值。
# isin
df[df.team.isin(['A','B'])] # 包含A、B两组的
df[df.isin({'team': ['C', 'D'], 'Q1':[36,93]})] # 复杂查询,其他值为NaN
df.query(expr)使用布尔表达式查询DataFrame的列,表达式是一个字符串,类似于SQL中的where从句,不过它相当灵活。
df.query('Q1 > Q2 > 90') # 直接写类型SQL where语句
df.query('Q1 + Q2 > 180')
df.query('Q1 == Q2')
df.query('(Q1<50) & (Q2>40) and (Q3>90)')
df.query('Q1 > Q2 > Q3 > Q4')
df.query('team != "C"')
df.query('team not in ("E","A","B")')
# 对于名称中带有空格的列,可以使用反引号引起来
df.query('B == `team name`')
还支持使用@符引入变量:
# 支持传入变量,如大于平均分40分的
a = df.Q1.mean()
df.query('Q1 > @a+40')
df.query('Q1 > `Q2`+@a')
df.eval()与df.query()类似,也可以用于表达式筛选:
# df.eval()用法与df.query类似
df[df.eval("Q1 > 90 > Q3 > 10")]
df[df.eval("Q1 > `Q2`+@a")]
df.filter()可以对行名和列名进行筛选,支持模糊匹配、正则表达式。
df.filter(items=['Q1', 'Q2']) # 选择两列
df.filter(regex='Q', axis=1) # 列名包含Q的列
df.filter(regex='e$', axis=1) # 以e结尾的列
df.filter(regex='1$', axis=0) # 正则,索引名以1结尾
df.filter(like='2', axis=0) # 索引中有2的
# 索引中以2开头、列名有Q的
df.filter(regex='^2', axis=0).filter(like='Q', axis=1)
Pandas提供了一个按列数据类型筛选的功能df.select_dtypes(include=None,exclude=None),它可以指定包含和不包含的数据类型,如果只有一个类型,传入字符;如果有多个类型,传入列表。
df.select_dtypes(include=['float64']) # 选择float64型数据
df.select_dtypes(include='bool')
df.select_dtypes(include=['number']) # 只取数字型
df.select_dtypes(exclude=['int']) # 排除int类型
df.select_dtypes(exclude=['datetime64'])
如果没有满足条件的数据,会返回一个仅有索引的DataFrame。
# 对所有字段指定统一类型
df = pd.DataFrame(data, dtype='float32')
# 对每个字段分别指定
df = pd.read_excel(data, dtype={'team': 'string', 'Q1': 'int32'})
Pandas可以用以下方法智能地推断各列的数据类型,会返回一个按推断修改后的DataFrame。如果需要使用这些类型的数据,可以赋值替换。
# 自动转换合适的数据类型
df.infer_objects() # 推断后的DataFrame
df.infer_objects().dtypes
'''
name object
team object
Q1 int64
Q2 int64
Q3 int64
Q4 int64
dtype: object
'''
# 推荐这个新方法,它支持string类型
df.convert_dtypes() # 推断后的DataFrame
df.convert_dtypes().dtypes
'''
name string
team string
Q1 Int64
Q2 Int64
Q3 Int64
Q4 Int64
dtype: object
'''
pd.to_XXX系统方法可以将数据安全转换,errors参数可以实现无法转换则转换为兜底类型:
# 按大体类型推定
m = ['1', 2, 3]
s = pd.to_numeric(s) # 转成数字
pd.to_datetime(m) # 转成时间
pd.to_timedelta(m) # 转成时间差
pd.to_datetime(m, errors='coerce') # 错误处理
pd.to_numeric(m, errors='ignore')
pd.to_numeric(m errors='coerce').fillna(0) # 兜底填充
pd.to_datetime(df[['year', 'month', 'day']]) # 组合成日期
转换为数字类型时,默认返回的dtype是float64还是int64取决于提供的数据。使用downcast参数获得向下转换后的其他类型。
# 最低期望
pd.to_numeric(m, downcast='integer') # 至少为有符号int数据类型
# array([1, 2, 3], dtype=int8)
pd.to_numeric(m, downcast='signed') # 同上
# array([1, 2, 3], dtype=int8)
pd.to_numeric(m, downcast='unsigned') # 至少为无符号int数据类型
# array([1, 2, 3], dtype=uint8)
pd.to_numeric(m, downcast='float') # 至少为float浮点类型
# array([1., 2., 3.], dtype=float32)
可以应用在函数中:
df = df.select_dtypes(include='number')
# 应用函数
df.apply(pd.to_numeric)
astype()是最常见也是最通用的数据类型转换方法,一般我们使用astype()操作数据转换就可以了。
df.Q1.astype('int32').dtypes
# dtype('int32')
df.astype({'Q1': 'int32','Q2': 'int32'}).dtypes
'''
Q1 int32
Q2 int32
Q3 int64
Q4 int64
dtype: object
'''
以下是一些使用示例:
df.index.astype('int64') # 索引类型转换
df.astype('int32') # 所有数据转换为int32
df.astype({'col1': 'int32'}) # 指定字段转指定类型
s.astype('int64')
s.astype('int64', copy=False) # 不与原数据关联
s.astype(np.uint8)
df['name'].astype('object')
data['Q4'].astype('float')
s.astype('datetime64[ns]')
data['状态'].astype('bool')
当数据的格式不具备转换为目标类型的条件时,需要先对数据进行处理。例如"89.3%"是一个字符串,要转换为数字,要先去掉百分号:
# 将"89.3%"这样的文本转为浮点数
data.rate.apply(lambda x: x.replace('%', '')).astype('float')/100
我们通常使用pd.to_datetime()和s.astype('datetime64[ns]')来做时间类型转换,第14章会专门介绍这两个函数。
t = pd.Series(['20200801', '20200802'])
t
'''
0 20200801
1 20200802
dtype: object
'''
pd.to_datetime(t)
'''
0 2020-08-01
1 2020-08-02
dtype: datetime64[ns]
'''
t.astype('datetime64[ns]')
'''
0 2020-08-01
1 2020-08-02
dtype: datetime64[ns]
'''
df.sort_index()实现按索引排序,默认以从小到大的升序方式排列。如希望按降序排序,传入ascending=False:
# 索引降序
df.sort_index(ascending=False)
'''
name team Q1 Q2 Q3 Q4
99 Ben E 21 43 41 74
98 Eli E 11 74 58 91
97 Lincoln4 C 98 93 1 20
96 Austin7 C 21 31 30 43
95 Gabriel C 48 59 87 74
.. ... ... .. .. .. ..
4 Oah D 65 49 61 86
3 Eorge C 93 96 71 78
2 Ack A 57 60 18 84
1 Arry C 36 37 37 57
0 Liver E 89 21 24 64
[100 rows x 6 columns]
'''
按列索引名排序:
# 在列索引方向上排序
df.sort_index(axis=1, ascending=False)
'''
team name Q4 Q3 Q2 Q1
0 E Liver 64 24 21 89
1 C Arry 57 37 37 36
2 A Ack 84 18 60 57
3 C Eorge 78 71 96 93
4 D Oah 86 61 49 65
.. ... ... .. .. .. ..
95 C Gabriel 74 87 59 48
96 C Austin7 43 30 31 21
97 C Lincoln4 20 1 93 98
98 E Eli 91 58 74 11
99 E Ben 74 41 43 21
[100 rows x 6 columns]
'''
更多方法如下:
s.sort_index() # 升序排列
df.sort_index() # df也是按索引进行排序
df.team.sort_index()
s.sort_index(ascending=False) # 降序排列
s.sort_index(inplace=True) # 排序后生效,改变原数据
# 索引重新0-(n-1)排,很有用,可以得到它的排序号
s.sort_index(ignore_index=True)
s.sort_index(na_position='first') # 空值在前,另'last'表示空值在后
s.sort_index(level=1) # 如果多层,排一级
s.sort_index(level=1, sort_remaining=False) # 这层不排
# 行索引排序,表头排序
df.sort_index(axis=1) # 会把列按列名顺序排列
df.reindex()指定自己定义顺序的索引,实现行和列的顺序重新定义:
df = pd.DataFrame({
'A': [1,2,4],
'B': [3,5,6]
}, index=['a', 'b', 'c'])
df
'''
A B
a 1 3
b 2 5
c 4 6
'''
# 按要求重新指定索引顺序
df.reindex(['c', 'b', 'a'])
'''
A B
c 4 6
b 2 5
a 1 3
'''
# 指定列顺序
df.reindex(['B', 'A'], axis=1)
'''
B A
a 3 1
b 5 2
c 6 4
'''
数据值的排序主要使用sort_values(),数字按大小顺序,字符按字母顺序。Series和DataFrame都支持此方法:
df.Q1.sort_values()
'''
37 1
39 2
85 2
58 4
82 4
..
3 93
88 96
38 97
19 97
97 98
Name: Q1, Length: 100, dtype: int64
'''
DataFrame需要传入一个或多个排序的列名:
df.sort_values('Q4')
'''
name team Q1 Q2 Q3 Q4
56 David B 21 47 99 2
19 Max E 97 75 41 3
90 Leon E 38 60 31 7
6 Acob B 61 95 94 8
88 Aaron A 96 75 55 8
.. ... ... .. .. .. ..
75 Stanley A 69 71 39 97
36 Jaxon E 88 98 19 98
62 Matthew C 44 33 41 98
72 Luke6 D 15 97 95 99
60 Ronnie B 53 13 34 99
[100 rows x 6 columns]
'''
默认排序是升序,但可以指定排序方式,下例先按team升序排列,如遇到相同的team再按name降序排列。
df.sort_values(by=['team', 'name'], ascending=[True, False])
'''
name team Q1 Q2 Q3 Q4
79 Tyler A 75 16 44 63
40 Toby A 52 27 17 68
75 Stanley A 69 71 39 97
34 Reggie1 A 30 12 23 9
9 Oscar A 77 9 26 67
.. ... ... .. .. .. ..
82 Finn E 4 1 55 32
98 Eli E 11 74 58 91
76 Dexter E 73 94 53 20
99 Ben E 21 43 41 74
41 Arlo8 E 48 34 52 51
[100 rows x 6 columns]
'''
其他常用方法如下:
s.sort_values(ascending=False) # 降序
s.sort_values(inplace=True) # 修改生效
s.sort_values(na_position='first') # 空值在前
# df按指定字段排列
df.sort_values(by=['team'])
df.sort_values('Q1')
# 按多个字段,先排team,在同team内再看Q1
df.sort_values(by=['team', 'Q1'])
# 全降序
df.sort_values(by=['team', 'Q1'], ascending=False)
# 对应指定team升Q1降
df.sort_values(by=['team', 'Q1'], ascending=[True, False])
# 索引重新0-(n-1)排
df.sort_values('team', ignore_index=True)
有时候需要用索引和数据值混合排序。下例中假如name是索引,我们需要先按team排名,再按索引排名:
df.set_index('name', inplace=True) # 设置name为索引
df.index.names = ['s_name'] # 给索引起名
df.sort_values(by=['s_name', 'team']) # 排序
'''
team Q1 Q2 Q3 Q4
name
Aaron A 96 75 55 8
Ack A 57 60 18 84
Acob B 61 95 94 8
Adam C 90 32 47 39
Aiden D 20 31 62 68
... ... .. .. .. ..
Toby A 52 27 17 68
Tommy C 29 44 28 76
Tyler A 75 16 44 63
William C 80 68 3 26
Zachary E 12 71 85 93
[100 rows x 5 columns]
'''
以下方法也可以实现上述需求,不过要注意顺序:
# 设置索引,按team排序,再按索引排序
df.set_index('name').sort_values('team').sort_index()
另外,还可以使用df.reindex(),通过给定新的索引方式来排名,按照这个思路可以实现人工指定任意顺序。
# 按姓名排序后取出排名后的索引列表
df.name.sort_values().index
'''
Int64Index([88, 2, 6, 33, 94, 83, 57, 63, 32, 12, 41, 1, 22, 96, 99, 44, 71,
52, 67, 86, 49, 91, 28, 56, 76, 42, 30, 98, 38, 73, 78, 81, 3, 21,
89, 27, 82, 53, 95, 92, 39, 5, 25, 64, 17, 51, 68, 24, 61, 47, 15,
93, 36, 66, 50, 31, 16, 43, 84, 10, 90, 58, 7, 85, 97, 0, 11, 48,
87, 59, 20, 72, 23, 62, 19, 77, 70, 4, 54, 9, 8, 34, 65, 29, 74,
60, 45, 80, 35, 37, 75, 26, 13, 69, 14, 40, 46, 79, 18, 55],
dtype='int64')
'''
# 将新的索引应用到数据中
df.reindex(df.name.sort_values().index)
'''
name team Q1 Q2 Q3 Q4
88 Aaron A 96 75 55 8
2 Ack A 57 60 18 84
6 Acob B 61 95 94 8
33 Adam C 90 32 47 39
94 Aiden D 20 31 62 68
.. ... ... .. .. .. ..
40 Toby A 52 27 17 68
46 Tommy C 29 44 28 76
79 Tyler A 75 16 44 63
18 William C 80 68 3 26
55 Zachary E 12 71 85 93
[100 rows x 6 columns]
'''
nsmallest()和nlargest()用来实现数字列的排序,并可指定返回的个数:
# 先按Q1最小在前,如果相同,Q2小的在前
df.nsmallest(5, ['Q1', 'Q2'])
'''
name team Q1 Q2 Q3 Q4
37 Sebastian C 1 14 68 48
85 Liam B 2 80 24 25
39 Harley B 2 99 12 13
82 Finn E 4 1 55 32
58 Lewis B 4 34 77 28
'''
以上显示了前5个最小的值,仅支持数字类型的排序。下面是几个其他示例:
s.nsmallest(3) # 最小的3个
s.nlargest(3) # 最大的3个
# 指定列
df.nlargest(3, 'Q1')
df.nlargest(5, ['Q1', 'Q2'])
df.nsmallest(5, ['Q1', 'Q2'])