【问题标题】:Improve Performance of Apply Method提高应用方法的性能
【发布时间】:2019-03-20 17:55:38
【问题描述】:

我想按我的 df "cod_id" 的变量分组,然后应用这个函数:

[df.loc[df['dt_op'].between(d, d + pd.Timedelta(days = 7)), 'quantity'].sum() \
                        for d in df['dt_op']]

从此df移动:

print(df)
dt_op      quantity      cod_id
20/01/18      1            613
21/01/18      8            611
21/01/18      1            613 
...

到这个:

print(final_df)
n = 7

dt_op      quantity   product_code     Final_Quantity
20/01/18      1            613               2
21/01/18      8            611               8
25/01/18      1            613               1
...

我试过了:

def lookforward(x):
    L = [x.loc[x['dt_op'].between(row.dt_op, row.dt_op + pd.Timedelta(days=7)), \
         'quantity'].sum() for row in x.itertuples(index=False)]
    return pd.Series(L, index=x.index)

s = df.groupby('cod_id').apply(lookforward)
s.index = s.index.droplevel(0)

df['Final_Quantity'] = s

print(df)

       dt_op  quantity  cod_id  Final_Quantity
0 2018-01-20         1     613               2
1 2018-01-21         8     611               8
2 2018-01-21         1     613               1

但这不是一个有效的解决方案,因为它在计算上

我如何提高 其性能? 即使使用导致相同结果的新代码/新功能,我也会实现它。

编辑:

原始数据集的子集,只有一个产品(cod_id == 2),我尝试在“w-m”提供的代码上运行:

   print(df)

    cod_id  dt_op          quantita  final_sum
0        2 2017-01-03         1       54.0
1        2 2017-01-04         1       53.0
2        2 2017-01-13         1       52.0
3        2 2017-01-23         2       51.0
4        2 2017-01-26         1       49.0
5        2 2017-02-03         1       48.0
6        2 2017-02-27         1       47.0
7        2 2017-03-05         1       46.0
8        2 2017-03-15         1       45.0
9        2 2017-03-23         1       44.0
10       2 2017-03-27         2       43.0
11       2 2017-03-31         3       41.0
12       2 2017-04-04         1       38.0
13       2 2017-04-05         1       37.0
14       2 2017-04-15         2       36.0
15       2 2017-04-27         2       34.0
16       2 2017-04-30         1       32.0
17       2 2017-05-16         1       31.0
18       2 2017-05-18         1       30.0
19       2 2017-05-19         1       29.0
20       2 2017-06-03         1       28.0
21       2 2017-06-04         1       27.0
22       2 2017-06-07         1       26.0
23       2 2017-06-13         2       25.0
24       2 2017-06-14         1       23.0
25       2 2017-06-20         1       22.0
26       2 2017-06-22         2       21.0
27       2 2017-06-28         1       19.0
28       2 2017-06-30         1       18.0
29       2 2017-07-03         1       17.0
30       2 2017-07-06         2       16.0
31       2 2017-07-07         1       14.0
32       2 2017-07-13         1       13.0
33       2 2017-07-20         1       12.0
34       2 2017-07-28         1       11.0
35       2 2017-08-06         1       10.0
36       2 2017-08-07         1        9.0
37       2 2017-08-24         1        8.0
38       2 2017-09-06         1        7.0
39       2 2017-09-16         2        6.0
40       2 2017-09-20         1        4.0
41       2 2017-10-07         1        3.0
42       2 2017-11-04         1        2.0
43       2 2017-12-07         1        1.0

【问题讨论】:

    标签: python pandas performance apply pandas-groupby


    【解决方案1】:

    编辑 181017:由于 pandas 对稀疏时间序列 not currently being supported 的前向滚动函数,此方法不起作用,请参阅 cmets。

    在执行 pandas 操作时,使用 for 循环可能会成为性能杀手。

    可以用.rolling("7D") 替换行周围的for 循环加上7 天的时间增量。要获得前滚时间增量(当前日期 + 7 天),我们按日期反转 df,如 here 所示。

    那么就不需要自定义函数了,你可以从groupby中取.quantity.sum()

    quant_sum = df.sort_values("dt_op", ascending=False).groupby("cod_id") \
                  .rolling("7D", on="dt_op").quantity.sum()
    
    cod_id  dt_op     
    611     2018-01-21    8.0
    613     2018-01-21    1.0
            2018-01-20    2.0
    Name: quantity, dtype: float64
    
    result = df.set_index(["cod_id", "dt_op"])
    result["final_sum"] = quant_sum
    result.reset_index()
    
       cod_id      dt_op  quantity  final_sum
    0     613 2018-01-20         1        2.0
    1     611 2018-01-21         8        8.0
    2     613 2018-01-21         1        1.0
    

    【讨论】:

    • @jpp 我添加了一个解决方案,需要将索引设置为'RollingGroupby' object has no attribute 'transform'。你能想出更好的方法吗?
    • 不,我试图将GroupBy + transformrolling 一起使用,但失败了。仅供参考,由于某种原因,我认为您的结果现在与 OP 不匹配。
    • @w-m 它似乎不起作用;看看“final_df”:它应该在接下来的 7 天里提前求和。所以,对于cod_id 613,总和应该是20号2,21号1
    • @w-m,这似乎可行。经常发生,我希望 Pandas 只允许负窗口!
    • 经过调查 - 这实际上是行不通的。 pandas 不支持倒置系列(向后日期)上的 .rolling("7D") - github.com/pandas-dev/pandas/issues/6772。当没有groupby 完成时,pandas 会抛出一个ValueError: dt_op must be monotonicgroupby 似乎隐藏了这个错误,pandas 计算出了一些错误(我认为这是一个错误)。抄送@jpp
    【解决方案2】:

    由于 pandas 的两个缺点,实现问题的确切行为很困难:既没有实现 groupby/rolling/transform,也没有实现前瞻性的滚动稀疏日期(有关更多详细信息,请参阅其他答案)。

    此答案尝试通过重新采样数据、填写所有天数,然后将 quant_sums 与原始数据连接起来来解决这两个问题。

    # Create a temporary df with all in between days filled in with zeros
    filled = df.set_index("dt_op").groupby("cod_id") \
               .resample("D").asfreq().fillna(0) \
               .quantity.to_frame()
    
    # Reverse and sum
    filled["quant_sum"] = filled.reset_index().set_index("dt_op") \
                                .iloc[::-1] \
                                .groupby("cod_id") \
                                .rolling(7, min_periods=1) \
                                .quantity.sum().astype(int)
    
    # Join with original `df`, dropping the filled days
    result = df.set_index(["cod_id", "dt_op"]).join(filled.quant_sum).reset_index()
    

    【讨论】:

    • 非常感谢;亚历山德罗
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-19
    • 2019-06-14
    • 1970-01-01
    • 2018-11-06
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多