[英]Pandas Groupby: get value from previous element of a group based on value of another column
[英]Get value from previous column for each group in groupby
這是我的df
地點 | 產品 | 時期 | 流入 | 外流 | 生產 | 期初庫存 | 新的期初庫存 | 期末存貨 | 需要生產 |
---|---|---|---|---|---|---|---|---|---|
加利福尼亞 | 蘋果 | 1 | 0 | 3226 | 4300 | 1213 | 1213 | 0 | 0 |
加利福尼亞 | 蘋果 | 2 | 0 | 3279 | 3876 | 0 | 0 | 0 | 0 |
加利福尼亞 | 蘋果 | 3 | 0 | 4390 | 4530 | 0 | 0 | 0 | 0 |
加利福尼亞 | 蘋果 | 4 | 0 | 4281 | 3870 | 0 | 0 | 0 | 0 |
加利福尼亞 | 蘋果 | 5 | 0 | 4421 | 4393 | 0 | 0 | 0 | 0 |
加利福尼亞 | 橘子 | 1 | 0 | 505 | 400 | 0 | 0 | 0 | 0 |
加利福尼亞 | 橘子 | 2 | 0 | 278 | 505 | 0 | 0 | 0 | 0 |
加利福尼亞 | 橘子 | 3 | 0 | 167 | 278 | 0 | 0 | 0 | 0 |
加利福尼亞 | 橘子 | 4 | 0 | 124 | 167 | 0 | 0 | 0 | 0 |
加利福尼亞 | 橘子 | 5 | 0 | 106 | 124 | 0 | 0 | 0 | 0 |
蒙特利爾 | 楓糖漿 | 1 | 0 | 445 | 465 | 293 | 293 | 0 | 0 |
蒙特利爾 | 楓糖漿 | 2 | 0 | 82 | 398 | 0 | 0 | 0 | 0 |
蒙特利爾 | 楓糖漿 | 3 | 0 | 745 | 346 | 0 | 0 | 0 | 0 |
蒙特利爾 | 楓糖漿 | 4 | 0 | 241 | 363 | 0 | 0 | 0 | 0 |
蒙特利爾 | 楓糖漿 | 5 | 0 | 189 | 254 | 0 | 0 | 0 | 0 |
如圖所示,按Site
和Product
分組時有三組。 對於三組中的每一個,我都想做以下事情(第 2 到第 5 階段) -
New Opening Inventory
設置為上期Closing Inventory
Closing Inventory
, Closing Inventory
= Production
+ Inflow
+ New Opening Inventory
- Outflow
我正在嘗試使用groupby
和for loop
的組合來解決這個問題
這是我到目前為止所擁有的 -
如果df
是一個單獨的組,我可以簡單地做
# calculate closing inventory of period 1
df['Closing Inventory'] = np.where(df['PeriodNo']==1, <formula>, 0)
for i in range(1, len(df)):
df.loc[i, 'New Opening Inventory'] = df.loc[i-1, 'Closing Inventory']
df.loc[i, 'Closing Inventory'] = df.loc[i, 'Production'] + df.loc[i, 'Inflow'] + df.loc[i, 'New Opening Inventory'] - df.loc[i, 'Outflow']
當我嘗試將此for loop
嵌套在groups
的循環中時
# calculate closing inventory of period 1 for all groups
df['Closing Inventory'] = np.where(df['PeriodNo']==1, <formula>, 0)
g = df.groupby(['Site', 'Product']
alist = []
for k in g.groups.keys():
temp = g.get_group(k).reset_index(drop=True)
for i in range(1, len(temp)):
temp.loc[i, 'New Opening Inventory'] = temp.loc[i-1, 'Closing Inventory']
temp.loc[i, 'Closing Inventory'] = temp.loc[i, 'Production'] + temp.loc[i, 'Inflow'] + temp.loc[i, 'New Opening Inventory'] - temp.loc[i, 'Outflow']
alist.append(temp)
df2 = pd.concat(alist, ignore_index=True)
df2
該解決方案有效,但使用嵌套循環似乎非常低效。 有一個更好的方法嗎?
您的新期初庫存始終是以前的期末庫存。
所以我可以修改這個公式
期末庫存 = 生產 + 流入 + 新期初庫存 - 流出
至
期末庫存 = 生產 + 流入 + 以前的期末庫存 - 流出
對於第一行,您沒有期末庫存。 但是從第二行開始,您計算期末庫存並將期末庫存結轉到下一行。
在獲得期末庫存之前,首先使用列表推導計算“生產”+“流入”-“溢出”。 列表推導比 for 循環執行得更好。
df['Closing Inventory'] = [x + y - z if p > 1 else 0 for p, x, y, z in zip(df['Period'], df['Production'], df['Inflow'], df['Outflow'])]
# df[['Site', 'Product', 'Closing Inventory']]
# Site Product Closing Inventory
# 0 California Apples 0
# 1 California Apples 597
# 2 California Apples 140
# 3 California Apples -411
# 4 California Apples -28
# 5 California Oranges 0
# 6 California Oranges 227
# 7 California Oranges 111
# ...
然后,公式的cumsum
就是將之前計算的 Closing Inventory 相加,也就是說你可以對這個結果求和。
For row 1, Previous Closing (0) + calculated part (597) = 597
For row 2, Previous Closing (597) + calculated part (140) = 737
...
df['Closing Inventory'] = df.groupby(['Site', 'Product'])['Closing Inventory'].cumsum()
# df[['Site', 'Product', 'Closing Inventory']]
# Site Product Closing_Inventory
# 0 California Apples 0
# 1 California Apples 597
# 2 California Apples 737
# 3 California Apples 326
# 4 California Apples 298
# 5 California Oranges 0
# 6 California Oranges 227
# 7 California Oranges 338
# ...
同樣,新的期初庫存始終是之前的期末庫存,除非期間為 1。因此,首先,轉移期末庫存,然后在期為 1 時選擇新的期初庫存。
我使用combine_first
從新的期初或期末庫存中挑選價值。
df['New Opening Inventory'] = (df['New Opening Inventory'].replace(0, np.nan)
.combine_first(
df.groupby(['Site', 'Product'])['Closing Inventory']
.shift()
.fillna(0)
).astype(int))
結果
Site Product Period New Opening Inventory Closing Inventory
0 California Apples 1 1213 0
1 California Apples 2 0 597
2 California Apples 3 597 737
3 California Apples 4 737 326
4 California Apples 5 326 298
5 California Oranges 1 0 0
6 California Oranges 2 0 227
7 California Oranges 3 227 338
...
用我筆記本電腦上的樣本數據,
Original solution: 8.44 ms ± 280 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
This solution: 2.95 ms ± 23.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
我認為通過列表理解和向量化 function,這個解決方案可以執行得更快。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.