高级问题:对于每一行,从另一个数据帧获取复杂信息

2024-10-02 20:44:13 发布

您现在位置:Python中文网/ 问答频道 /正文

问题

我有一个数据帧df

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

我还有另一个dataframedf_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

(因为第四个比较老,第五个有一个Product-Date>;Date) 目前已知的johndoe购买的最后一种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

如您所见,有多种可能性:

  • 买家不必购买所有产品(参见^{,他们只购买A产品)
  • 有时,在df.Date没有已知的产品(见新df的第3行,2015年,我们不知道pauldoe购买了任何产品)
  • 有时,在df.Date只知道一种产品,价值是该产品的价值(见新df的第3行,2015年,我们在johndoe只知道一种产品,这是2014年购买的B产品,价格为200

我所做的:

我找到了这个问题的解决方案,但由于我的数据帧太大,使用起来太费时了

为此,我在df行上使用iterrows进行迭代,然后选择链接到买家的产品,在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的可能性


Tags: 类型dfdateindex产品type时间价格
1条回答
网友
1楼 · 发布于 2024-10-02 20:44:13

主要思想是使用merge_asof获取每个产品类型客户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)

输出

  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\u asof无法处理重复的值,因此我们需要创建唯一的值。这些新值是客户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')

最后,做一个groupby并对价格求和,而不是在做填充以填充缺少的值之前

更新

你可以试试:

# 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)

这里的想法是改变生成唯一值的方式

相关问题 更多 >