Scipy平滑含零值数据问题:保留零值并分段平滑非零段
问题:分段平滑非零数据段并保留零值
原始DataFrame数据
1960-09-01 24027064 4503904.333 1960-10-01 18020298 3377928.25 1960-11-01 12013532 2251952.167 1960-12-01 6006766 1125976.083 1961-01-01 0 0 1961-02-01 0 0 1961-03-01 0 0 1961-04-01 0 0 1961-05-01 0 0 1961-06-01 0 0 1961-07-01 0 0 1961-08-01 0 0 1961-09-01 0 0 1961-10-01 0 0 1961-11-01 0 0 1961-12-01 0 0 1969-01-01 0 0 1969-02-01 6173432.667 1150976.083 1969-03-01 12346865.33 2301952.167 1969-04-01 18520298 3452928.25
尝试的平滑代码
直接对整个DataFrame应用savgol_filter:
from scipy.signal import savgol_filter df = df.apply(savgol_filter, window_length=df.shape[0] // 5, polyorder=2)
不符合预期的结果
原零值被拟合为非零数值,结果如下:
1960-09-01 25874679.88 4850242.328 1960-10-01 24574614.17 4606543.324 1960-11-01 23301520.97 4367900.35 1960-12-01 22055400.26 4134313.405 1961-01-01 20836252.04 3905782.489 1961-02-01 19644076.31 3682307.603 1961-03-01 18478873.09 3463888.745 1961-04-01 17340642.35 3250525.916 1961-05-01 16229384.11 3042219.117 1961-06-01 15145098.37 2838968.347 1961-07-01 14087785.12 2640773.605 1961-08-01 13057444.36 2447634.893 1961-09-01 12054076.1 2259552.21 1961-10-01 11077680.33 2076525.557 1961-11-01 10128257.06 1898554.932 1961-12-01 9205806.28 1725640.336 1969-01-01 19071395.37 5088684.927 1969-02-01 19448311.7 5412158.942 1969-03-01 19790962.91 5737508.936 1969-04-01 20099349 6080752.937
核心需求
- 保留所有原始零值,不做任何修改
- 仅对连续的非零数据段单独应用平滑处理,滑动窗口计算时忽略零值
- 本质是实现分段曲线拟合平滑,非零段之间的零值区域完全隔离
解决方案
通过识别连续非零数据分组,对每个分组单独应用savgol_filter,零值部分直接保留:
步骤1:定义分段平滑函数
import pandas as pd from scipy.signal import savgol_filter def segment_savgol(series, window_length, polyorder): # 为连续非零数据分配唯一分组ID,零值会被分到独立分组 non_zero_groups = series.ne(0).cumsum() def process_group(group): # 只有当分组长度大于多项式阶数时,才应用平滑(savgol要求window_length > polyorder) if len(group) > polyorder: # 窗口长度不能超过分组本身的长度 adjusted_window = min(window_length, len(group)) return savgol_filter(group, window_length=adjusted_window, polyorder=polyorder) # 长度不足的分组(包括零值分组)直接返回原数据 return group # 按分组处理后,还原为原索引顺序 return series.groupby(non_zero_groups).apply(process_group)
步骤2:应用到DataFrame
# 设置参数,和原代码保持一致 window_len = df.shape[0] // 5 poly_order = 2 # 对每一列应用分段平滑 df_smoothed = df.apply(segment_savgol, window_length=window_len, polyorder=poly_order)
效果说明
- 所有原始零值会被完整保留,不会参与任何平滑计算
- 每个连续非零数据段会被独立平滑,不会受到其他段或零值区域的影响
- 对于长度不足的非零段(长度≤多项式阶数),直接保留原数据避免报错
内容的提问来源于stack exchange,提问作者ishandutta2007
相关产品推荐
相关产品推荐

