簡體   English   中英

從 groupby 中每個組的上一列獲取值

[英]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

如圖所示,按SiteProduct分組時有三組。 對於三組中的每一個,我都想做以下事情(第 2 到第 5 階段) -

  • New Opening Inventory設置為上期Closing Inventory
  • 使用公式計算下一個期間的期末Closing InventoryClosing Inventory = Production + Inflow + New Opening Inventory - Outflow

我正在嘗試使用groupbyfor 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM