【问题标题】:Why is pandas apply lambda slower than loop here?为什么熊猫在这里应用 lambda 比循环慢?
【发布时间】:2018-05-24 17:53:20
【问题描述】:

我有一个 pandas 数据框,我想根据是否满足某些条件进行过滤。我运行了一个循环和一个.apply() 并使用%%timeit 来测试速度。数据集大约有 45000 行。 sn -p for 循环的代码是:

%%timeit
qualified_actions = []
for row in all_actions.index:
    if all_actions.ix[row,'Lower'] <= all_actions.ix[row, 'Mid'] <= all_actions.ix[row,'Upper']:
        qualified_actions.append(True)
    else:
        qualified_actions.append(False)

每个循环 1.44 秒 ± 3.7 毫秒(平均值 ± 标准偏差。7 次运行,每个循环 1 个)

对于.apply() 是:

%%timeit
qualified_actions = all_actions.apply(lambda row: row['Lower'] <= row['Mid'] <= row['Upper'], axis=1)

每个循环 6.71 秒 ± 54.6 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)

我认为.apply() 应该比在 pandas 中循环遍历要快得多。有人可以解释为什么在这种情况下它会变慢吗?

【问题讨论】:

  • 我不确定知道,但我这是因为apply必须为每个人构造一个dict排。同时,您的for 方法使用ix 有效地访问数据,而无需构造任何新对象。我相信这只会在您应用 Python 函数时发生;应用 numpy 函数,您将留在 C 领域,事情进展得很快。
  • .applynot 假设在遍历行时更快。 .apply 本质上是引擎盖下的一个 for 循环,如果你继续使用 axis=1。见here
  • @Amadan 实际上,它在每一行中构造了一个Series,但是是的,效果相同。

标签: python pandas performance


【解决方案1】:

apply 在引擎盖下使用循环,因此如果需要better performance,最好和最快的是 vecorized 替代方案。

无循环,仅链 2 个条件向量化解:

m1 = all_actions['Lower'] <= all_actions['Mid']
m2 = all_actions['Mid'] <= all_actions['Upper']
qualified_actions = m1 & m2

感谢Jon Clements 提供另一个解决方案:

all_actions.Mid.between(all_actions.Lower, all_actions.Upper)

时间安排

np.random.seed(2017)
N = 45000
all_actions=pd.DataFrame(np.random.randint(50, size=(N,3)),columns=['Lower','Mid','Upper'])

#print (all_actions)

In [85]: %%timeit
    ...: qualified_actions = []
    ...: for row in all_actions.index:
    ...:     if all_actions.ix[row,'Lower'] <= all_actions.ix[row, 'Mid'] <= all_actions.ix[row,'Upper']:
    ...:         qualified_actions.append(True)
    ...:     else:
    ...:         qualified_actions.append(False)
    ...: 
    ...: 
__main__:259: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
1 loop, best of 3: 579 ms per loop

In [86]: %%timeit
    ...: (all_actions.apply(lambda row: row['Lower'] <= row['Mid'] <= row['Upper'], axis=1))
    ...: 
1 loop, best of 3: 1.17 s per loop

In [87]: %%timeit
    ...: ((all_actions['Lower'] <= all_actions['Mid']) & (all_actions['Mid'] <= all_actions['Upper']))
    ...: 
1000 loops, best of 3: 509 µs per loop


In [90]: %%timeit
    ...: (all_actions.Mid.between(all_actions.Lower, all_actions.Upper))
    ...: 
1000 loops, best of 3: 520 µs per loop

【讨论】:

  • 或者只是all_actions.Mid.between(all_actions.Lower, all_actions.Upper)
  • @JonClements - 谢谢,我添加它来回答。在样本数据中它有点慢,但差异只有11us,所以它大致相同。
猜你喜欢
  • 2019-12-08
  • 2022-01-12
  • 1970-01-01
  • 2023-04-08
  • 2013-08-05
  • 2023-03-17
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多