Python 提高应用方法的性能

Python 提高应用方法的性能,python,pandas,performance,apply,pandas-groupby,Python,Pandas,Performance,Apply,Pandas Groupby,我想通过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

我想通过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”提供的代码:


编辑181017:由于pandas在稀疏时间序列上具有前向滚动功能,此方法不起作用,请参见注释。

在执行操作时,使用for循环可能是性能杀手

行周围的for循环加上7天的时间增量可以替换为
.rolling(“7D”)
。为了获得向前滚动的时间增量(当前日期+7天),我们按日期反转
df
,如图所示

这样就不再需要自定义函数,您可以从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

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

这个答案试图通过重新采样数据、填写所有天数,然后将定量和与原始数据合并,来解决这两个问题

# 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()

@jpp我添加了一个解决方案,它要求索引设置为
“RollingGroupby”对象没有属性“transform”
。你能想出一个更好的方法来做这件事吗?不,我试图使用
GroupBy
+
transform
滚动
,但失败了。仅供参考,由于某种原因,我认为您的结果与OP不匹配。@w-m它似乎不起作用;看一看“最终df”:它应该提前计算未来7天的总和。因此,对于cod_id 613,总和应该是20日的2和21日的121th@w-m、 这似乎奏效了。这种情况经常发生,我希望熊猫只允许负面窗口!经过调查,这实际上是行不通的<代码>在反转序列(向后日期)上滚动(“7D”)不受pandas-支持。如果不使用
groupby
,pandas会抛出一个
ValueError:dt_op必须是单调的
groupby
似乎隐藏了这个错误,熊猫计算出了一些错误(我认为这是一个bug)。cc@jpp非常感谢你;爱丽丝
# 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()