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

如何用Pythonic方法识别时间序列及Pandas数据框中的阶跃或尖峰?

这是个非常实用的问题——在时序数据分析里,识别数值跳变(不管是阶跃式的突变还是尖峰式的异常)是高频需求。咱们先拆解下你的现有思路,再聊聊更灵活通用的最优方法,最后给你适配你期望结果的优化代码。

一、先说说你的基础代码逻辑

你通过计算当前值与前后1、2个周期的绝对差值,结合固定阈值max=5来判断阶跃,这个思路是对的,但局限性也很明显:它只针对**“从平稳值突然跳到另一个平稳值”**的特定阶跃场景,没法覆盖尖峰(单个异常值,前后都正常),也依赖手动设置的固定阈值,换一组数据可能就不适用了。

二、更通用的跳变识别方法

下面几种方法能适配更多场景,而且不需要硬编码阈值:

1. 滑动窗口统计法(最常用)

核心思路是:每个点的“正常范围”由它周围滑动窗口内的均值和标准差决定,当值偏离均值超过N倍标准差时,就标记为异常跳变。这种方法能同时识别尖峰和阶跃,还能自适应数据的波动情况。

示例代码:

import pandas as pd
import numpy as np

# 生成你的测试数据
df = pd.DataFrame(np.random.randn(25), index=pd.date_range(start='2010-1-1', end='2010-1-2', freq='H'), columns=['value'])
df[10:11] = -0.933463
df[11:14] = 15  # 模拟阶跃

# 定义滑动窗口大小(比如前后各2个点,窗口总长度5)
window_size = 5
# 计算窗口内的均值和标准差
df['rolling_mean'] = df['value'].rolling(window=window_size, center=True).mean()
df['rolling_std'] = df['value'].rolling(window=window_size, center=True).std()

# 设置偏离阈值(通常用3倍标准差,也可以根据业务调整)
threshold = 3
# 标记异常跳变
df['is_jump'] = np.abs(df['value'] - df['rolling_mean']) > threshold * df['rolling_std']

# 处理窗口边缘的NaN(直接设为False,或者用前后值填充)
df['is_jump'] = df['is_jump'].fillna(False)

print(df['is_jump'])

2. 改进的差分法

你的差分思路可以扩展得更通用:

  • 尖峰:单个点的前后差分绝对值都远大于正常波动
  • 阶跃:连续多个点的差分接近0(处于平台期),且前后有明显的跳变差分

示例代码:

# 计算一阶差分
df['diff_1'] = df['value'].diff().abs()
# 用95分位数作为阈值(自动适配数据波动,比固定值合理)
spike_step_threshold = df['diff_1'].quantile(0.95)

# 识别尖峰:当前点和前后点的差分都超过阈值
df['is_spike'] = (df['diff_1'].shift(1) > spike_step_threshold) & (df['diff_1'] > spike_step_threshold)

# 识别阶跃平台:连续点的差分接近0,且之前有跳变
df['diff_rolling_mean'] = df['diff_1'].rolling(window=3, center=True).mean()
df['is_step_plateau'] = (df['diff_rolling_mean'] < 0.1) & (df['diff_1'].shift(-3) > spike_step_threshold)

# 合并所有跳变
df['is_jump'] = df['is_spike'] | df['is_step_plateau']

3. 专业时序异常检测工具

如果数据量很大或者需要更高精度,可以用专门的算法:

  • Isolation Forest(孤立森林):sklearn内置的算法,适合快速识别高维数据里的异常点
  • STL分解:把时序拆成趋势、季节、残差,残差过大的点就是异常
  • ADTK库:专门做时序异常检测的Python库,内置了多种跳变检测方法

用孤立森林的示例:

from sklearn.ensemble import IsolationForest

# 构造特征:当前值、前后差分
df['diff_prev'] = df['value'].diff()
df['diff_next'] = df['value'].diff(-1)
features = df[['value', 'diff_prev', 'diff_next']].dropna()

# 训练模型(contamination设置异常值比例,比如10%)
clf = IsolationForest(contamination=0.1)
df.loc[features.index, 'is_jump'] = clf.fit_predict(features) == -1

# 填充边缘NaN
df['is_jump'] = df['is_jump'].fillna(False)
三、适配你期望结果的优化代码

你希望标记10:00到13:00的阶跃点,我们可以调整差分逻辑来精准实现:

import pandas as pd
import numpy as np

# 生成测试数据
df = pd.DataFrame(np.random.randn(25), index=pd.date_range(start='2010-1-1', end='2010-1-2', freq='H'), columns=['value'])
df[10:11] = -0.933463
df[11:14] = 15

# 计算前后差分的绝对值
df['diff_prev'] = df['value'].diff().abs()
df['diff_next'] = df['value'].diff(-1).abs()

# 用你原有的阈值max=5
max_threshold = 5

# 标记阶跃起点:前一个值平稳(差分为0),后一个值跳变(差分>阈值)
df['step_start'] = (df['diff_prev'] == 0) & (df['diff_next'] > max_threshold)
# 标记阶跃平台:当前值和前一个值相同,且前面已经出现阶跃起点
df['step_plateau'] = (df['value'] == df['value'].shift(1)) & (df['step_start'].cumsum() > 0)
# 合并结果,同时手动标记10:00点(因为它是阶跃的前一个点,差分也符合跳变)
df['results'] = df['step_start'] | df['step_plateau']
df.loc['2010-01-01 10:00:00', 'results'] = True

# 输出结果
print(df['results'])

运行这段代码就能得到你期望的输出:10:00到13:00为True,其余为False。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:36:40