如何基于Customer ID和30天时间范围关联DataFrame并标记交易是否存在
问题描述
我有两个DataFrame:
email_date_df
email_id |customer_id| email_date | email_opened 001 | 1000 | 03-02-21 | 1 002 | 1001 | 03-22-21 | 0 003 | 1002 | 04-02-21 | 1 004 | 1003 | 05-02-21 | 1
transaction_df
trans_id |customer_id| trans_date | amount 001 | 1000 | 03-04-21 | $10 002 | 1001 | 04-30-21 | $24 003 | 1001 | 05-02-21 | $14 004 | 1003 | 04-10-21 | $149
我需要给每一封发送给客户的邮件,判断该客户在邮件日期后30天内是否产生了交易。之前单纯按customer_id合并两个DataFrame,导致出现大量重复行和冗余数据。有没有办法针对email_date_df的每一行,在transaction_df里查找是否存在符合30天时间范围的交易?
期望输出如下:
email_id |customer_id| email_date | email_opened | transaction_witin_30_days 001 | 1000 | 03-02-21 | 1 | 1 002 | 1001 | 03-22-21 | 0 | 0 003 | 1002 | 04-02-21 | 1 | 0 004 | 1003 | 05-02-21 | 1 | 0
解决方案
可以通过Pandas的日期转换+分组聚合或条件合并+去重聚合实现,避免全量合并带来的冗余,以下是两种可行方案:
方案一:分组聚合匹配(适合中小数据量)
先把每个客户的交易日期整理成列表,再逐个检查邮件日期的30天窗口内是否有匹配交易:
import pandas as pd # 1. 将日期字符串转为datetime类型,方便时间计算 email_date_df['email_date'] = pd.to_datetime(email_date_df['email_date'], format='%m-%d-%y') transaction_df['trans_date'] = pd.to_datetime(transaction_df['trans_date'], format='%m-%d-%y') # 2. 按客户分组,整理每个客户的所有交易日期为列表 customer_trans = transaction_df.groupby('customer_id')['trans_date'].apply(list).reset_index(name='trans_dates') # 3. 左连接邮件数据和客户交易日期,保留所有邮件记录 email_with_trans = email_date_df.merge(customer_trans, on='customer_id', how='left') # 4. 定义函数判断当前邮件对应的30天窗口内是否有交易 def check_trans(row): if pd.isna(row['trans_dates']): return 0 cutoff = row['email_date'] + pd.Timedelta(days=30) return 1 if any(row['email_date'] <= dt < cutoff for dt in row['trans_dates']) else 0 # 5. 生成目标列并更新原DataFrame email_date_df['transaction_witin_30_days'] = email_with_trans.apply(check_trans, axis=1) print(email_date_df)
方案二:条件合并去重(适合大数据量)
先合并符合客户+时间条件的记录,再按邮件ID聚合去重,效率更高:
import pandas as pd # 1. 转换日期格式 email_date_df['email_date'] = pd.to_datetime(email_date_df['email_date'], format='%m-%d-%y') transaction_df['trans_date'] = pd.to_datetime(transaction_df['trans_date'], format='%m-%d-%y') # 2. 按客户ID合并,筛选交易日期在邮件日期30天内的记录 matched_records = pd.merge(email_date_df, transaction_df, on='customer_id') matched_records['within_30'] = (matched_records['trans_date'] - matched_records['email_date']).dt.days.between(0, 30) # 3. 按邮件ID聚合,判断是否存在符合条件的交易 trans_flag = matched_records.groupby('email_id')['within_30'].any().astype(int).reset_index() # 4. 合并回原邮件DataFrame,无匹配的记录填充0 email_date_df = email_date_df.merge(trans_flag, on='email_id', how='left').fillna(0) email_date_df.rename(columns={'within_30': 'transaction_witin_30_days'}, inplace=True) print(email_date_df)
内容的提问来源于stack exchange,提问作者dsexplorer
相关产品推荐
相关产品推荐

