在数据分析和数据建模的过程中需要对数据进行清洗和整理等工作,有时需要对数据增删字段。本文将介绍Pandas对数据的复杂查询。
实际业务需求往往需要按照一定的条件甚至复杂的组合条件来查询数据。本文将介绍如何发挥Pandas数据筛选的无限可能,随心所欲地取用数据。
推荐文章
01 逻辑运算
类似于Python的逻辑运算,我们以DataFrame其中一列进行逻辑计算,会产生一个对应的由布尔值组成的Series,真假值由此位上的数据是否满足逻辑表达式决定。例如下例中索引为0的数据值为89,大于36,所以最后值为True。
1# Q1成绩大于36
2df.Q1 > 36
3'''
40 True
51 False
62 True
73 True
84 True
9 ...
10
1195 True
1296 False
1397 True
1498 False
1599 False
16Name: Q1, Length: 100, dtype: bool
17'''
一个针对索引的逻辑表达式会产生一个array类型数组,该数组由布尔值组成。根据逻辑表达式,只有索引为1的值为True,其余全为False。
1# 索引等于1
2df.index == 1
3'''
4array([False, True, False, False, False, False, False, False, False,
5 False, False, False, False, False, False, False, False, False,
6 False, False, False, False, False, False, False, False, False,
7 False, False, False, False, False, False, False, False, False,
8 False, False, False, False, False, False, False, False, False,
9 False, False, False, False, False, False, False, False, False,
10 False, False, False, False, False, False, False, False, False,
11 False, False, False, False, False, False, False, False, False,
12 False, False, False, False, False, False, False, False, False,
13 False, False, False, False, False, False, False, False, False,
14 False, False, False, False, False, False, False, False, False,
15 False])
16'''
再看一下关于DataFrame的逻辑运算,判断数值部分的所有值是否大于60,满足表达式的值显示为True,不满足表达式的值显示为False。
1# df.loc[:,'Q1':'Q4']部分只取数字部分,否则会因字符无大于运算而报错
2df.loc[:,'Q1':'Q4'] > 60
3'''
4 Q1 Q2 Q3 Q4
50 True False False True
61 False False False False
72 False False False True
83 True True True True
94 True False True True
10.. ... ... ... ...
1195 False False True True
1296 False False False False
1397 True True False False
1498 False True False True
1599 False False False True
16[100 rows x 4 columns]
17'''
除了逻辑运算,Pandas还支持组合条件的Python位运算:
1# Q1成绩不小于60分,并且是C组成员
2~(df.Q1 < 60) & (df['team'] == 'C')
3'''
40 False
51 False
62 False
73 True
84 False
9 ...
1095 False
1196 False
1297 True
1398 False
1499 False
15Length: 100, dtype: bool
16'''
02 逻辑筛选数据
切片([])、.loc[]和.iloc[]均支持上文所介绍的逻辑表达式。通过逻辑表达式进行复杂条件的数据筛选时需要注意,表达式输出的结果必须是一个布尔序列或者符合其格式要求的数据形式。例如,df.iloc[1+1]和df.iloc[lambda df: len(df)-1]计算出一个数值,符合索引的格式,df.iloc[df.index==8]返回的是一个布尔序列,df.iloc[df.index]返回的是一个索引,它们都是有效的表达式。
以下是切片([])的一些逻辑筛选的示例:
1df[df['Q1'] == 8] # Q1等于8
2df[~(df['Q1'] == 8)] # 不等于8
3df[df.name == 'Ben'] # 姓名为Ben
4df[df.Q1 > df.Q2]
以下是.loc[]和.lic[]的一些示例:
1# 表达式与切片一致
2df.loc[df['Q1'] > 90, 'Q1':] # Q1大于90,只显示Q1
3df.loc[(df.Q1 > 80) & (df.Q2 < 15)] # and关系
4df.loc[(df.Q1 > 90) | (df.Q2 < 90)] # or关系
5df.loc[df['Q1'] == 8] # 等于8
6df.loc[df.Q1 == 8] # 等于8
7df.loc[df['Q1'] > 90, 'Q1':] # Q1大于90,显示Q1及其后所有列
需要注意的是在进行或(|)、与(&)、非(~)运算时,各个独立逻辑表达式需要用括号括起来。
any和all对逻辑计算后的布尔序列再进行判断,序列中所有值都为True时all才返回True,序列中只要有一个值为True时any就返回True。它们还可以传入axis参数的值,用于指定判断的方向,与Pandas的axis参数整体约定一样,默认为0列方向,传入1为行方向。利用这两个方法,我们可以对整体数据进行逻辑判断,例如:
1# Q1、Q2成绩全为超过80分的
2df[(df.loc[:,['Q1','Q2']] > 80).all(1)]
3# Q1、Q2成绩至少有一个超过80分的
4df[(df.loc[:,['Q1','Q2']] > 80).any(1)]
上例对两个列整体先做逻辑计算得到一个两列的布尔序列,然后用all和any在行方向上做逻辑计算。
03 函数筛选
可以在表达式使用lambda函数,默认变量是其操作的对象。如果操作的对象是一个DataFrame,那么变量就是这个DataFrame;如果是一个Series,那么就是这个Series。可以看以下例子,s就是指df.Q1这个Series:
1# 查询最大索引的值
2df.Q1[lambda s: max(s.index)] # 值为21
3# 计算最大值
4max(df.Q1.index) # 99
5df.Q1[df.index==99]
6'''
799 21
8Name: Q1, dtype: int64
9'''
下面是一些示例:
1df[lambda df: df['Q1'] == 8] # Q1为8的
2df.loc[lambda df: df.Q1 == 8, 'Q1':'Q2'] # Q1为8的,显示 Q1、Q2
3df.loc[:, lambda df: df.columns.str.len()==4] # 由真假值组成的序列
4df.loc[:, lambda df: [i for i in df.columns if 'Q' in i]] # 列名列表
5df.iloc[:3, lambda df: df.columns.str.len()==2] # 由真假值组成的序列
04 比较函数
Pandas提供了一些比较函数,使我们可以将逻辑表达式替换为函数形式。
1# 以下相当于 df[df.Q1 == 60]
2df[df.Q1.eq(60)]
3'''
4 name team Q1 Q2 Q3 Q4
520 Lucas A 60 41 77 62
6'''
除了.eq(),还有:
1df.ne() # 不等于 !=
2df.le() # 小于等于 <=
3df.lt() # 小于 <
4df.ge() # 大于等于 >=
5df.gt() # 大于 >
使用示例如下:
1df[df.Q1.ne(89)] # Q1不等于89
2df.loc[df.Q1.gt(90) & df.Q2.lt(90)] # and关系,Q1>90,Q2<90
这些函数可以传入一个定值、数列、布尔序列、Series或DataFrame,来与原数据比较。
另外还有一个. isin()函数,用于判断数据是否包含指定内容。可以传入一个列表,原数据只需要满足其中一个存在即可;也可以传入一个字典,键为列名,值为需要匹配的值,以实现按列个性化匹配存在值。
1# isin
2df[df.team.isin(['A','B'])] # 包含A、B两组的
3df[df.isin({'team': ['C', 'D'], 'Q1':[36,93]})] # 复杂查询,其他值为NaN
05 查询df.query()
df.query(expr)使用布尔表达式查询DataFrame的列,表达式是一个字符串,类似于SQL中的where从句,不过它相当灵活。
1df.query('Q1 > Q2 > 90') # 直接写类型SQL where语句
2df.query('Q1 + Q2 > 180')
3df.query('Q1 == Q2')
4df.query('(Q1<50) & (Q2>40) and (Q3>90)')
5df.query('Q1 > Q2 > Q3 > Q4')
6df.query('team != "C"')
7df.query('team not in ("E","A","B")')
8# 对于名称中带有空格的列,可以使用反引号引起来
9df.query('B == `team name`')
还支持使用@符引入变量:
1# 支持传入变量,如大于平均分40分的
2a = df.Q1.mean()
3df.query('Q1 > @a+40')
4df.query('Q1 > `Q2`+@a')
df.eval()与df.query()类似,也可以用于表达式筛选:
1# df.eval()用法与df.query类似
2df[df.eval("Q1 > 90 > Q3 > 10")]
3df[df.eval("Q1 > `Q2`+@a")]
06 筛选df.filter()
df.filter()可以对行名和列名进行筛选,支持模糊匹配、正则表达式。
1df.filter(items=['Q1', 'Q2']) # 选择两列
2df.filter(regex='Q', axis=1) # 列名包含Q的列
3df.filter(regex='e$', axis=1) # 以e结尾的列
4df.filter(regex='1$', axis=0) # 正则,索引名以1结尾
5df.filter(like='2', axis=0) # 索引中有2的
6# 索引中以2开头、列名有Q的
7df.filter(regex='^2', axis=0).filter(like='Q', axis=1)
07 按数据类型查询
Pandas提供了一个按列数据类型筛选的功能df.select_dtypes(include=None, exclude=None),它可以指定包含和不包含的数据类型,如果只有一个类型,传入字符;如果有多个类型,传入列表。
1df.select_dtypes(include=['float64']) # 选择float64型数据
2df.select_dtypes(include='bool')
3df.select_dtypes(include=['number']) # 只取数字型
4df.select_dtypes(exclude=['int']) # 排除int类型
5df.select_dtypes(exclude=['datetime64'])
如果没有满足条件的数据,会返回一个仅有索引的DataFrame。
小结
本文介绍了如何实现复杂逻辑的数据查询需求,复杂的数据查询功能是Pandas的杀手锏,这些功能Excel实现起来会比较困难,有些甚至无法实现,这正是Pandas的优势所在。