You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas按type分组统计当前行日期2年内的历史记录数

高效统计Pandas分组内过去2年的记录数

我有一个包含id、type、date字段的Pandas DataFrame,需要新增past_records_in_2_years列,实现按type分组后,统计当前行日期过去2年内的同组记录数(不包含当前行本身)。

示例数据

输入数据

id  type    date    
1   a   2023-06-18  
2   a   2022-06-18  
3   a   2021-06-18  
4   b   2023-06-18  
5   b   2020-06-18  
6   c   2023-06-18  

输出数据

id  type    date    past_records_in_2_years
1   a   2023-06-18  2
2   a   2022-06-18  1
3   a   2021-06-18  0
4   b   2023-06-18  0
5   b   2020-06-18  0
6   c   2023-06-18  0

原低效实现问题

最初用嵌套for循环实现,但百万级数据量下运行效率极低,原代码如下:

for i in range(len(df)):
    temp = df[df['type'] == df.loc[i]['type']].reset_index(drop = True)
    if len(temp) > 1:
        past_dates = 0
        for j in range(len(temp)):
            if (temp.loc[j]['date'] - df.loc[i]['date']) / np.timedelta64(1, 'Y') < 3:
                past_dates += 1
        if past_dates >= 2:
            df[i]['date'] = 1
        else:
            df[i]['date'] = 0
    else:
        df[i]['date'] = 0

高效解决方案

要处理百万级数据,必须避免循环,利用Pandas的向量化操作和分组窗口函数,以下是两种高效实现方式:

方法一:分组滚动窗口(rolling)

这种方法利用Pandas的滚动时间窗口功能,分组后统计每个日期前2年内的记录数:

import pandas as pd

# 1. 将date列转换为datetime类型(确保日期格式正确)
df['date'] = pd.to_datetime(df['date'])

# 2. 按type分组后对date排序,滚动窗口依赖有序序列
df_sorted = df.sort_values(['type', 'date']).reset_index(drop=True)

# 3. 定义2年的时间窗口(365*2天,可根据实际需求调整闰年逻辑)
two_years_window = pd.Timedelta(days=365*2)

# 4. 分组计算滚动窗口内的记录数,closed='left'表示不包含当前行
df_sorted['past_records_in_2_years'] = df_sorted.groupby('type')['date'] \
    .rolling(window=two_years_window, closed='left') \
    .count() \
    .reset_index(level=0, drop=True)

# 5. 合并回原DataFrame,恢复原始顺序
df = df.merge(df_sorted[['id', 'past_records_in_2_years']], on='id')

方法二:merge_asof区间匹配(大数据量首选)

merge_asof是基于二分查找的高效区间匹配方法,适合百万级以上数据:

import pandas as pd

# 1. 转换日期类型
df['date'] = pd.to_datetime(df['date'])

# 2. 生成匹配用的数据集,计算每个日期的2年前时间点
df_match = df.copy()
df_match['lower_date'] = df_match['date'] - pd.Timedelta(days=365*2)

# 3. 按type和date排序,merge_asof要求双方都按匹配键排序
df_sorted = df.sort_values(['type', 'date'])
df_match_sorted = df_match.sort_values(['type', 'date'])

# 4. 执行区间匹配:匹配同组内,date在[lower_date, 当前date)之间的记录
merged = pd.merge_asof(
    df_sorted,
    df_match_sorted[['type', 'date', 'id']],
    on='date',
    by='type',
    direction='backward',
    tolerance=pd.Timedelta(days=365*2)
)

# 5. 统计每个原始id的匹配数量,减去1排除自身
record_counts = merged.groupby('id_x').size() - 1
# 映射回原DataFrame,空值填充为0并转为整数
df['past_records_in_2_years'] = df['id'].map(record_counts).fillna(0).astype(int)

关键优化说明

  • 抛弃循环:用Pandas向量化操作替代O(n²)的嵌套循环,时间复杂度降至O(n log n)
  • 排序前置:时间窗口和区间匹配都依赖有序序列,排序后能大幅提升计算效率
  • 工具选择:数据量超大时优先用merge_asof,它的底层是二分查找,比滚动窗口更高效

内容的提问来源于stack exchange,提问作者ANUJ PATEL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:43:22