基于时间窗口的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
相关产品推荐
相关产品推荐

