是否有一种性能numpy或pandas方法来创建自定义桶



在python中是否有一种高效的方法可以在不使用for循环的情况下创建以下输出
我有一个数据框架,包括多个项目、日期和数量,以及每个项目的特定交付周期。目标是创建一个数据框架,总结交付周期内的数量
在下面的示例中,第一个项目的交付周期为14天,因此在2020年2月10日,接下来14天的数量总和为6。在2020年2月11日,总数将是4,依此类推。问题是,每个项目都有自己的交付周期

我有大约12000件商品,需求超过一年,所以我想避免循环以提高效率。有人知道如何使用熊猫、numpy或其他东西来解决这个问题吗?

数据帧:

item   date        quantity   lead_time
1      10.02.2020  2          14
1      12.02.2020  1          14
1      14.02.2020  3          14
...
2      10.02.2020  2          20
2      20.02.2020  2          20
2      02.03.2020  2          20
...

预期输出:

item   date       quantity
1      10.02.2020   6
1      11.02.2020   4
1      12.02.2020   4
1      13.02.2020   3
1      14.02.2020   3
1      15.02.2020   0
...
2      10.02.2020   4
2      11.02.2020   2
2      12.02.2020   2
2      13.02.2020   4
2      14.02.2020   4
2      15.02.2020   4
...

这就是我使用循环来解决它的方法:
demand = pd.DataFrame({"item":["1","1","1","1","1"], "date":["2020-01-03", "2020-01-08", "2020-01-15", "2020-01-17", "2020-01-22"], "quantity":[1,1,1,1,1], "lead_time":[14,14,14,14,14]})
demand.date = pd.to_datetime(demand.date)
calendar = pd.DataFrame({"date":  pd.to_datetime(pd.date_range('2020-01-01', '2020-01-31', freq='d').strftime("%Y-%m-%d"))})
calendar.date = pd.to_datetime(calendar.date)
calendar = calendar.merge(demand, how='left')
calendar.lead_time = 14
calendar['cummulative_quantity'] = 0
calendar.quantity = calendar.quantity.fillna(0)
for i in range(len(calendar)):
lead_time = calendar.loc[i, "lead_time"]
calendar.loc[i, "cummulative_quantity"] = sum(calendar.loc[i:i+lead_time, "quantity"])

这里有一个建议,它不是完全无循环的,但可能对您有用。作为一个示例帧demand,我修改了您的第一个示例:

item       date  quantity  lead_time
0     1 2020-02-10         2         14
1     1 2020-02-12         1         14
2     1 2020-02-14         3         14
3     2 2020-02-10         2         20
4     2 2020-02-20         2         20
5     2 2020-03-02         2         20

这个

dfs = []
for key, group in demand.groupby(['item', 'lead_time']):
group = group.resample('D', on='date').first().fillna({'item': key[0], 'quantity': 0})
group.quantity = group.quantity[::-1].rolling(f'{key[1]}d').sum()[::-1]
dfs.append(group[['item', 'quantity']])
df = pd.concat(dfs, axis='index')

生成以下结果:

item  quantity
date                      
2020-02-10   1.0       6.0
2020-02-11   1.0       4.0
2020-02-12   1.0       4.0
2020-02-13   1.0       3.0
2020-02-14   1.0       3.0
2020-02-10   2.0       4.0
2020-02-11   2.0       2.0
2020-02-12   2.0       4.0
2020-02-13   2.0       4.0
2020-02-14   2.0       4.0
2020-02-15   2.0       4.0
2020-02-16   2.0       4.0
2020-02-17   2.0       4.0
2020-02-18   2.0       4.0
2020-02-19   2.0       4.0
2020-02-20   2.0       4.0
2020-02-21   2.0       2.0
2020-02-22   2.0       2.0
2020-02-23   2.0       2.0
2020-02-24   2.0       2.0
2020-02-25   2.0       2.0
2020-02-26   2.0       2.0
2020-02-27   2.0       2.0
2020-02-28   2.0       2.0
2020-02-29   2.0       2.0
2020-03-01   2.0       2.0
2020-03-02   2.0       2.0

resample('D')确实填补了缺失的天数。由于key[0]是组的项目值,因此列item用它填充。列quantity0填充插入的天数。

由于key[1]是该组的lead_time,所以rolling((f'{key[1]}d').sum()确实在lead_time上求和——许多天。由于领先的[::-1],求和是在正确的方向上进行的(然后再重新调整(。

您对item == 2的预期输出略有不同,但我无法解释?

我定义了源DataFrame(df(,其中date列为datetime,因此它包含:

item       date  quantity  lead_time
0     1 2020-02-10         2         14
1     1 2020-02-12         1         14
2     1 2020-02-14         3         14
3     2 2020-02-10         2         20
4     2 2020-02-20         2         20
5     2 2020-03-02         2         20

请注意,日期列的格式(打印时(与您的源示例(但它可能取决于当前的语言环境(。

首先定义2个功能:

  1. myCnt计算给定时间段内数量的总和:

    def myCnt(dd, grp, dltDays):
    return grp.loc[dd : dd + pd.Timedelta(dltDays, 'D')].sum()
    

    参数:

    • dd-开始日期
    • grp-源数量组,但索引设置为日期
    • dltDays-从dd算起的最后一天

    请注意,通过索引(使用loc(,因此它应该运行得很快。

  2. itemProc处理当前源行组的函数:

    def itemProc(grp):
    row0 = grp.iloc[0]
    d1 = row0.date
    d2 = grp.date.max() + pd.Timedelta('1D')
    dltDays = row0.lead_time
    grp2 = grp.set_index('date')
    cnt = pd.date_range(d1, d2).to_series().apply(myCnt,
    args=(grp2.quantity, dltDays))
    return pd.DataFrame({'item': row0['item'], 'date': cnt.index, 'quantity': cnt})
    

    参数是当前的一组行。

    由于您没有定义每个组的输出日期范围,我假设下面的";"边界";日期:

    • 开始-每组的开始日期
    • end-每组的最后一天+1天(对于所有进一步的,如果您想要日期,结果将包含0数量(

    但如果你想要日期的另一个边界,请根据你的愿望为d1d2

然后,要生成实际结果,运行单个指令就足够了,即将此功能应用于每组(按项目分组(:

result = df.groupby('item', as_index=False).apply(itemProc).reset_index(drop=True)

对于您的数据样本(标题为数据帧,而不是来自您的代码样本(,结果是:

item       date  quantity
0      1 2020-02-10         6
1      1 2020-02-11         4
2      1 2020-02-12         4
3      1 2020-02-13         3
4      1 2020-02-14         3
5      1 2020-02-15         0
6      2 2020-02-10         4
7      2 2020-02-11         4
8      2 2020-02-12         4
9      2 2020-02-13         4
10     2 2020-02-14         4
11     2 2020-02-15         4
12     2 2020-02-16         4
13     2 2020-02-17         4
14     2 2020-02-18         4
15     2 2020-02-19         4
16     2 2020-02-20         4
17     2 2020-02-21         2
18     2 2020-02-22         2
19     2 2020-02-23         2
20     2 2020-02-24         2
21     2 2020-02-25         2
22     2 2020-02-26         2
23     2 2020-02-27         2
24     2 2020-02-28         2
25     2 2020-02-29         2
26     2 2020-03-01         2
27     2 2020-03-02         2
28     2 2020-03-03         0

请注意,对于项==2的组,此结果与您的预期结果。

我的结果正确(不是你的(的理由:

  1. 对于2020-02-10,日期范围最长为22020-03-01(包括(。这一时期数量的总和为4(对于2020-02-1022020-02-20(。

  2. 对于22020-02-11,日期范围为2020-03-02。这一时期数量的总和为4(对于2020-020-02-2022020-03-02(。

  3. 等等。

显然,您首先为较小的lead_time创建了预期结果,然后在创建源数据样本时,您将其设置为20天。

最新更新