基于Pandas动态日期筛选,为proj_df计算3个交易统计列
高效实现多维度日期筛选交易统计的解决方案
针对10万行交易数据+1万行项目数据的场景,我们可以通过预计算累积交易数+有序匹配的方式高效完成需求,避免低效的循环或全量合并,具体步骤如下:
核心思路
先对交易数据按不同维度分组排序,计算每个时间点的累积交易总数;再用merge_asof(有序合并)匹配项目数据的last_purchase_date,快速获取该日期前的统计值。这种方法的时间复杂度为O(n log n),适合大规模数据。
假设数据结构
- transaction_df:包含
customer_id(客户ID)、part_id(零件ID)、partner_id(合作伙伴ID)、transaction_date(交易日期) - proj_df:包含
customer_id、part_id、partner_id、last_purchase_date(项目截止日期)
具体实现步骤
1. 预计算各维度的累积交易数
对交易数据按不同维度分组、排序后,用cumcount()生成累积交易数(代表截至当前交易日期的总交易次数):
import pandas as pd # 确保日期列是datetime类型 transaction_df['transaction_date'] = pd.to_datetime(transaction_df['transaction_date']) proj_df['last_purchase_date'] = pd.to_datetime(proj_df['last_purchase_date']) # 客户维度:按客户分组,按交易日期排序,计算累积交易数 cust_trans_df = transaction_df.sort_values(['customer_id', 'transaction_date']).copy() cust_trans_df['cust_total'] = cust_trans_df.groupby('customer_id').cumcount() + 1 # 客户+零件维度:同理计算累积交易数 cust_part_trans_df = transaction_df.sort_values(['customer_id', 'part_id', 'transaction_date']).copy() cust_part_trans_df['cust_part_total'] = cust_part_trans_df.groupby(['customer_id', 'part_id']).cumcount() + 1 # 客户+零件+合作伙伴维度:计算累积交易数 cust_part_partner_trans_df = transaction_df.sort_values(['customer_id', 'part_id', 'partner_id', 'transaction_date']).copy() cust_part_partner_trans_df['cust_part_partner_total'] = cust_part_partner_trans_df.groupby(['customer_id', 'part_id', 'partner_id']).cumcount() + 1
2. 用merge_asof匹配项目数据的截止日期
merge_asof会按指定键匹配,并找到小于等于目标日期的最近一条记录,正好对应“截止日期前的总交易数”:
# 匹配客户维度统计结果 proj_sorted = proj_df.sort_values(['customer_id', 'last_purchase_date']) cust_trans_sorted = cust_trans_df.sort_values(['customer_id', 'transaction_date']) proj_result = pd.merge_asof( proj_sorted, cust_trans_sorted[['customer_id', 'transaction_date', 'cust_total']], left_on='last_purchase_date', right_on='transaction_date', by='customer_id', direction='backward' # 取<=last_purchase_date的最大日期对应的统计值 ) # 无交易的客户补0 proj_result['cust_total_trans'] = proj_result['cust_total'].fillna(0).astype(int) # 匹配客户+零件维度统计结果 proj_sorted_cust_part = proj_result.sort_values(['customer_id', 'part_id', 'last_purchase_date']) cust_part_trans_sorted = cust_part_trans_df.sort_values(['customer_id', 'part_id', 'transaction_date']) proj_result = pd.merge_asof( proj_sorted_cust_part, cust_part_trans_df[['customer_id', 'part_id', 'transaction_date', 'cust_part_total']], left_on='last_purchase_date', right_on='transaction_date', by=['customer_id', 'part_id'], direction='backward' ) proj_result['cust_part_total_trans'] = proj_result['cust_part_total'].fillna(0).astype(int) # 匹配客户+零件+合作伙伴维度统计结果 proj_sorted_all = proj_result.sort_values(['customer_id', 'part_id', 'partner_id', 'last_purchase_date']) cust_part_partner_trans_sorted = cust_part_partner_trans_df.sort_values(['customer_id', 'part_id', 'partner_id', 'transaction_date']) proj_result = pd.merge_asof( proj_sorted_all, cust_part_partner_trans_df[['customer_id', 'part_id', 'partner_id', 'transaction_date', 'cust_part_partner_total']], left_on='last_purchase_date', right_on='transaction_date', by=['customer_id', 'part_id', 'partner_id'], direction='backward' ) proj_result['cust_part_partner_total_trans'] = proj_result['cust_part_partner_total'].fillna(0).astype(int) # 清理临时列,得到最终结果 final_proj_df = proj_result.drop(['transaction_date_x', 'cust_total', 'transaction_date_y', 'cust_part_total', 'transaction_date', 'cust_part_partner_total'], axis=1)
关键优势
- 高效性:排序和有序合并的时间复杂度为O(n log n),远优于循环遍历或全量笛卡尔积,适合10万级数据
- 准确性:严格按日期筛选,确保统计的是
last_purchase_date之前的交易 - 内存友好:避免生成冗余的中间数据,仅保留必要的统计列
内容的提问来源于stack exchange,提问作者The Great
相关产品推荐
相关产品推荐

