在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
用它填充。列quantity
用0
填充插入的天数。
由于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个功能:
myCnt计算给定时间段内数量的总和:
def myCnt(dd, grp, dltDays): return grp.loc[dd : dd + pd.Timedelta(dltDays, 'D')].sum()
参数:
- dd-开始日期
- grp-源数量组,但索引设置为日期
- dltDays-从dd算起的最后一天
请注意,通过索引(使用loc(,因此它应该运行得很快。
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的数量(
但如果你想要日期的另一个边界,请根据你的愿望为d1和d2。
然后,要生成实际结果,运行单个指令就足够了,即将此功能应用于每组(按项目分组(:
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的组,此结果与您的预期结果。
我的结果正确(不是你的(的理由:
对于2020-02-10,日期范围最长为22020-03-01(包括(。这一时期数量的总和为4(对于2020-02-10和22020-02-20(。
对于22020-02-11,日期范围为2020-03-02。这一时期数量的总和为和4(对于2020-020-02-20和22020-03-02(。
等等。
显然,您首先为较小的lead_time创建了预期结果,然后在创建源数据样本时,您将其设置为20天。