【问题标题】:Pandas transform method performing slow熊猫变换方法执行缓慢
【发布时间】:2020-12-03 22:51:00
【问题描述】:

我有一个规范的 Pandas transform 示例,其中性能似乎莫名其妙地慢。我已经阅读了Q&A on the apply method,这是相关的,但在我看来,它为我的问题提供了一个不完整且可能具有误导性的答案,如下所述。

我的数据框的前五行是

id 日期 xvar 0 1004 1992-05-31 4.151628 1 1004 1993-05-31 2.868015 2 1004 1994-05-31 3.043287 3 1004 1995-05-31 3.189541 4 1004 1996-05-31 4.008760
  • 数据框中有 24,693 行。
  • 有 2,992 个唯一的 id 值。

我想以id 居中xvar

方法 1 耗时 861 毫秒:

df_r['xvar_center'] = (
    df_r
    .groupby('id')['xvar']
    .transform(lambda x: x - x.mean())
)

方法 2 需要 9 毫秒:

# Group means
df_r_mean = (
    df_r
    .groupby('id', as_index=False)['xvar']
    .mean()
    .rename(columns={'xvar':'xvar_avg'})
)

# Merge group means onto dataframe and center
df_w = (
    pd
    .merge(df_r, df_r_mean, on='id', how='left')
    .assign(xvar_center=lambda x: x.xvar - x.xvar_avg)
)

Q&A on the apply method 建议尽可能依赖矢量化函数,就像@sammywemmy 的评论所暗示的那样。我认为这是重叠的。但是,Q&A on the apply method 也表示:

"...这里有一些常见的情况,您希望摆脱对apply...Numeric Data"的任何调用

@sammywemmy 的评论在他们对我的问题的回答中并没有“摆脱对transform 方法的任何调用”。相反,答案依赖于transform 方法。因此,除非@sammywemmy 的建议严格由不依赖transform 方法的替代方法主导,否则我认为我的问题及其答案与Q&A on the apply method 中的讨论完全不同。 (感谢您的耐心和帮助。)

【问题讨论】:

  • 避免匿名函数,使用df.xvar - df.groupby("id")["xvar"].transform("mean")
  • @sammywemmy。非常感谢。您的方法在 3 毫秒内执行,并且比我的任何方法都更具 Pythonic。这是我的第一个问题,所以如果我能做任何其他事情来感谢您的有用(和快速)回复,请告诉我。
  • @MRR,没关系。很高兴它对你有效。只需通过帮助别人来支付它。干杯
  • 我看到了我的问题和linked question 之间的联系,但我很惊讶它被认为是重复的。 @sammywemmy 的建议是否从另一个问题的答案中显而易见? (我不这么认为。)

标签: python pandas performance transform


【解决方案1】:

这个答案是由于@sammywemmy 的富有洞察力的评论,他应该得到所有的赞扬,并且没有任何不准确的责任。因为transform 的类似用法在Pandas User's Guide 中有说明,所以我认为详细说明可能对其他人有用。

我的假设是问题在于使用非向量化函数和大量组的组合。当我将groupby 变量从id(2,992 个唯一值)更改为year(由date 变量构造并包含28 个唯一值)时,我的原始方法和@sammywemmy 之间的性能差异大大缩小,但仍然很重要。

%%timeit
df_r['xvar_center_y'] = (
    df_r
    .groupby('year')['xvar']
    .transform(lambda x: x - x.mean())
)
11.4 ms ± 202 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

对比

%timeit df_r['xvar_center_y'] = df_r.xvar - df_r.groupby('year')['xvar'].transform('mean')
1.69 ms ± 5.11 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

@sammywemmy 洞察力的美妙之处在于,它很容易应用于其他常见的转换,以实现潜在的显着性能改进,并且在额外代码方面的成本适中。例如,考虑标准化一个变量:

%%timeit
df_r['xvar_z'] = (
    df_r
    .groupby('id')['xvar']
    .transform(lambda x: (x - x.mean()) / x.std())
)
1.34 s ± 38 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

对比

%%timeit 
df_r['xvar_z'] = (
    (df_r.xvar - df_r.groupby('id')['xvar'].transform('mean')) 
    / df_r.groupby('id')['xvar'].transform('std')
)
3.96 ms ± 297 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • 顺便说一句,事先将df_r.groupby('id')['xvar'] 保存到变量中会节省更多时间,因为分组不会进行两次。
猜你喜欢
  • 2017-06-18
  • 1970-01-01
  • 2015-07-05
  • 1970-01-01
  • 2022-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-19
相关资源
最近更新 更多