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

非日期格式天数列的30天滚动求和实现方案问询

解决数值型Day列的30天滚动求和问题

你原代码的问题在于:rolling(30, on='day')中的30指定的是窗口包含的行数,而非基于day数值的时间范围,所以会计算最近30行的求和,而非30天内的数据。下面提供三种针对性的解决方案:

方案一:分组+行级筛选(小数据量适用)

直接针对每个客户分组,对组内每一行筛选出day在当前行day-30到day范围内的foo值求和:

import pandas as pd
import numpy as np

# 示例数据
df = pd.DataFrame({'client': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'], 
                   'day': [319, 323, 336, 352, 379, 424, 461, 486, 496, 499, 303, 334, 346, 373, 374, 395, 401, 408, 458, 492],
                   'foo': [5.0, 2.0, np.nan, np.nan, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan, 8.0, 7.0, 22.0, np.nan, 13.0, np.nan, np.nan, 5.0, 11.0, np.nan]})

def calc_30day_sum(group):
    group['foo_30day'] = group.apply(
        lambda row: group.loc[(group['day'] >= row['day'] - 30) & (group['day'] <= row['day']), 'foo'].sum(skipna=True),
        axis=1
    )
    return group

df = df.groupby('client').apply(calc_30day_sum).reset_index(drop=True)
print(df)

运行后会得到正确的按天数范围的求和结果,比如client A的第1行(day=323)会包含day=319和323的foo值,求和为5.0+2.0=7.0。

方案二:merge_asof批量关联(大数据量推荐)

如果数据量较大,行级循环效率偏低,可以用merge_asof批量匹配每个行对应的30天范围数据,再分组求和:

# 先按client和day排序(merge_asof要求输入已排序)
df_sorted = df.sort_values(['client', 'day']).reset_index(drop=True)
# 复制数据作为匹配源,计算day的下限
df_match = df_sorted.copy()
df_match['day_low'] = df_match['day'] - 30

# 匹配每个client下,day在[day_low, day]范围内的所有记录
merged = pd.merge_asof(
    df_sorted,
    df_match[['client', 'day', 'foo']],
    on='day',
    by='client',
    direction='backward',
    tolerance=30
)

# 按原数据索引分组求和,再合并回原表
df_sorted['foo_30day'] = merged.groupby(merged.index)['foo_y'].sum(skipna=True)
df = df.merge(df_sorted[['client', 'day', 'foo_30day']], on=['client', 'day'], how='left')
print(df)

这种方法利用向量化操作,比循环apply效率提升显著,适合处理十万级以上的数据量。

方案三:转换为日期格式后使用滚动窗口

如果day代表的是明确的日期偏移(比如当年的第几天、从某个起始日开始的累计天数),可以转换为日期格式后使用rolling('30D'):

# 假设day是2023年的第几天,起始日期可根据实际情况调整
df['date'] = pd.to_datetime('2023-01-01') + pd.to_timedelta(df['day'] - 1, unit='D')
# 按client分组,基于日期计算30天滚动求和
df['foo_30day'] = df.groupby('client').rolling('30D', on='date', min_periods=1)['foo'].sum().values
# 可选:删除临时日期列
df = df.drop('date', axis=1)
print(df)

这种方法代码简洁,前提是你能明确day对应的日期规则。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:37:49