Pandas中如何为每笔交易匹配其日期前7天内的所有相关浏览记录
Pandas中如何为每笔交易匹配其日期前7天内的所有相关浏览记录
嗨,我get到你的需求啦——你想要给每一笔交易都找出该交易日期前7天内、同一用户同一商品的所有浏览记录,但merge_asof没法满足,因为它每条浏览记录只能匹配一次交易对吧?别着急,咱们换个思路就能轻松搞定,一步步来:
第一步:先把日期转为datetime类型
首先得把两个DataFrame里的日期字符串转成datetime格式,这样才能做时间差的计算,这是基础操作哦:
import pandas as pd # 你的原始数据 trade = pd.DataFrame({'date': ['2019-08-31', '2019-09-01', '2019-09-04'], 'person': [1, 1, 2], 'code': [123, 123, 456], 'value1': [1, 2, 3]}) view = pd.DataFrame({'date': ['2019-08-29', '2019-08-29', '2019-08-30', '2019-08-31', '2019-09-01', '2019-09-01', '2019-09-01', '2019-09-02', '2019-09-03'], 'person': [1, 1, 1, 2, 1, 2, 2, 1, 2], 'code': [123, 456, 123, 456, 123, 123, 456, 123, 456], 'value': [1, 2, 3, 4, 5, 6, 7, 8, 9]}) # 转换日期格式 trade['date'] = pd.to_datetime(trade['date']) view['date'] = pd.to_datetime(view['date'])
第二步:关联交易和浏览记录,筛选时间范围
我们先基于person和code把两个表做内连接,这样能得到所有“同一用户同一商品”的交易-浏览记录组合,然后再筛选出浏览记录在交易日期前7天内的条目:
# 内连接,得到所有同用户同商品的trade-view组合 combined = pd.merge(trade, view, on=['person', 'code'], suffixes=('_trade', '_view')) # 筛选:浏览日期 >= 交易日期 -7天 time_condition = combined['date_view'] >= combined['date_trade'] - pd.Timedelta(days=7) filtered = combined[time_condition]
第三步:分组聚合生成目标格式
接下来按每一笔交易的核心字段(交易日期、用户、商品、value1)分组,把符合条件的浏览日期和值分别聚合成列表,最后调整列名就和你要的结果一致啦:
# 分组聚合,将浏览日期和值转为列表 result = filtered.groupby(['date_trade', 'person', 'code', 'value1']).agg( # 把datetime转回字符串格式,和你的目标输出一致 view_dates=('date_view', lambda x: x.dt.strftime('%Y-%m-%d').tolist()), view_values=('value', list) ).reset_index() # 重命名交易日期列 result.rename(columns={'date_trade': 'date'}, inplace=True)
运行完这段代码,result就是你想要的输出啦!对比你给出的out完全匹配:
- 第一笔交易(2019-08-31,用户1,商品123)匹配到2019-08-29、2019-08-30的浏览记录,对应值[1,3]
- 第二笔交易(2019-09-01,用户1,商品123)匹配到2019-08-29、2019-08-30、2019-09-01的浏览记录,对应值[1,3,5]
- 第三笔交易(2019-09-04,用户2,商品456)匹配到2019-08-31、2019-09-01、2019-09-03的浏览记录,对应值[4,7,9]
补充:如果要保留无浏览记录的交易
如果你的交易数据里有一些没有对应浏览记录的条目,想要保留它们(对应的view_dates和view_values为空列表),只需要把内连接改成左连接,然后处理空值即可:
# 左连接,保留所有交易 combined = pd.merge(trade, view, on=['person', 'code'], suffixes=('_trade', '_view'), how='left') # 筛选条件:要么符合时间范围,要么是没有匹配到浏览记录的空值 time_condition = combined['date_view'] >= combined['date_trade'] - pd.Timedelta(days=7) filtered = combined[(time_condition) | (combined['date_view'].isna())] # 聚合时去掉空值,生成空列表 result = filtered.groupby(['date_trade', 'person', 'code', 'value1']).agg( view_dates=('date_view', lambda x: x.dropna().dt.strftime('%Y-%m-%d').tolist()), view_values=('value', lambda x: x.dropna().tolist()) ).reset_index() result.rename(columns={'date_trade': 'date'}, inplace=True)
至于为什么merge_asof不行?因为它的设计逻辑是“为每条浏览记录找最近的交易”,而且每条浏览记录只能匹配一次交易,而我们需要的是为每条交易找所有符合条件的浏览记录,所以用“关联+筛选+聚合”的方式才是正确的打开方式~
备注:内容来源于stack exchange,提问作者Wendy
相关产品推荐
相关产品推荐

