Pandas进阶筛选和取数操作

总结了pandas各种进阶操作与使用技巧,并且对各方法间的效率进行比较。

创建一个pandas的dataframe对象作为下文样例:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A":np.arange(0,5),
    "B":np.arange(5,10),
    "C":np.arange(15,20),
    "D":np.arange(25,30),
    "E":np.arange(35,40),
})

1 通过向量化筛选

通过使用pandas表达式,pandas会返回一个包含了布尔数据的series对象:

print(df['A']>2)
type(df['A']>2)

  这时,我们只需将布尔型的series再次输入给df,就能筛选出我们想要的数据。这种方法也称为向量化(Vectorization),即使用series类型的布尔向量进行取数:

df[df['A']>2]

 当存在多个条件时,用户要使用 括号 分割各条件,并使用 ‘&’ 和 ‘|’ 逻辑运算符,注意不能使用 and 或者 or

df[df['A'] > 1 and df['A'] < 4]     # 错误写法
df[(df['A'] > 1) & (df['A'] < 4)]   # 正确写法

 上面我们是直接使用大于号和小于号进行比较,也可以使用pandas的数值函数:

df.eq()    # 等于相等 ==
df.ne()    # 不等于 !=
df.le()    # 小于等于 >=
df.lt()    # 小于 <
df.ge()    # 大于等于 >=
df.gt()    # 大于 >

# 具体用法如下
df[df.A.ge(3)]

 

对于字符串型的内容,我们可以使用pandas.str里的方法。这里我们先将第一列数改为字符串,再取出第一列包含字符串‘2’所在的行:

df['A'] = df['A'].astype('str')   # 将第一列数字改为‘str’类型

df[df['A'].str.contains('2')]     # 取出字符串‘2’所在的行

类似的,除了str.contains外,还有str.startswith与str.endswith,这里不作过多展示。

2 通过query()、eval()、filter()筛选

前面使用向量法进行取数操作,本质上是通过pandas表达式构造series型的布尔向量进行取数。

接下来我们使用pandas提供的更高效的取数函数:query()和eval()。二者语法是一致的,区别在于前者取出所在的行,后者是返回布尔值。与向量取数不同的是,query和eval函数并不要求多个条件使用括号隔开,也不限定逻辑表达式的形式。样例如下:

df.query('A >= 1 and B <= 8')

df.eval('A >= 1 and B <= 8')

当需要传入外部对象时,需要使用@符号:

a = 1
b = 8
df.query('A >= @a and B <= @b')

除此以外,pandas还有以下取数方法:

  • 过滤器filter(),筛选需要的行和列;
  • 取值函数where(),返回符合要求的数据,不符合的返回NaN;
  • 取反函数mask(),语法与where()一致,但返回的是不满足要求的数据。

上述的取数方法比向量化的方法更高效,是因为在处理复合表达式时,在上述案例中:

df[(df['A'] > 1) & (df['A'] < 4)]

实际上是等价于:

a = (df['A'] > 1)
b = (df['A'] < 4)
df[a & b]

每个中间步骤都存在内存显示分配,如果某个复合表达式特别大,会导致很大的内存和计算开销。因此催生了Numexpr 程序库,提供了元素到元素的复合代数式运算,不需要为临时数组分配全部内存,可以实现c级别的速度。

Pandas 的 eval() 和 query() 工具是基于于 Numexpr 来实现的,因此效率很高,使用时建议优先考虑。

3 通过定位取数

最简单的取数方式是使用索引切片。不过二者在pandas中都不常用

df[df.index == 1]          # 取出第二行的数据

df[start : stop : step]    # 起始位置start,结束位置stop,步长step

切片的使用方式与python定义的模式相同;当步长为负数时,pandas会按倒叙取数。

除此以外,pandas支持的通过定位取数的方法有:

  • loc
  • iloc
  • at
  • iat
  • any
  • all

这些作为常见的方法,这里不进行过多的介绍,使用中注意以下几点即可:

  1. 在Dataframe中,如果只有一个中括号(如df.loc[0])返回的是series对象,如果是两个中括号(如df.loc[[0]])才会返回dataframe对象。
  2. at 和 iat 与 loc 和 iloc 的区别在于前者只会返回一个值,并且效率稍高。
  3. any()是至少有一个为True则为True;all()是所有为True才会为True。

  4. any()和all()中,当传入的axis=1,会按照行进行查询;axis=0表示按照列查询。

4 通过迭代器取数

前面我们是通过定位取数,但是通常要通过for循环来实现迭代,pandas中迭代取数的方法主要包括:

iteritems()

  • iteritems()是按列遍历,返回一对键值对,其中键是列标签,值是列内容(值是一个series对象)。

iterrows()

  • iterrows()可以看成iteritems()的转置,是按行遍历,返回的键是行标签,值是行内容(值是一个series对象)。

itertuples()

  • itertuples()与iterrows()是类似的,都是按行返回结构,区别在于它将iterrows()的键和值都放入一个元组中,一起返回。

for循环 + zip()

  • zip()可以将pandas中的两列缝合在一起,再通过for循环一起输出,例如:
  • [a+b for a, b in zip(df['A'], df['B'])]
        

apply函数

  • pandas对象具有apply方法,通常需要和lambda函数结合使用,例如:
  • df.apply(lambda x: x.A + x.B, axis=1)

numpy矩阵

  • 通过pandas对象的 .values 或者 .to_numpy 方法,将dataframe对象转变为numpy矩阵,再进行相关操作。
  • df['A'].values + df['B'].values

5 处理缺失数据

最后讲一下处理缺失值‘NaN’的方法。我们将案例中的第一列添加两个空值:

df['A'][0] = np.nan
df['A'][1] = np.nan
df

 我们可以使用 .isnull() 或者 .isna() 查看空值,这俩效果相同:

df.isna()

 

但是这样还不够方便,我们可以用any()函数查看存在缺失值的列:

df.isnull().any()

我们还可以将pandas转换为numpy矩阵来查看缺失值所在的行:

df[df.isnull().to_numpy() == True]   # df[df.isnull().values == True] 也可以

我们还可以对各行列的缺失值,缺失率进行统计:

isnull().sum(axis=0)    # 列缺失统计
isnull().sum(axis=1)    # 行缺失统计
isnull().mean()         # 缺失率

这里有必要解释一下,为什么要使用isnull,不能直接判断。在python中,NaN类型不等于任何值,连自己都不相等。例如:

df['A'][0] == np.nan

找出缺失数据后,用户可能需要删除缺失值所在的行或列,使用drop()方法即可。也可以使用以下方法:

  • .dropna()会删除缺失值所在的行或列;
  • .fillna()会填充指定行列的缺失值;

 6 各取数方法的速度比较

直接说结论:numpy完胜

在上述介绍的方法中,速度由快至慢分别是:

numpy矩阵、iteritems、pandas向量化操作、for+zip、itertuples、apply、ilat、iloc、iterrows。

(其中query和eval方法 > pandas向量化操作)

由于numpy矩阵运算是基于c语言实现的,效率上远优于其他迭代方法。如下图所示,numpy的向量化操作,其效率完胜于pandas的向量化操作,而后者的效率也远胜于普通的for循环。在内存空间充足的情况下,numpy和pandas向量化操作应当作为首选!

参考链接:

dataframe遍历效率对比_dataframe 遍历效率_Takoony的博客-CSDN博客

https://towardsdatascience.com/how-to-make-your-pandas-loop-71-803-times-faster-805030df4f06

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值