pandas将字符串转换成时间_高性能Pandas:eval() 与 query() 上篇

正如我们在前面的文章中已经介绍过的,Python 数据科学生态环境的强大力量是建立在 NumPy 和 Pandas 的基础之上,并通过直观的语法将基本操作转换成C语言:

NumPy中,是向量化/广播式操作

Pandas中是分组式运算

虽然这些抽象功能对于许多常见的用例来说是简洁和高效的,但它们往往依赖于临时中间对象的创建,这样就会占用大量的计算时间和内存,造成算力的不必要的浪费。

从0.13版本(2014年1月正式发布)开始,Pandas引入了一些实验性的工具,允许用户直接运行C语言速度的操作,而不需要费力地配置中间数组。这些就是eval() 和 query() 函数。

eval()query()它们依赖于Numexpr程序包。

(https://github.com/pydata/numexpr)。

下面我们将介绍它们的使用方法,并给出我自己做项目中的一些经验,告诉你何时可以考虑使用它们。

query() 和 eval() 的设计动机:复合表达式

前面我们已经看到,NumPy 和 Pandas 都支持快速的向量化操作;例如,对下面两个数组进行求和:

51fc3a6f1f417b283085923ef0b0d48c.png

这样做比普通的Python 循环或列表综合要快很多:

e484ee73d5ebcef6e5ef2316aa36a4ce.png

但这种运算在处理复合表达式的问题时,效率会变得不那么高效。例如,考虑以下表达式:

c2bc60122fde2f90a2b1d2d978a348a2.png

因为 NumPy 会对每个代数子表达式进行计算,所以大致等价于以下内容:

d72b453ffd49e7941a63cb5a0f838ba4.png

换句话说,每一个中间步骤都在内存中显式分配

如果x和y数组非常大,会导致大量的内存和计算开销。

Numexpr 程序库提供了元素到元素的复合代数式运算,而无需分配完整的中间数组。

Numexpr 文档中有更多的细节,但简而言之,这个程序库其实就是用一个 NumPy 风格的字符串代数式进行运算:

5eacf48914bfa4cebbced48795910122.png

好处是,Numexpr 在计算代数式时不需要为临时数组分配全部内存,因此可以比 NumPy 更高效,尤其是对于大型数组的处理更为适合。

我们将在下面讨论 Pandas 的 eval() 和 query() 工具在概念上是相似的,并且也是基于于 Numexpr 来实现的。

# 用 pandas.eval() 实现高性能运算

Pandas中的 eval() 函数使用字符串表达式实现了 DataFrames 的高性能运算。例如以下的DataFrames:

37f526f408a5093901654a2dac3a6b92.png

如果要用普通的 Pandas 方法计算所有四个 DataFrames 的总和,我们可以直接写出总和:

5089a2ab8852a7df22fea19af521300f.png

可以通过 pd.eval  和字符串表达式计算出同样的结果:

a69f89b2509b2a0369f25ec5b85bcc3a.png

这个表达式的 eval() 版本比普通方法大约快了一倍(而且使用的内存少得多),同时给出了同样的结果:

f80c7781daa7b1de3cf52c8903f47d78.png

## pd.eval() 支持的运算

从Pandas v0.16版本开始,pd.eval()支持多种操作运算。为了演示这些操作,我们将使用一个整数类型的 DataFrames:

e970aa2e68b069ba93fc47309d3d6321.png

### 算术运算符

pd.eval() 支持所有的算术运算符。例如:

615c1f3d779bfb9be37bda0a8c3c1bce.png

### 比较运算符

pd.eval() 支持所有比较运算符,包括链式表达式:

cf12669923415d9552343c6b690d5dc8.png

### 位运算符

pd.eval() 支持 & 和 | (或)等位运算符:

81a59924610d9d6d3241ff76dd6c9b76.png

此外,它还支持在布尔表达式中使用 and 和 or 等字面值:

0948e85c0a05e1621fcca8cd3e995afc.png

### 对象属性与索引

pd.eval() 支持通过 obj.attr 语法获取对象属性,以及通过 obj[index] 语法获取对象索引:

3ed21749be7aed9ac406c66fe49ef0fc.png

### 其他运算

其他的操作,如函数调用、条件语句、循环和其他更复杂的运算。目前还不能在 pd.eval() 中实现。

如果你想执行这些更复杂的表达式类型,你可以使用Numexpr 库来实现。

# 用 DataFrame.eval() 实现列间运算

就像 Pandas 有一个顶层的 pd.eval() 函数一样,DataFrames 也有一个 eval() 方法可以做类似的运算。

使用 eval() 方法的好处是可以借助列名称来进行运算。我们以这个标签数组为例:

e5b1094889d1002c992db89df6de54c3.png

如果使用前面介绍的 pd.eval(),我们可以通过下面的代数式计算出这三列:

a671e03ac26eac8a18ed28d1a244b47e.png

DataFrame.eval() 方法可以通过列名称实现更简洁的表达式:

ac9daa971d7d17452e37db07b31f4215.png

请注意,这里我们将列名称作为变量来计算表达式,结果也是正确的。

## 用DataFrame.eval() 新增列

除了刚才讨论的运算功能外,DataFrame.eval() 还允许创建新的列。

让我们使用之前的 DataFrame 来演示,它有 'A'、'B' 和 'C' 列:

e4d2673111ef74d8a9c4372df4c9940e.png

我们可以使用 df.eval() 来创建一个新的列'D',并给它分配一个从其他列计算出来的值:

6f95303ce256721b9b42542ae3b11069.png

还可以修改已有的列:

6363d3fb0477de28862968ddb97895b1.png

## DataFrame.eval() 使用局部变量

DataFrame.eval() 方法支持一种额外的语法,它允许它与本地 Python 变量一起工作。如下所示:

18483cbb0c72c3143ae39c79fdde08f3.png

这里的 @ 字符表示这是一个变量名,而不是一个列名,它让你有效地评估两个 "命名空间 "的表达式:列的命名空间和 Python 对象的命名空间。

注意这个 @ 字符只能在 DataFrame.eval() 方法中使用,而不能在 pandas.eval() 函数中使用,因为 pandas.eval()函数只能获取一个 (Python) 命名空间的内容。

# DataFrame.query() 方法

DataFrame有另一个基于已评估字符串的方法,称为query()方法。考虑以下内容:

7a9f6bebde52caee13d7c01524132d3f.png

就像在我们讨论中使用的例子一样DataFrame.eval(),这是一个涉及列的表达式DataFrame。

但是,不能使用DataFrame.eval()语法来表达它!

相反,对于这种类型的过滤操作,可以使用以下query()方法:

与掩蔽表达式相比,除了计算效率更高之外,它更易于阅读和理解。请注意,该query()方法还接受@标记以标记局部变量:

# 何时使用eval()和query()

性能决定使用时机。

在考虑是否使用这些功能时,有两个注意事项:计算时间内存使用量

内存使用是最可预测的方面。如前所述,每个涉及NumPy数组或PandasDataFrame的复合表达式都将导致隐式创建临时数组。例如:

e336f15e79b242dd15b4218c3669a841.png

大致相当于:

668cb9f557a67f1ef690ab5b673b2112.png

如果DataFrame临时占用内存比我们电脑可用系统内存要大,(通常为数GB),则最好使用eval()  orquery()表达式。

我们可以使用以下方法检查数组的近似大小(以字节为单位):

e529cfc23f238162d480425a1a480271.png

在性能方面,eval()即使您没有最大限度地利用系统内存,也可以更快。

本文介绍了eval()query()的使用方法。主要是让计算机的效率达到最高,是Pandas高级用法之一。

关注?君,持续学习精进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值