优化Pandas中按person-id和固定applied_on统计前置特定事件的效率
高效统计每个用户-申请日期组合的历史特定事件数量
优化方案(基于Pandas的merge_asof高效不等连接)
原循环实现的时间复杂度为O(n*m)(n为唯一组合数,m为总数据量),大数据量下性能极差。以下方案利用Pandas的merge_asof实现O(n log n)复杂度的高效计算,步骤如下:
import pandas as pd # 示例数据 df = pd.DataFrame({ 'person_id': ['A', 'A', 'A','B'], 'applied_on': pd.to_datetime(["2020-01-01", "2020-01-01","2021-01-01","2021-01-01"]), 'event_date': pd.to_datetime(["2020-03-01", "2020-09-01","2021-03-01","2020-03-01"]), 'event_indicator': [1, 0, 0, 1] }) # 1. 预处理事件数据:按用户分组排序,计算累计特定事件数 event_data = df[['person_id', 'event_date', 'event_indicator']].copy() event_data = event_data.sort_values(['person_id', 'event_date']) # 按用户累加特定事件数,得到每个事件日期对应的累计值 event_data['cumulative_events'] = event_data.groupby('person_id')['event_indicator'].cumsum() # 2. 提取并排序唯一的用户-申请日期组合(merge_asof要求左右数据集按关联键排序) unique_applications = df[['person_id', 'applied_on']].drop_duplicates() unique_applications = unique_applications.sort_values(['person_id', 'applied_on']) # 3. 用merge_asof做不等连接,匹配每个申请日期之前的最后一个事件累计值 result = pd.merge_asof( unique_applications, event_data, left_on='applied_on', right_on='event_date', by='person_id', direction='backward' # 匹配小于applied_on的最大event_date ) # 4. 处理无历史事件的情况,填充0并整理结果 result['numEvent'] = result['cumulative_events'].fillna(0).astype(int) result = result[['person_id', 'applied_on', 'numEvent']].reset_index(drop=True) print(result)
输出结果
person_id applied_on numEvent 0 A 2020-01-01 0 1 A 2021-01-01 1 2 B 2021-01-01 1
方案说明
- 排序与累计计算:先对事件数据按用户和事件日期排序,确保累计值的计算顺序正确,每个事件日期对应的累计值代表该用户到该日期为止的特定事件总数。
merge_asof高效匹配:该方法通过二分查找实现不等连接,仅需一次排序和匹配操作,远快于循环遍历的暴力筛选。direction='backward'确保找到每个申请日期之前的最后一个事件,其累计值即为所有符合条件的事件总和。- 空值处理:若用户在申请日期前无任何事件,
cumulative_events会返回NaN,需填充为0保证结果完整性。
原方案的问题
原循环对每个唯一的person_id+applied_on组合都遍历整个数据集进行筛选求和,当数据量较大(如百万级)时,会产生大量重复计算,导致性能急剧下降。
内容的提问来源于stack exchange,提问作者Mattias
相关产品推荐
相关产品推荐

