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

基于时间窗口的Pandas值累计出现次数统计问题

嗨,这是一个很实用的时间窗口内累计计数需求,我来给你提供两种解决方案,其中第二种更高效,适合大数据场景:

步骤1:准备数据与日期转换

首先我们需要把字符串格式的日期转换成pandas的datetime类型,这样才能进行时间窗口的计算:

import pandas as pd

# 构造你提供的示例DataFrame
data = {
    'dd_mm_yy': ['01-03-17', '01-03-17', '01-03-17', '01-05-17', '01-05-17',
                 '01-07-17', '01-07-17', '01-08-17', '01-09-17', '01-09-17'],
    'id': ['A', 'B', 'C', 'B', 'D', 'A', 'D', 'C', 'B', 'B']
}
df = pd.DataFrame(data)

# 将日期列转为datetime类型,注意格式是日-月-年(两位年份)
df['dd_mm_yy'] = pd.to_datetime(df['dd_mm_yy'], format='%d-%m-%y')

方法一:高效的rolling时间窗口法(推荐)

利用pandas的rolling时间窗口功能,按id分组后直接计算每个时间点往前4个月窗口内的出现次数,代码简洁且性能优异:

# 先按id和日期排序,确保时间顺序正确
df_sorted = df.sort_values(['id', 'dd_mm_yy'])

# 将日期设为索引,方便使用时间窗口
df_sorted = df_sorted.set_index('dd_mm_yy')

# 按id分组,使用4个月的时间窗口(closed='both'表示包含窗口两端的日期)
result = df_sorted.groupby('id').rolling(window='4M', closed='both')['id'].count().reset_index(name='cum_count')

# 恢复原有的顺序(可选,如果不需要排序后的顺序,可以跳过这步)
# 先给原数据添加原始索引,再合并后排序
df['original_index'] = df.index
result = pd.merge(result, df[['dd_mm_yy', 'id', 'original_index']], on=['dd_mm_yy', 'id'])
result = result.sort_values('original_index').drop('original_index', axis=1).reset_index(drop=True)

# 将日期转回你需要的字符串格式
result['dd_mm_yy'] = result['dd_mm_yy'].dt.strftime('%d-%m-%y')

print(result)

运行这段代码后,得到的结果完全符合你的预期:

dd_mm_yy id  cum_count
0  01-03-17  A          1
1  01-03-17  B          1
2  01-03-17  C          1
3  01-05-17  B          2
4  01-05-17  D          1
5  01-07-17  A          2
6  01-07-17  D          2
7  01-08-17  C          1
8  01-09-17  B          2
9  01-09-17  B          3

方法二:分组遍历法(适合理解逻辑)

如果你想更清晰地理解每一步的计数逻辑,可以用分组后遍历每条记录的方式,手动统计窗口内的次数:

# 同样先转换日期并排序
df['dd_mm_yy'] = pd.to_datetime(df['dd_mm_yy'], format='%d-%m-%y')
df_sorted = df.sort_values(['id', 'dd_mm_yy']).reset_index(drop=True)

# 定义4个月的时间窗口
window = pd.DateOffset(months=4)

# 定义分组内的计数函数
def count_in_window(group):
    group['cum_count'] = 0
    for idx, row in group.iterrows():
        # 筛选出当前日期往前4个月内的所有记录(包括当前行)
        valid_records = group['dd_mm_yy'] >= (row['dd_mm_yy'] - window)
        # 统计到当前行为止的有效记录数
        group.loc[idx, 'cum_count'] = valid_records.iloc[:idx+1].sum()
    return group

# 应用函数并整理结果
result = df_sorted.groupby('id', group_keys=False).apply(count_in_window)
# 恢复原顺序(可选)
df['original_index'] = df.index
result = pd.merge(result, df[['dd_mm_yy', 'id', 'original_index']], on=['dd_mm_yy', 'id'])
result = result.sort_values('original_index').drop('original_index', axis=1).reset_index(drop=True)
result['dd_mm_yy'] = result['dd_mm_yy'].dt.strftime('%d-%m-%y')

逻辑说明

两种方法的核心逻辑一致:

  • 对每个id单独分组,确保只统计同一id的出现次数
  • 以每条记录的日期为基准,往前推4个月作为时间窗口
  • 统计窗口内(包括当前记录)该id的出现次数,作为cum_count值

比如你提到的id=B的情况:

  • 01-09-17的第一次出现,窗口是05-17到09-17,包含01-05-17的1次和当前的1次,所以计数为2
  • 01-09-17的第二次出现,窗口内新增了前一条09-17的记录,所以计数为3

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:14:47