簡體   English   中英

Pandas 高級問題:對於每一行,從另一個 dataframe 獲取復雜信息

[英]Pandas advanced problem : For each row, get complex info from another dataframe

問題

我有一個 dataframe df

Index  Client_ID   Date   
1      johndoe     2019-01-15
2      johndoe     2015-11-25
3      pauldoe     2015-05-26

我還有另一個 dataframe df_prod ,產品如下:

Index   Product-Type   Product-Date   Buyer     Price
1       A              2020-01-01     pauldoe   300
2       A              2018-01-01     pauldoe   200
3       A              2019-01-01     johndoe   600
4       A              2017-01-01     johndoe   800
5       A              2020-11-05     johndoe   100
6       B              2014-12-12     johndoe   200
7       B              2016-11-15     johndoe   300

我想要向df添加一列,它將匯總當前日期已知的每種類型的最后產品的價格(使用Product-Date <= df.Date )。 一個例子是最好的解釋方式:

對於df的第一行

1      johndoe     2019-01-01

johndoe目前已知的最后一款 A 產品是:

3       A              2019-01-01     johndoe   600

(因為第 4 個較舊,第 5 個有Product-Date > Datejohndoe在該日期購買的最后一個 B 產品是這個:

7       B              2016-11-15     johndoe   300

因此df中的行在轉換后將如下所示( 900600 + 300 ,兩種感興趣產品的價格):

1      johndoe     2019-01-15   900

轉換后的完整df將是:

Index  Client_ID   Date         LastProdSum
1      johndoe     2019-15-01   900
2      johndoe     2015-11-25   200
3      pauldoe     2015-05-26   0

如您所見,有多種可能性:

  • 買家不必購買所有產品(參見pauldoe ,他只購買了 A 產品)
  • 有時,在df.Date不知道任何產品(參見新df的第 3 行,在 2015 年,我們不知道pauldoe購買的任何產品)
  • 有時,在df.Date只知道一個產品,並且值是該產品的一個(參見新df的第 3 行,在 2015 年,我們只有一個產品johndoe ,這是 2014 年購買的 B 產品, 價格為200 )

我做了什么:

我找到了解決這個問題的方法,但是使用時間太長了,因為我的 dataframe 很大。

為此,我在df的行上使用 iterrows 進行迭代,然后 select 鏈接到買方的產品,在df_prod上有Product-Date < Date ,然后按Product-Type獲取較舊的分組並獲取最大日期,然后我最后求和我所有的產品價格。 我解決了在每一行上迭代的問題(使用 for iterrows),為df的每一行提取我工作的df_prod的一部分以最終得到我的總和,這使得它真的很長。 我幾乎可以肯定有更好的方法來解決這個問題,使用 pandas 函數(例如pivot ),但我找不到方法。 我一直在尋找很多。

在此先感謝您的幫助

在 Dani 的回答后編輯

非常感謝您的回答。 看起來真的很好,我接受了,因為你花了很多時間。 執行仍然很長,因為我沒有指定一些東西。 事實上, Product-Types並不是通過買家共享的:每個買家都有自己的多個產品類型。 看到這個的真實方法是這樣的:

Index   Product-Type   Product-Date   Buyer     Price
1       pauldoe-ID1    2020-01-01     pauldoe   300
2       pauldoe-ID1    2018-01-01     pauldoe   200
3       johndoe-ID2    2019-01-01     johndoe   600
4       johndoe-ID2    2017-01-01     johndoe   800
5       johndoe-ID2    2020-11-05     johndoe   100
6       johndoe-ID3    2014-12-12     johndoe   200
7       johndoe-ID3    2016-11-15     johndoe   300

正如你所理解的,產品類型不會通過不同的買家共享(事實上,它可能會發生,但在非常罕見的情況下,我們不會在這里考慮)

問題仍然存在,因為您想對價格求和,您將添加 johndoe-ID2 和 johndoe-ID3 最后出現的價格以獲得相同的最終結果行

1      johndoe     2019-15-01   900

但是正如您現在所了解的那樣,實際上Product-TypesBuyers多,因此從您的答案中“獲取獨特的產品類型”的步驟在最初的問題上看起來很快,實際上需要很多時間。

很抱歉在這一點上不清楚,我沒有想到有可能根據產品類型創建一個新的 df 。

主要思想是使用merge_asof獲取每個Product-TypeClient_ID的最后日期,因此請執行以下操作:

# get unique product types
product_types = list(df_prod['Product-Type'].unique())

# create a new DataFrame with a row for each Product-Type for each Client_ID
df['Product-Type'] = [product_types for _ in range(len(df))]
df_with_prod = df.explode('Product-Type')

# merge only the closest date by each client and product type
merge = pd.merge_asof(df_with_prod.sort_values(['Date', 'Client_ID']),
                      df_prod.sort_values(['Product-Date', 'Buyer']),
                      left_on='Date',
                      right_on='Product-Date',
                      left_by=['Client_ID', 'Product-Type'], right_by=['Buyer', 'Product-Type'])

# fill na in prices
merge['Price'] = merge['Price'].fillna(0)

# sum Price by client and date
res = merge.groupby(['Client_ID', 'Date'], as_index=False)['Price'].sum().rename(columns={'Price' : 'LastProdSum'})
print(res)

Output

  Client_ID       Date  LastProdSum
0   johndoe 2015-11-25        200.0
1   johndoe 2019-01-15        900.0
2   pauldoe 2015-05-26          0.0

問題是merge_asof不能處理重復值,所以我們需要創建唯一值。 這些新值是Client_IDProduct-Type的笛卡爾積,這部分在:

# get unique product types
product_types = list(df_prod['Product-Type'].unique())

# create a new DataFrame with a row for each Product-Type for each Client_ID
df['Product-Type'] = [product_types for _ in range(len(df))]
df_with_prod = df.explode('Product-Type')

最后做一個 groupby 並對Price求和,而不是在做一個fillna來填充缺失值之前。

更新

你可以試試:

# get unique product types
product_types = df_prod.groupby('Buyer')['Product-Type'].apply(lambda x: list(set(x)))

# create a new DataFrame with a row for each Product-Type for each Client_ID
df['Product-Type'] = df['Client_ID'].map(product_types)
df_with_prod = df.explode('Product-Type')

# merge only the closest date by each client and product type
merge = pd.merge_asof(df_with_prod.sort_values(['Date', 'Client_ID']),
                      df_prod.sort_values(['Product-Date', 'Buyer']),
                      left_on='Date',
                      right_on='Product-Date',
                      left_by=['Client_ID', 'Product-Type'], right_by=['Buyer', 'Product-Type'])

# fill na in prices
merge['Price'] = merge['Price'].fillna(0)

# sum Price by client and date
res = merge.groupby(['Client_ID', 'Date'], as_index=False)['Price'].sum().rename(columns={'Price' : 'LastProdSum'})

print(res)

這里的想法是改變生成唯一值的方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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