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

Python中分解时间序列:如何获取无缺失趋势分量用于在线变点检测?

问题背景

我需要对每日时间序列的趋势分量做在线变点检测,避免季节性因素导致误报。但用statsmodels的seasonal_decompose和STL方法时,因为底层用了中心化移动平均(CMA),趋势分量的首尾会出现NaN值。我有4年多的每日数据,需要每日更新的完整趋势分量来检测变化,但目前哪怕数据包含2个以上周期,趋势分量首尾仍各有6个月的NaN,求问题原因和替代方法。

当前使用的代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose

df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)

ts = df['value'].values
result = seasonal_decompose(ts, model='additive', period=365)  # 年度季节性

trend_component = result.trend

valid_indices = ~np.isnan(trend_component)
trend_component_np = trend_component[valid_indices].reshape(-1, 1)

detector = rpt.Pelt(model="l2").fit(trend_component_np)
change_points = detector.predict(pen=150)
问题原因解释

中心化移动平均(CMA)的计算逻辑导致了首尾NaN:

  • 对于周期为365的年度季节性,seasonal_decompose先计算365天的简单移动平均(SMA),再对这个SMA做2期移动平均来中心化,最终得到趋势分量。
  • 计算开头的趋势值时,需要前182天左右((365+1)/2≈182)的前置数据;计算结尾的趋势值时,需要后182天的后置数据。
  • 这是CMA方法的固有缺陷,哪怕有4年数据,首尾的182天仍然没有足够的相邻数据完成计算,因此会生成NaN,和数据包含的周期数量无关。
替代方法

以下是几种能生成完整趋势分量、适配每日在线更新的方法:

1. 简单移动平均(SMA)

直接用季节周期长度作为窗口计算SMA,替代CMA,不会产生NaN,计算简单:

# 方式1:中心化窗口,首尾仍有少量NaN,但比CMA少很多
trend_component = df['value'].rolling(window=365, center=True).mean()
# 方式2:非中心化窗口,仅开头364天有NaN,适合在线更新时关注最新数据
trend_component = df['value'].rolling(window=365, center=False).mean()
  • 优点:计算快,非中心化模式下无尾部NaN,每日增量更新成本低。
  • 缺点:平滑效果弱于CMA,对异常值敏感度较高。

2. 指数加权移动平均(EWMA)

依赖权重衰减逻辑,不需要固定窗口,自然覆盖首尾数据,适合在线更新:

# span参数模拟365天的平滑效果,span≈2/(alpha+1),alpha为衰减系数
trend_component = df['value'].ewm(span=365, adjust=False).mean()
  • 优点:无NaN生成,更新速度快,对近期数据赋予更高权重,适配渐变趋势。
  • 缺点:需要调整span参数匹配季节性周期,平滑逻辑和CMA差异较大。

3. LOWESS局部加权回归

statsmodels提供的非参数拟合方法,可拟合非线性趋势,全程无NaN:

from statsmodels.nonparametric.smoothers_lowess import lowess

# frac参数控制平滑程度,0.2表示用20%的数据拟合每个点
trend_component = lowess(df['value'].values, df.index, frac=0.2, return_sorted=False)
trend_component = pd.Series(trend_component, index=df.index)
  • 优点:无NaN,能拟合复杂非线性趋势,对异常值鲁棒性强。
  • 缺点:计算量大于移动平均,在线更新时需重新拟合全序列(或做增量拟合)。

4. Prophet模型

Facebook的Prophet模型可自动分解趋势、季节性与节假日效应,输出完整趋势分量,支持在线更新:

from prophet import Prophet

# 转换为Prophet要求的格式
prophet_df = df.reset_index().rename(columns={'date': 'ds', 'value': 'y'})
model = Prophet(yearly_seasonality=True)
model.fit(prophet_df)
# 提取趋势分量
trend_component = model.predict(prophet_df)['trend'].values
trend_component = pd.Series(trend_component, index=df.index)
  • 优点:自动处理季节性,输出完整趋势,支持增量更新,对缺失值鲁棒。
  • 缺点:模型复杂度高,计算时间长于移动平均类方法。
在线变点检测适配

无论用哪种方法得到无NaN的趋势分量,都可直接传入变点检测代码,无需再过滤NaN:

# 假设trend_component是无NaN的Series
detector = rpt.Pelt(model="l2").fit(trend_component.values.reshape(-1, 1))
change_points = detector.predict(pen=150)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:12:51