[英]Performance difference between applying lambda vs. named function to a pandas DataFrame
我开始确定 lambda 表达式在应用于 pandas DataFrame 时是否比命名函数慢或快,没想到会发现实质性差异。 令我惊讶的是,在以下示例中,lambda 方法的速度要慢得多。 这是否始终如一? 如果是这样,为什么?
import pandas as pd
import numpy as np
import cmath
df = pd.DataFrame(np.random.randint(1,100,(100000,3)), columns=['col1','col2', 'col3'])
#Named function approach:
def quad_roots(row):
'''Function that calculates roots of a quadratic equation'''
a = row['col1']; b = row['col2']; c = row['col3']
dis = (b ** 2) - (4 * a * c)
root1 = (-b - cmath.sqrt(dis)) / (2 * a)
root2 = (-b + cmath.sqrt(dis)) / (2 * a)
return np.round(root1,2), np.round(root2,2)
df['roots_named'] = df.apply(quad_roots, axis=1)
#Lambda approach
df['roots_lambda'] = df.apply(lambda x: ((np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) ),
(np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) ))
, axis=1)
一般来说,它们是等价的。 有人认为 lambda 稍微快一些。
在这两种情况下,这里的区别与 lambda 与声明的语句无关,而是代码编译脚本的操作顺序。 为了证明这一点,我们可以使用与 lambda 相同的语法声明 function 并比较结果。
%%timeit
df.apply(lambda x: ((np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) ),
(np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) ))
, axis=1)
返回
7.78 s ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
同时:
%%timeit
def consolidated_func(x):
return ((np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) ),
(np.round((-x['col2'] - cmath.sqrt((x['col2'] ** 2) - (4 * x['col1'] * x['col3']))) / (2 * x['col1']),2) )
)
df.apply(consolidated_func,axis=1)
返回
7.79 s ± 30.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
大致相同的性能。
代码的差异归结为 python 的编译方式。 在 df.apply 等非矢量化代码中, df.apply
必须单独编译每个操作。 在 lambda 示例中,我们强制 python 在np.round
和cmath.sqrt
操作之间切换。 切换会导致更长的执行时间。
为了真正改进,我们可以对大部分 function 进行矢量化。 (我没有对 cmath.sqrt 进行矢量化。可能会找到等效的 function,但我假设您使用它是有原因的,所以我不理会它。)
df['dis'] = (df['col2'] ** 2) - (4 * df['col1'] * df['col3'])
df['root1'] = np.round((-df['col2'] - df['dis'].apply(cmath.sqrt)) / (2 * df['col1']).values,2)
df['root2'] = np.round((-df['col2'] + df['dis'].apply(cmath.sqrt)) / (2 * df['col1']).values,2)
df['roots_vectorized'] = df[['root1','root2']].apply(tuple,axis=1) # There is probably a better way to do this.
产量:
792 ms ± 6.58 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
这将执行时间减少了 10 倍。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.