如何用Pandas高效为交易数据添加前3个窗口的交易计数字段
Pandas高效实现银行卡交易窗口历史计数统计
问题背景
给定如下银行卡交易数据集:
data = { 'TRANSACTIONDATETIME': ['1-Jan-23', '2-Jan-23', '3-Jan-23', '4-Jan-23', '4-Jan-23', '5-Jan-23', '2-Jan-23', '3-Jan-23', '8-Jan-23', '9-Jan-23'], 'CARD_NUM': [123, 123, 123, 123, 123, 123, 234, 234, 234, 234], 'WINDOW': [1, 1, 2, 2, 2, 3, 1, 1, 4, 4], 'COUNT_CURRENT_WINDOW': [1, 2, 1, 2, 3, 1, 1, 2, 1, 2] }
其中WINDOW是单卡交易按48小时划分的分组,COUNT_CURRENT_WINDOW是当前窗口内的交易累计计数。
需求
在不使用循环(保证大数据集处理效率)的前提下,新增3个字段:
COUNT_PREV_WINDOW:当前窗口减1对应的窗口内该卡的总交易数COUNT_2WINDOWS_AGO:当前窗口减2对应的窗口内该卡的总交易数COUNT_3WINDOWS_AGO:当前窗口减3对应的窗口内该卡的总交易数
对应窗口无交易时填0,示例输出参考题目给出的结果。
原代码问题分析
原代码直接按CARD_NUM和WINDOW-1分组取最大值,无法正确关联到原数据的每条记录,且未处理窗口不存在时的补0逻辑,导致结果不符合预期。
正确实现方案
方案一:基于Merge的矢量化关联(推荐大数据集)
1. 生成单卡各窗口的总交易数映射表
先按CARD_NUM和WINDOW分组,取每个窗口COUNT_CURRENT_WINDOW的最大值(即该窗口的总交易数):
import pandas as pd df = pd.DataFrame(data) # 生成卡-窗口对应的总交易数表 window_totals = df.groupby(['CARD_NUM', 'WINDOW'])['COUNT_CURRENT_WINDOW'].max().reset_index() window_totals.rename(columns={'COUNT_CURRENT_WINDOW': 'TOTAL_TRANS'}, inplace=True)
2. 关联历史窗口数据
通过多次左连接,分别匹配当前窗口减1、减2、减3的窗口数据,缺失值填充为0:
# 匹配前1个窗口 df = df.merge( window_totals.assign(WINDOW=window_totals['WINDOW'] + 1), on=['CARD_NUM', 'WINDOW'], how='left' ).rename(columns={'TOTAL_TRANS': 'COUNT_PREV_WINDOW'}).fillna(0) # 匹配前2个窗口 df = df.merge( window_totals.assign(WINDOW=window_totals['WINDOW'] + 2), on=['CARD_NUM', 'WINDOW'], how='left' ).rename(columns={'TOTAL_TRANS': 'COUNT_2WINDOWS_AGO'}).fillna(0) # 匹配前3个窗口 df = df.merge( window_totals.assign(WINDOW=window_totals['WINDOW'] + 3), on=['CARD_NUM', 'WINDOW'], how='left' ).rename(columns={'TOTAL_TRANS': 'COUNT_3WINDOWS_AGO'}).fillna(0)
方案二:基于字典映射的实现(代码更简洁)
先构建卡-窗口到总交易数的字典,再通过map匹配历史窗口:
import pandas as pd df = pd.DataFrame(data) # 生成卡-窗口的总交易数字典 window_totals = df.groupby(['CARD_NUM', 'WINDOW'])['COUNT_CURRENT_WINDOW'].max() window_map = window_totals.to_dict() # 匹配各历史窗口,无数据则填0 df['COUNT_PREV_WINDOW'] = df.apply( lambda x: window_map.get((x['CARD_NUM'], x['WINDOW'] - 1), 0), axis=1 ) df['COUNT_2WINDOWS_AGO'] = df.apply( lambda x: window_map.get((x['CARD_NUM'], x['WINDOW'] - 2), 0), axis=1 ) df['COUNT_3WINDOWS_AGO'] = df.apply( lambda x: window_map.get((x['CARD_NUM'], x['WINDOW'] - 3), 0), axis=1 )
两种方案均使用Pandas矢量化操作,无循环,可高效处理大规模数据集。
内容的提问来源于stack exchange,提问作者nickot
相关产品推荐
相关产品推荐

