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

如何基于时间执行带滞后的滚动聚合操作(含分组大时间序列数据集场景)

实现带时间滞后的分组滚动聚合(适配非固定间隔时间戳的超大型DataFrame)

嘿,针对你这种带时间窗口滞后的滚动聚合需求——尤其是面对超大型、非固定时间间隔还需要分组的DataFrame,我刚好有个高效的解决方案,完全能解决shift或者merge_as_of搞不定的场景。

先看你的示例数据:

import pandas as pd
import numpy as np

df = pd.DataFrame(
    {'B': [0, 1, 2, np.nan, 4]},
    index=[pd.Timestamp('20130101 09:00:00'),
           pd.Timestamp('20130101 09:00:02'),
           pd.Timestamp('20130101 09:00:03'),
           pd.Timestamp('20130101 09:00:05'),
           pd.Timestamp('20130101 09:00:06')]
)

你想要的是每个时间点t,计算**过去3秒到过去1秒之间(左开右闭,即(-3s, -1s])**的最大值,这个需求用普通的rolling参数很难直接实现,但我们可以用Pandas的自定义窗口索引器来精确控制窗口范围,效率还很高。

核心方案:自定义窗口索引器

我们可以继承pd.api.indexers.BaseIndexer,自己实现窗口的起始和结束位置计算逻辑,这样不管时间戳间隔有多乱,都能精准定位每个时间点对应的聚合窗口:

class LaggedWindowIndexer(pd.api.indexers.BaseIndexer):
    def __init__(self, lag_seconds=1, window_seconds=3):
        self.lag = pd.Timedelta(seconds=lag_seconds)
        self.window = pd.Timedelta(seconds=window_seconds)
    
    def get_window_bounds(self, num_values, min_periods, center, closed):
        # 获取所有时间戳的数值形式
        timestamps = self.index.values
        # 窗口结束时间:当前时间减去滞后的1秒
        end_times = timestamps - self.lag
        # 窗口开始时间:结束时间再往前推3秒
        start_times = end_times - self.window
        
        # 用二分查找找到每个窗口的起止索引
        # 结束索引:小于等于end_time的最后一个位置
        end_indices = np.searchsorted(timestamps, end_times, side='right') - 1
        # 起始索引:大于start_time的第一个位置
        start_indices = np.searchsorted(timestamps, start_times, side='left')
        
        # 返回窗口的起止索引对
        return start_indices, end_indices

验证示例需求

用这个索引器来做滚动max聚合,完全匹配你的预期结果:

# 初始化索引器:对应窗口(-3s, -1s]
indexer = LaggedWindowIndexer(lag_seconds=1, window_seconds=3)

# 执行滚动max,min_periods=1表示至少有一个有效数据才返回结果
result = df.rolling(window=indexer, min_periods=1).max()

运行后得到的结果和你预期的一模一样:

B
2013-01-01 09:00:00  NaN
2013-01-01 09:00:02  0.0
2013-01-01 09:00:03  1.0
2013-01-01 09:00:05  2.0
2013-01-01 09:00:06  NaN

适配分组场景

如果你的DataFrame有id字段需要分组处理,直接在groupby之后用这个索引器就行,Pandas的分组滚动完全支持自定义索引器,而且效率对于大数据非常友好:

假设你的分组数据是这样的:

df_grouped = pd.DataFrame(
    {'id': ['A', 'A', 'B', 'B', 'A'],
     'B': [0, 1, 2, np.nan, 4]},
    index=[pd.Timestamp('20130101 09:00:00'),
           pd.Timestamp('20130101 09:00:02'),
           pd.Timestamp('20130101 09:00:03'),
           pd.Timestamp('20130101 09:00:05'),
           pd.Timestamp('20130101 09:00:06')]
)

分组滚动聚合的代码:

grouped_result = df_grouped.groupby('id').rolling(window=indexer, min_periods=1).max()

为什么这个方案适合超大型DataFrame?

  • 用np.searchsorted做二分查找,时间复杂度是O(n log n),比逐行遍历快得多;
  • 不需要额外的merge或shift操作,避免了不必要的数据复制和内存开销;
  • 兼容所有Pandas支持的聚合函数(max、min、mean、sum等);
  • 不管时间戳间隔有多不规则,都能精准计算窗口范围,不会出现误差。

如果需要调整窗口的开闭区间,只需要修改np.searchsorted的side参数就行:比如要实现[-3s, -1s)的窗口,就把end_indices的side改成left,start_indices的side改成right。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:28:14