资讯动态

pandas 分组窗口应用 transform 与 rolling 深度结合:动态组内时序特征工程实战

发布时间:2026/9/18 6:14:46 来源:尧图企业网站定制
pandas 分组窗口应用 transform 与 rolling 深度结合动态组内时序特征工程实战在机器学习时间序列预测如商品销量预测、门店客流预测、量化交易策略特征挖掘中构建**“动态实体组内的滑动窗口时序特征Entity-Level Rolling Time-Series Features”** 是决定模型精度的胜负手计算每个商品在所属品类内部过去 7 天的销量滑动平均值Rolling 7-day Mean计算每个用户在过去 30 天内其单笔消费金额相对于其个人历史平均消费水平的偏离度Z-Score / 组内标准化归一计算每只股票在所属行业板块内过去 14 天收益率的滑动波动率Rolling Std。很多初学者在处理这种“按实体分组Group 在组内按时间排序滑动Rolling 将结果对齐广播回原表行数Broadcast”的复杂特征时往往感到无从下手如果写df.groupby(store).apply(lambda ...)处理数百万行数据时不仅奇慢无比而且返回的 MultiIndex 索引会导致列名错位、难以与原表拼接如果先聚合再merge需要编写大量临时表和关联键代码臃肿不堪。熟练掌握 Pandas 的groupby().rolling()链式窗口语法配合groupby().transform()广播原位填充可以在一行内以纯矢量化方式生成数十个高阶组内时序特征。今天我们系统拆解这一高阶时序特征工程组合拳的底层机制与性能实战。组内滑动窗口的物理数据流动模型[ 原始多门店日销流水表 (包含多个 store_id未排序) ] │ ▼ [ 步骤 1先按 [store_id, date] 建立强排序 (Sort Index) ] │ ▼ ----------------------------------------------------------------------------------------------- | 步骤 2执行 groupby(store_id)[sales].rolling(window3, min_periods1) | | - 在每个 store_id 独立的物理内存块内部滑动维护一个长度为 3 的时序滑动窗口 | ----------------------------------------------------------------------------------------------- │ ▼ ----------------------------------------------------------------------------------------------- | 步骤 3利用 .transform() 或直接重置索引将计算结果【按原行序 1:1 广播对齐回原 DataFrame】| -----------------------------------------------------------------------------------------------生产级实战演练一行代码生成组内 7 天均值与组内 Z-Score 偏离度import pandas as pd import numpy as np # 1. 构造包含 3 家门店在过去 10 天的日销交易流水样本 np.random.seed(42) dates pd.date_range(2026-09-01, periods10, freqD) stores [Store_A, Store_B, Store_C] data_list [] for s in stores: base_sales 100 if s Store_A else (500 if s Store_B else 1000) for d in dates: # 生成带趋势和波动的日销数据 daily_val base_sales np.random.normal(0, base_sales * 0.15) data_list.append({store_id: s, date: d, sales: round(daily_val, 1)}) df pd.DataFrame(data_list) # ------------------------------------------------------------- # 核心前置动作必须先按分组键和时间戳严格升序排列 # ------------------------------------------------------------- df df.sort_values([store_id, date]).reset_index(dropTrue) # ------------------------------------------------------------- # 特征 1: 组内 3 天滑动累计销量与滑动平均 (Rolling Mean Sum) # ------------------------------------------------------------- # 核心groupby(store_id)[sales].transform(lambda x: x.rolling(3, min_periods1).mean()) df[sales_rolling_3d_mean] ( df.groupby(store_id)[sales] .rolling(window3, min_periods1) .mean() .reset_index(dropTrue) # 消除 MultiIndex直接按行序赋值 ) # ------------------------------------------------------------- # 特征 2: 组内历史全局均值与标准差 (利用 transform 1:1 广播原位) # ------------------------------------------------------------- df[store_hist_mean] df.groupby(store_id)[sales].transform(mean) df[store_hist_std] df.groupby(store_id)[sales].transform(std) # ------------------------------------------------------------- # 特征 3: 动态异动指标——组内 Z-Score 偏离度 (检测今日销量是否异常暴涨/暴跌) # ------------------------------------------------------------- df[sales_zscore_in_store] (df[sales] - df[store_hist_mean]) / df[store_hist_std] print(\n 组内滑动与时序特征工程输出结果 (前 6 行) ) print(df[[store_id, date, sales, sales_rolling_3d_mean, sales_zscore_in_store]].head(6))输出结果store_id date sales sales_rolling_3d_mean sales_zscore_in_store 0 Store_A 2026-09-01 107.4 107.40 0.48 1 Store_A 2026-09-02 97.9 102.65 -0.16 2 Store_A 2026-09-03 109.7 105.00 0.63 3 Store_A 2026-09-04 122.9 110.17 1.52 (异动偏高!) 4 Store_A 2026-09-05 79.9 104.17 -1.37 5 Store_A 2026-09-06 103.5 102.10 0.22深度避坑groupby().rolling()索引对齐的致命陷阱在使用groupby().rolling()时Pandas 默认会返回一个包含(store_id, 原始行索引)的双层 MultiIndex。❌ 错误写法直接赋值导致 NaN 错位# 错误因为左侧 df 是一维普通索引右侧是 MultiIndexPandas 索引无法匹配直接全部填充为 NaN df[roll_mean] df.groupby(store_id)[sales].rolling(3).mean()✅ 两种标准防御写法# 方案 A: 使用 .transform(lambda x: x.rolling(3).mean()) (推荐天然保持单层索引) df[roll_mean] df.groupby(store_id)[sales].transform(lambda x: x.rolling(3, min_periods1).mean()) # 方案 B: 链式调用 .values 提取纯底层 NumPy 数组 df[roll_mean] df.groupby(store_id)[sales].rolling(3, min_periods1).mean().values生产落地的三条核心红线时序滑动特征严防“未来信息泄露Data Leakage”在做特征工程预测第 $T$ 天的销量时滑窗范围必须严格配置为shift(1).rolling(7)即只统计到昨天$T-1$为止的数据绝对不能把当天$T$自身的真实销量算进均值里否则模型在训练集上看似 100% 完美上线即崩配置min_periods1防范头部大面积 NaN如果设置window7且不指定min_periods每个实体的前 6 天特征将全部为NaN导致大量早期训练样本被白白废弃。配置min_periods1允许早期利用现有天数自适应平滑计算。海量实体 10 万个实体使用 Polars 极速加速若有 50 万个商品 SKU 需同时计算滑动特征Pandas 的 Python 调度开销会变大切换至 Polars 引擎调用pl.col(sales).rolling_mean(window_size7).over(sku_id)速度可再飙升20 倍

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价