Python 从pandas中的公式动态计算表达式?
我想使用Python 从pandas中的公式动态计算表达式?,python,pandas,dataframe,formula,eval,Python,Pandas,Dataframe,Formula,Eval,我想使用pd.eval对一个或多个数据帧列执行算术。具体而言,我想移植以下代码来计算公式: x = 5 df2['D'] = df1['A'] + (df1['B'] * x) …使用pd.eval进行编码。使用pd.eval的原因是我想自动化许多工作流,因此动态创建它们对我很有用 我的两个输入数据帧是: import pandas as pd import numpy as np np.random.seed(0) df1 = pd.DataFrame(np.random.choice(
pd.eval
对一个或多个数据帧列执行算术。具体而言,我想移植以下代码来计算公式:
x = 5
df2['D'] = df1['A'] + (df1['B'] * x)
…使用pd.eval
进行编码。使用pd.eval
的原因是我想自动化许多工作流,因此动态创建它们对我很有用
我的两个输入数据帧是:
import pandas as pd
import numpy as np
np.random.seed(0)
df1 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df1
A B C D
0 5 0 3 3
1 7 9 3 5
2 2 4 7 6
3 8 8 1 6
4 7 7 8 1
df2
A B C D
0 5 9 8 9
1 4 3 0 3
2 5 0 2 3
3 8 1 3 3
4 3 7 0 1
我试图更好地理解pd.eval
的引擎
和解析器
参数,以确定如何最好地解决我的问题。我已经经历了这一过程,但我没有弄清楚其中的区别
df2
x
作为参数1) 这是熊猫文档应该包含的“缺失手册”。 注意:在讨论的三个功能中,
pd.eval
是最重要的df.eval
和df.query
调用
pd.eval
发动机罩下。行为和用法或多或少是不同的
在三个功能中保持一致,但有一些次要的语义
稍后将突出显示的变体。本节将
介绍所有三个函数中通用的功能—包括(但不限于)允许的语法、优先规则和关键字参数
pd.eval
可以计算由变量和/或文字组成的算术表达式。这些表达式必须作为字符串传递。因此,要回答上述问题,您可以
x = 5
pd.eval("df1.A + (df1.B * x)")
这里需要注意的是:
df1
、df2
和x
引用全局命名空间中的变量,这些变量在解析表达式时由eval
拾取“df1['A']+(df1['B']*x)”
达到相同的效果target=…
属性的部分中讨论重新分配的具体问题。但就目前而言,以下是使用pd.eval
进行有效操作的更简单示例:
pd.eval("df1.A + df2.A") # Valid, returns a pd.Series object
pd.eval("abs(df1) ** .5") # Valid, returns a pd.DataFrame object
pd.eval("[1, 2, 3]")
array([1, 2, 3], dtype=object)
……等等。条件表达式也以同样的方式得到支持。下面的语句都是有效的表达式,将由引擎计算
pd.eval("df1 > df2")
pd.eval("df1 > 5")
pd.eval("df1 < df2 and df3 < df4")
pd.eval("df1 in [1, 2, 3]")
pd.eval("1 < 2 < 3")
1a)解析器选择:Parser=…
参数
pd.eval
在解析表达式字符串以生成语法树时支持两种不同的解析器选项:pandas
和python
。两者之间的主要区别在于优先级规则略有不同
使用默认解析器pandas,重载的位运算符&
和|
将实现对pandas对象的矢量化and和OR操作,它们将具有与和和或
相同的运算符优先级。所以
pd.eval("(df1 > df2) & (df3 < df4)")
这两种解析器之间的另一个区别是==
和的语义=使用'pandas'
解析器时,带有列表和元组节点的code>运算符,其语义分别与中的和中的相似。比如说,
pd.eval("df1 == [1, 2, 3]")
是有效的,并将使用与相同的语义运行
pd.eval("df1 in [1, 2, 3]")
OTOH,pd.eval(“df1==[1,2,3]”,parser='python')
将抛出一个NotImplementedError
错误
1b)后端选择:引擎=…
参数
有两个选项-numexpr
(默认值)和python
。numexpr
选项使用后端,该后端针对性能进行了优化
使用'python'
后端,对表达式的求值类似于将表达式传递给python的eval
函数。您可以灵活地执行更多的内部表达式,例如字符串操作
df = pd.DataFrame({'A': ['abc', 'def', 'abacus']})
pd.eval('df.A.str.contains("ab")', engine='python')
0 True
1 False
2 True
Name: A, dtype: bool
不幸的是,与numexpr
引擎相比,此方法没有提供任何性能优势,而且几乎没有安全措施可确保不评估危险的表达式,因此使用风险自负!除非您知道自己在做什么,否则通常不建议将此选项更改为'python'
1c)local_dict
和global_dict
参数
有时,为表达式中使用但当前未在命名空间中定义的变量提供值很有用。您可以将词典传递给local\u dict
例如:
pd.eval("df1 > thresh")
UndefinedVariableError: name 'thresh' is not defined
pd.eval('df1.A + df2.A')
0 10
1 11
2 7
3 16
4 10
dtype: int32
此操作失败,因为未定义阈值。然而,这是可行的:
pd.eval("df1 > thresh", local_dict={'thresh': 10})
当您需要从字典中提供变量时,这非常有用。或者,使用'python'
引擎,您可以简单地执行以下操作:
mydict = {'thresh': 5}
# Dictionary values with *string* keys cannot be accessed without
# using the 'python' engine.
pd.eval('df1 > mydict["thresh"]', engine='python')
但这可能比使用'numexpr'
引擎并将字典传递到本地dict
或全局dict
要慢得多。希望这能为使用这些参数提供令人信服的理由
1d)target
(+inplace
)参数和赋值表达式
这通常不是一个要求,因为通常有更简单的方法,但是您可以将pd.eval
的结果分配给实现\uuuu getitem\uuuuu
的对象,例如dict
s和(您猜到的)数据帧
考虑问题中的例子
要将列“D”分配给df2
,我们需要
pd.eval('D = df1.A + (df1.B * x)', target=df2)
A B C D
0 5 9 8 5
1 4 3 0 52
2 5 0 2 22
3 8 1 3 48
4 3 7 0 42
这不是就地修改
df = pd.DataFrame({'A': ['abc', 'def', 'abacus']})
pd.eval('df.A.str.contains("ab")', engine='python')
0 True
1 False
2 True
Name: A, dtype: bool
pd.eval("df1 > thresh")
UndefinedVariableError: name 'thresh' is not defined
pd.eval("df1 > thresh", local_dict={'thresh': 10})
mydict = {'thresh': 5}
# Dictionary values with *string* keys cannot be accessed without
# using the 'python' engine.
pd.eval('df1 > mydict["thresh"]', engine='python')
x = 5
df2['D'] = df1['A'] + (df1['B'] * x)
pd.eval('D = df1.A + (df1.B * x)', target=df2)
A B C D
0 5 9 8 5
1 4 3 0 52
2 5 0 2 22
3 8 1 3 48
4 3 7 0 42
pd.eval('df1.A + df2.A')
0 10
1 11
2 7
3 16
4 10
dtype: int32
df = pd.DataFrame(columns=list('FBGH'), index=df1.index)
df
F B G H
0 NaN NaN NaN NaN
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN NaN NaN NaN
4 NaN NaN NaN NaN
df = pd.eval('B = df1.A + df2.A', target=df)
# Similar to
# df = df.assign(B=pd.eval('df1.A + df2.A'))
df
F B G H
0 NaN 10 NaN NaN
1 NaN 11 NaN NaN
2 NaN 7 NaN NaN
3 NaN 16 NaN NaN
4 NaN 10 NaN NaN
pd.eval('B = df1.A + df2.A', target=df, inplace=True)
# Similar to
# df['B'] = pd.eval('df1.A + df2.A')
df
F B G H
0 NaN 10 NaN NaN
1 NaN 11 NaN NaN
2 NaN 7 NaN NaN
3 NaN 16 NaN NaN
4 NaN 10 NaN NaN
df = df.eval("B = @df1.A + @df2.A")
# df.eval("B = @df1.A + @df2.A", inplace=True)
df
F B G H
0 NaN 10 NaN NaN
1 NaN 11 NaN NaN
2 NaN 7 NaN NaN
3 NaN 16 NaN NaN
4 NaN 10 NaN NaN
pd.eval("[1, 2, 3]")
array([1, 2, 3], dtype=object)
pd.eval("[[1, 2, 3], [4, 5], [10]]", engine='python')
[[1, 2, 3], [4, 5], [10]]
pd.eval(["[1, 2, 3]", "[4, 5]", "[10]"], engine='python')
[[1, 2, 3], [4, 5], [10]]
pd.eval(["[1]"] * 100, engine='python') # Works
pd.eval(["[1]"] * 101, engine='python')
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
def eval(self, expr, inplace=False, **kwargs):
from pandas.core.computation.eval import eval as _eval
inplace = validate_bool_kwarg(inplace, 'inplace')
resolvers = kwargs.pop('resolvers', None)
kwargs['level'] = kwargs.pop('level', 0) + 1
if resolvers is None:
index_resolvers = self._get_index_resolvers()
resolvers = dict(self.iteritems()), index_resolvers
if 'target' not in kwargs:
kwargs['target'] = self
kwargs['resolvers'] = kwargs.get('resolvers', ()) + tuple(resolvers)
return _eval(expr, inplace=inplace, **kwargs)
pd.eval("df1.A + df1.B")
df1.eval("A + B")
df1.eval("A + index")
A = 5
df1.eval("A > @A")
df1.eval("""
E = A + B
F = @df2.A + @df2.B
G = E >= F
""")
A B C D E F G
0 5 0 3 3 5 14 False
1 7 9 3 5 16 7 True
2 2 4 7 6 6 5 True
3 8 8 1 6 16 9 True
4 7 7 8 1 14 10 True
df1.A
0 5
1 7
2 2
3 8
4 7
Name: A, dtype: int32
df1.B
0 9
1 3
2 0
3 1
4 7
Name: B, dtype: int32
m = df1.eval("A >= B")
m
0 True
1 False
2 False
3 True
4 True
dtype: bool
df1[m]
# df1.loc[m]
A B C D
0 5 0 3 3
3 8 8 1 6
4 7 7 8 1
df1.query("A >= B")
A B C D
0 5 0 3 3
3 8 8 1 6
4 7 7 8 1
df1_big = pd.concat([df1] * 100000, ignore_index=True)
%timeit df1_big[df1_big.eval("A >= B")]
%timeit df1_big.query("A >= B")
14.7 ms ± 33.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
14.7 ms ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
df1.query("index")
# Same as df1.loc[df1.index] # Pointless,... I know
A B C D
0 5 0 3 3
1 7 9 3 5
2 2 4 7 6
3 8 8 1 6
4 7 7 8 1
births['decade'] = 10 * (births['year'] // 10)
births.eval("decade = 10 * (year // 10)")
births.eval("decade = 10 * (year // 10)", engine= "python")