我正在尝试评估数据帧列的值,以确定另一列的值。我通过成功使用if
语句和.apply()
函数来完成此操作。即
if Col x < 0.3:
return y
elif Col x > 0.6:
return z
等。问题是运行大量数据需要相当长的时间。相反,我试图使用以下逻辑来确定新的列值:
(x <0.3)* y +(x> 0.6)* z
因此Python评估TRUE / FALSE并应用正确的值。这似乎工作得更快,唯一的事情是Python说: &#34; UserWarning:在Python空间进行评估,因为&#39; *&#39;对于bool dtype,numexpr不支持operator,使用&#39;&amp;&#39;代替 不支持[op_str]))&#34;
这是一个问题吗?我应该使用&#34;&amp;&#34;?我觉得使用&#34;&amp;&#34;乘以时会不正确。
谢谢!
答案 0 :(得分:1)
根据我到目前为止所读到的,性能差距由pandas
选择的解析器后端发出。常规的python解析器作为后端,另外还有一个解析后端的pandas
文档说,如果在这里使用普通的旧python而不是pandas,就没有性能提升:Pandas eval Backends
然而,你显然在熊猫后端打了一个白点;即你形成了一个无法用pandas评估的表达式。结果是pandas回退到最初的python解析后端,如结果UserWarning中所述:
UserWarning:在Python空间中进行评估,因为对于bool dtype,numexpr不支持'*'运算符,请使用'&amp;'代替 不支持[op_str]))
因此,正如我们现在知道不同的解析后端,是时候检查pandas
提供的适合您所需数据帧操作的几个选项(下面的完整脚本):
expr_a = '''(a < 0.3) * 1 + (a > 0.6) * 3 + (a >= 0.3) * (a <= 0.6) * 2'''
pandas
后端python
后端pandas
df.apply()
df.applymap()
我的机器上一列中具有10,000,000个随机浮点值的数据帧的结果是:
(1) Eval (pd) 0.240498406269
(2) Eval (py) 0.197919774926
(3) Eval @ (pd) 0.200814546686
(4) Apply 3.242620778595
(5) ApplyMap 6.542354086152
(6) Direct 0.140075372736
解释性能差异的主要观点很可能如下:
apply()
和applymap()
)是(当然!)比使用C中完全实现的功能慢得多python
后端的后端选择和后退,因为pandas
不评估bool * int
。没什么新鲜的,嗯?
我们基本上只是证明了我们之前的直觉(即:熊猫为任务选择了正确的后端)。
因此,我认为忽略UserWarning是完全没问题,只要您知道基础 hows和为什么。
因此:继续前进并让pandas
使用所有实现中最快的实现,这与通常的C函数一样。
from __future__ import print_function
import sys
import random
import pandas as pd
import numpy as np
from timeit import default_timer as timer
def conditional_column(val):
if val < 0.3:
return 1
elif val > 0.6:
return 3
return 2
if __name__ == '__main__':
nr = 10000000
df = pd.DataFrame({
'a': [random.random() for _ in range(nr)]
})
print(nr, 'rows')
expr_a = '''(a < 0.3) * 1 + (a > 0.6) * 3 + (a >= 0.3) * (a <= 0.6) * 2'''
expr_b = '''(@df.a < 0.3) * 1 + (@df.a > 0.6) * 3 + (@df.a >= 0.3) * (@df.a <= 0.6) * 2'''
fmt = '{:16s} {:.12f}'
# Evaluate the string expression using pandas parser
t0 = timer()
b = df.eval(expr_a, parser='pandas')
print(fmt.format('(1) Eval (pd)', timer() - t0))
# Evaluate the string expression using python parser
t0 = timer()
c = df.eval(expr_a, parser='python')
print(fmt.format('(2) Eval (py)', timer() - t0))
# Evaluate the string expression using pandas parser with external variable access (@)
t0 = timer()
d = df.eval(expr_b, parser='pandas')
print(fmt.format('(3) Eval @ (pd)', timer() - t0))
# Use apply to map the if/else function to each row of the df
t0 = timer()
d = df['a'].apply(conditional_column)
print(fmt.format('(4) Apply', timer() - t0))
# Use element-wise apply (WARNING: requires a dataframe and walks ALL cols AND rows)
t0 = timer()
e = df.applymap(conditional_column)
print(fmt.format('(5) ApplyMap', timer() - t0))
# Directly access the pandas series objects returned by boolean expressions on columns
t0 = timer()
f = (df['a'] < 0.3) * 1 + (df['a'] > 0.6) * 3 + (df['a'] >= 0.3) * (df['a'] <= 0.6) * 2
print(fmt.format('(6) Direct', timer() - t0))