正如我们在前面几节中已经看到的,PyData堆栈的强大功能建立在NumPy和Pandas通过直观语法将基本操作使用C实现能力之上:例如NumPy的矢量化/广播操作,Pandas的分组类型操作。尽管这些抽象概念在许多常见的情况是有效和起作用的,它们经常依赖创建临时中间对象,它们导致计算时间和内存使用的不当开销。
从版本0.13开始,Pandas包含了一下实验性的工具允许你直接使用C速度操作,避免中间数组的浪费。这些工具是eval()和 query()函数,它们依赖 Numexpr包。我们将浏览它们的用法,并给出一些关于何时使用它们的经验法则。
eval()和 query()动机:复合表达式
我们已经看到NumPy和Pandas支持快速的矢量化操作;如,计算两个数组元素的和值:
import numpy as np
rng = np.random.RandomState(42)
x = rng.rand(1000000)
y = rng.rand(1000000)
%timeit x + y
100 loops, best of 3: 3.39 ms per loop
%timeit np.fromiter((xi + yi for xi, yi in zip(x, y)), dtype=x.dtype, count=len(x))
1 loop, best of 3: 266 ms per loop
但是这种概念在计算复合表达式时效率就不高了。例如,考虑如下表达:
mask = (x > 0.5) & (y < 0.5)
因为NumPy评估每个子表达式,这基本相当于做如下操作:
tmp1 = (x > 0.5)
tmp2 = (y < 0.5)
mask = tmp1 & tmp2
换句话说,每个步骤都需要明确的分配内存。如果X和y数组很大的话,那将导致显著的内存和计算开销。Numexpr库按元素计算这种复合表达式的能力,并不用分配完整的中间数组。文档The Numexpr documentation 由许多细节,但就目前来说,理解这个库接受想要计算的NumPy样式表达式的字符串就足够了:
import numexpr
mask_numexpr = numexpr.evaluate('(x > 0.5) & (y < 0.5)')
np.allclose(mask, mask_numexpr)
True
Numexpr计算表达式的好处是它不必使用全部临时数组,并且对于大数组来说,比NumPy效率更高。我们在这里讨论的Panda seval()和query()工具概念上类似,并且依赖于Numexpr包。
pandas.eval()用于高效操作
Pandas的evaluate()函数使用字符串表达式来来计算对DataFrame的操作。例如&