- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
测试代码:
import numpy as np
import pandas as pd
COUNT = 1000000
df = pd.DataFrame({
'y': np.random.normal(0, 1, COUNT),
'z': np.random.gamma(50, 1, COUNT),
})
%timeit df.y[(10 < df.z) & (df.z < 50)].mean()
%timeit df.y.values[(10 < df.z.values) & (df.z.values < 50)].mean()
%timeit df.eval('y[(10 < z) & (z < 50)].mean()', engine='numexpr')
我的机器(一个相当快的带有 Python 3.6 的 x86-64 Linux 桌面)上的输出是:
17.8 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.44 ms ± 502 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
46.4 ms ± 2.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
我明白为什么第二行要快一点(它忽略了 Pandas 索引)。但为什么使用 numexpr
的 eval()
方法这么慢?它不应该至少比第一种方法更快吗?文档确实使它看起来像:https://pandas.pydata.org/pandas-docs/stable/enhancingperf.html
最佳答案
从下面的调查来看,性能较差的一个不起眼的原因似乎是“开销”。
只是表达式y[(10 < z) & (z < 50)].mean()
的一小部分通过 numexpr
完成-模块。 numexpr
doesn't support indexing , 因此我们只能希望 (10 < z) & (z < 50)
加速 - 其他任何内容都将映射到 pandas
-操作。
然而,(10 < z) & (z < 50)
不是这里的瓶颈,可以很容易地看出:
%timeit df.y[(10 < df.z) & (df.z < 50)].mean() # 16.7 ms
mask=(10 < df.z) & (df.z < 50)
%timeit df.y[mask].mean() # 13.7 ms
%timeit df.y[mask] # 13.2 ms
df.y[mask]
-占运行时间的大部分。
我们可以比较 df.y[mask]
的分析器输出和 df.eval('y[mask]')
看看有什么不同。
当我使用以下脚本时:
import numpy as np
import pandas as pd
COUNT = 1000000
df = pd.DataFrame({
'y': np.random.normal(0, 1, COUNT),
'z': np.random.gamma(50, 1, COUNT),
})
mask = (10 < df.z) & (df.z < 50)
df['m']=mask
for _ in range(500):
df.y[df.m]
# OR
#df.eval('y[m]', engine='numexpr')
并用 python -m cProfile -s cumulative run.py
运行它(或 IPython 中的 %prun -s cumulative <...>
),我可以看到以下配置文件。
直接调用 pandas 功能:
ncalls tottime percall cumtime percall filename:lineno(function)
419/1 0.013 0.000 7.228 7.228 {built-in method builtins.exec}
1 0.006 0.006 7.228 7.228 run.py:1(<module>)
500 0.005 0.000 6.589 0.013 series.py:764(__getitem__)
500 0.003 0.000 6.475 0.013 series.py:812(_get_with)
500 0.003 0.000 6.468 0.013 series.py:875(_get_values)
500 0.009 0.000 6.445 0.013 internals.py:4702(get_slice)
500 0.006 0.000 3.246 0.006 range.py:491(__getitem__)
505 3.146 0.006 3.236 0.006 base.py:2067(__getitem__)
500 3.170 0.006 3.170 0.006 internals.py:310(_slice)
635/2 0.003 0.000 0.414 0.207 <frozen importlib._bootstrap>:958(_find_and_load)
我们可以看到几乎 100% 的时间花在了 series.__getitem__
上。没有任何开销。
通过 df.eval(...)
调用电话,情况就大不相同了:
ncalls tottime percall cumtime percall filename:lineno(function)
453/1 0.013 0.000 12.702 12.702 {built-in method builtins.exec}
1 0.015 0.015 12.702 12.702 run.py:1(<module>)
500 0.013 0.000 12.090 0.024 frame.py:2861(eval)
1000/500 0.025 0.000 10.319 0.021 eval.py:153(eval)
1000/500 0.007 0.000 9.247 0.018 expr.py:731(__init__)
1000/500 0.004 0.000 9.236 0.018 expr.py:754(parse)
4500/500 0.019 0.000 9.233 0.018 expr.py:307(visit)
1000/500 0.003 0.000 9.105 0.018 expr.py:323(visit_Module)
1000/500 0.002 0.000 9.102 0.018 expr.py:329(visit_Expr)
500 0.011 0.000 9.096 0.018 expr.py:461(visit_Subscript)
500 0.007 0.000 6.874 0.014 series.py:764(__getitem__)
500 0.003 0.000 6.748 0.013 series.py:812(_get_with)
500 0.004 0.000 6.742 0.013 series.py:875(_get_values)
500 0.009 0.000 6.717 0.013 internals.py:4702(get_slice)
500 0.006 0.000 3.404 0.007 range.py:491(__getitem__)
506 3.289 0.007 3.391 0.007 base.py:2067(__getitem__)
500 3.282 0.007 3.282 0.007 internals.py:310(_slice)
500 0.003 0.000 1.730 0.003 generic.py:432(_get_index_resolvers)
1000 0.014 0.000 1.725 0.002 generic.py:402(_get_axis_resolvers)
2000 0.018 0.000 1.685 0.001 base.py:1179(to_series)
1000 0.003 0.000 1.537 0.002 scope.py:21(_ensure_scope)
1000 0.014 0.000 1.534 0.002 scope.py:102(__init__)
500 0.005 0.000 1.476 0.003 scope.py:242(update)
500 0.002 0.000 1.451 0.003 inspect.py:1489(stack)
500 0.021 0.000 1.449 0.003 inspect.py:1461(getouterframes)
11000 0.062 0.000 1.415 0.000 inspect.py:1422(getframeinfo)
2000 0.008 0.000 1.276 0.001 base.py:1253(_to_embed)
2035 1.261 0.001 1.261 0.001 {method 'copy' of 'numpy.ndarray' objects}
1000 0.015 0.000 1.226 0.001 engines.py:61(evaluate)
11000 0.081 0.000 1.081 0.000 inspect.py:757(findsource)
再次在 series.__getitem__
中花费了大约 7 秒, 但也有大约 6 秒的开销 - 例如 frame.py:2861(eval)
中大约 2 秒在 expr.py:461(visit_Subscript)
中大约 2 秒.
我只进行了粗浅的调查(请参阅下面的更多详细信息),但这种开销似乎并不仅仅是恒定的,而且至少与系列中的元素数量呈线性关系。例如有 method 'copy' of 'numpy.ndarray' objects
这意味着数据被复制(目前尚不清楚,为什么这本身是必要的)。
我的收获:使用 pd.eval
只要计算的表达式可以用 numexpr
计算就具有优势独自的。一旦不是这种情况,可能就会因相当大的开销而不再是 yield 而是损失。
使用 line_profiler
(这里我使用 %lprun-magic(在用 %load_ext line_profliler
加载它之后)用于函数 run()
,它或多或少是上面脚本的副本)我们可以很容易地找到时间在 Frame.eval
中丢失的地方。 :
%lprun -f pd.core.frame.DataFrame.eval
-f pd.core.frame.DataFrame._get_index_resolvers
-f pd.core.frame.DataFrame._get_axis_resolvers
-f pd.core.indexes.base.Index.to_series
-f pd.core.indexes.base.Index._to_embed
run()
在这里我们可以看到是否花费了额外的 10%:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2861 def eval(self, expr,
....
2951 10 206.0 20.6 0.0 from pandas.core.computation.eval import eval as _eval
2952
2953 10 176.0 17.6 0.0 inplace = validate_bool_kwarg(inplace, 'inplace')
2954 10 30.0 3.0 0.0 resolvers = kwargs.pop('resolvers', None)
2955 10 37.0 3.7 0.0 kwargs['level'] = kwargs.pop('level', 0) + 1
2956 10 17.0 1.7 0.0 if resolvers is None:
2957 10 235850.0 23585.0 9.0 index_resolvers = self._get_index_resolvers()
2958 10 2231.0 223.1 0.1 resolvers = dict(self.iteritems()), index_resolvers
2959 10 29.0 2.9 0.0 if 'target' not in kwargs:
2960 10 19.0 1.9 0.0 kwargs['target'] = self
2961 10 46.0 4.6 0.0 kwargs['resolvers'] = kwargs.get('resolvers', ()) + tuple(resolvers)
2962 10 2392725.0 239272.5 90.9 return _eval(expr, inplace=inplace, **kwargs)
和_get_index_resolvers()
可以向下钻取到 Index._to_embed
:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1253 def _to_embed(self, keep_tz=False, dtype=None):
1254 """
1255 *this is an internal non-public method*
1256
1257 return an array repr of this object, potentially casting to object
1258
1259 """
1260 40 73.0 1.8 0.0 if dtype is not None:
1261 return self.astype(dtype)._to_embed(keep_tz=keep_tz)
1262
1263 40 201490.0 5037.2 100.0 return self.values.copy()
哪里O(n)
- 发生复制。
关于python - 为什么带有 numexpr 的 Pandas.eval() 这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52588653/
我需要在大约一千万点处评估 BesselK 函数。我知道scipy.special支持此为 scipy.special.kv(n, x) ,但我想要更快的评估以及内存有效的评估。理想的numexpr会
我过去在很多机器上成功安装了 numexpr。但现在我实在无法应付使其在新的 Linux 系统上运行。本质上我下载了 tar.gz 文件并且 python setup.py install --hom
我正在尝试执行以下操作: import numexpr as ne a = np.random.rand(10, 1) b = np.random.rand(1, 10) ne.NumExpr('su
我想评估 python (2.7) 中 numexpr 模块的性能。为此,我创建了一个大小为 (10^5, 10^5) 的随机稀疏矩阵。但是,下面的脚本已经在表达式求值步骤中抛出错误,表示它无法识别对
我正在使用 sympy 进行一些符号数学运算,然后使用 eval 和 sympy 的 lambdastr 实用程序生成 Python lambda 函数。这是我的意思的一个简化示例: import s
我需要使用 numexpr 重写这段代码,它正在计算矩阵数据 [行 x 列] 和向量 [1 x 列] 的欧几里德范数矩阵。 d = ((data-vec)**2).sum(axis=1) 如何实现?或
我正在尝试在 numexpr 表达式中使用对象属性。最明显的做法是: import numpy as np import numexpr as ne class MyClass: def __
如何使用 numexpr 有效地表达以下内容? z = min(x-y, 1.0) / (x+y) 这里,x 和y 是一些相同形状的大型 NumPy 数组。 换句话说,在除以 x+y 之前,我试图将
在 python numexpr 中将值分配给您正在操作的同一数组以避免创建临时数组是否安全? 来自 project homepage 上的内存使用说明看起来不错,但如果不深入研究源代码,这几乎不是一
在 Windows 10 Python 3.7.9 (IDLE) 上,我成功安装了“pip install numexpr”,但在“import numexpr as ne”时出现错误: 回溯(最近调
我在 Windows 7 机器上通过 pip 安装 numexpr 包: pip list | grep numexpr numexpr (2.4.6) 但是当我打开 ipython 并尝试使用 df
考虑以下 dtype float32 的 NumPy 数组: In [29]: x = numpy.arange(10, dtype=numpy.float32) 当我使用pytables.Expr将
我刚刚开始使用 numexpr,虽然 github 存储库似乎有一些如何使用它的基本示例,但我无法清楚地理解它们如何应用于某些复杂的情况。假设我有一个函数: def func(x): #x is
我在安装 numexpr 以安装 PyTables 时遇到问题,因此我可以处理大量数据集。 构建细节:Windows 7 专业版 64 位、Python 2.7、numexpr 2.01、numpy
我一直在尝试学习 Numexpr包裹。关于如何使用它的例子充其量是稀疏的。有人可以给我一个关于如何使用“local_dict”和“global_dict”参数的简单示例吗? 最佳答案 下面的例子可能会
我对 NumPy 据说是对其算术数组操作进行矢量化的概念感到有点挣扎:它是否克服了 Python 的 GIL,因为 NumPy 的一部分是用 C 实现的?另外,Numexpr 是如何工作的呢?如果我理
我正在尝试使用 numexpr 加速 lambdify 生成的一些数字代码。不幸的是,基于numexpr 的函数在使用sqrt 函数时会中断,即使它是supported functions 之一。 .
由于 NumPy 不使用多核,我正在学习使用 NumExpr 加速 NumPy 代码,因为它对多线程有很好的支持。下面是我正在使用的示例: # input array to work with x =
测试代码: import numpy as np import pandas as pd COUNT = 1000000 df = pd.DataFrame({ 'y': np.random.
我正在尝试创建一个较小的,分层的样本,以减少处理时间。 运行此代码: df_strat= stratified_sample(df, ["Parental Status","Gender", "Age
我是一名优秀的程序员,十分优秀!