2017-06-20 67 views
3

我有一个大的pandas数据框df,仓库数据显示接收到的项目数量。pandas - 在新列中聚集列的一部分到新值

想象结构的相关部分为:

Date   SKU received 
2017-05-29 sku1 0 
2017-05-30 sku1 0 
2017-05-31 sku1 0 
2017-06-01 sku1 0 
2017-06-02 sku1 6 
2017-06-03 sku1 2 
2017-05-29 sku2 4 
2017-05-30 sku2 4 
2017-05-31 sku2 0 
2017-06-01 sku2 0 
2017-06-02 sku2 0 
2017-06-03 sku2 24 

在这里,我想重建秩序的过程。我知道,股票水平是基于股票水平在星期一进行审查,新订单被放置。订单大约在一周后到达仓库,有时会分成多个出口。

我想为平日(df["Weekday"])和下订单(df["Order"])创建一个额外的列。 根据工作日,我想汇总未来4至11天内“已收到”列的数据,仅限于相关SKU。

输出看起来是这样的:

Date   SKU received Weekday Order 
2017-05-29 sku1 0   0   8 
2017-05-30 sku1 0   1   0 
2017-05-31 sku1 0   2   0 
2017-06-01 sku1 0   3   0 
2017-06-02 sku1 6   4   0 
2017-06-03 sku1 2   5   0 
2017-05-29 sku2 4   0   24 
2017-05-30 sku2 4   1   0 
2017-05-31 sku2 0   2   0 
2017-06-01 sku2 0   3   0 
2017-06-02 sku2 0   4   0 
2017-06-03 sku2 24   5   0 

这里是我试过的代码:

import pandas as pd 

# 0 is Monday, 1 is Tuesday, etc 
df["Weekday"] = df["Date"].dt.dayofweek 

# create new column for the orders 
df["Order"] = 0 

min_days = 4 
max_days = min_days + 7 

for i in range(len(df)): 
    if df.loc[i, "Weekday"] == 0: 
     df.loc[i, "Order"] = df.loc[(df.Date >= df.loc[i, "Date"] + pd.to_timedelta(min_days, unit="D")) & 
            (df.Date < df.loc[i, "Date"] + pd.to_timedelta(max_days, unit="D")) & 
            (df.SKU == df.loc[i, "SKU"]), "received"].sum() 

这似乎做的工作,但在一个缓慢的方式。也许有人可以帮助我找到更多的pythonic/pandas方法来节省一些计算时间。

感谢您的帮助。

回答

1

这是一个可能的解决方案,它使用熊猫groupby和变换。

第一个想法是,您可以通过计算滚动和的差值来实现两天之间的计数。此外,请注意两次还原订单([::-1])的诀窍,以便将来有一个滚动拣选日。

def count_between(ts, min_days, max_days): 
    return ts[::-1].pipe(lambda y: y.rolling(max_days,1).sum() - y.rolling(min_days-1,1).sum())[::-1] 

这个功能会给你结果的每一天,让你结果限制为仅周一所有其他条目设置为0(使用[.where][1])。

设置Date为指标后,您可以执行以下操作:

order = df.groupby('SKU')\ 
      .transform(lambda x: count_between(x, min_days, max_days)\ 
           .where(lambda y: y.index.dayofweek==0, other = 0)) 
order.columns = ['Order'] 

这给了预期的结果:

pd.concat([df, order], axis = 1) 
Out[319]: 
      SKU received Order 
Date        
2017-05-29 sku1   0 8.0 
2017-05-30 sku1   0 0.0 
2017-05-31 sku1   0 0.0 
2017-06-01 sku1   0 0.0 
2017-06-02 sku1   6 0.0 
2017-06-03 sku1   2 0.0 
2017-05-29 sku2   4 24.0 
2017-05-30 sku2   4 0.0 
2017-05-31 sku2   0 0.0 
2017-06-01 sku2   0 0.0 
2017-06-02 sku2   0 0.0 
2017-06-03 sku2  24 0.0 
+0

感谢@FLab。我的'df'实际上有点大,有更多的列,'Date'不是索引,这就是为什么我运行代码'y.index.dayofweek == 0'遇到麻烦。 – Axel

+0

将代码调整为'count_between(x.received,min_days,max_days)'和'.where(lambda y:y.Date.dayofweek == 0')没有帮助。我是否缺少某些东西? – Axel

+0

哦,是的,应该有提到我设置日期作为索引,你不能操作数据框的一个子集,看起来像报告的例子吗?在此期间,我会想一个修复 – FLab