4、时间序列基础:时间序列特性、平稳性检验、差分与滑动窗口

各位好,我是老张。在储能系统里摸爬滚打了十几年,今天咱们聊聊时间序列。

说白了,储能系统里跑出来的数据,不管是电压、电流、温度还是SOC,本质上都是一串按时间顺序排列的数字。这就是时间序列。你想想看,电池的电压曲线、充放电功率的变化,哪个不是随时间走的?

我刚开始做储能数据分析时,犯过一个低级错误——直接把原始数据扔进模型里训练。结果呢?模型预测出来的结果完全没法用,偏差大到离谱。后来才明白,时间序列这东西,有它自己的脾气。

4.1 时间序列的三个核心特性

时间序列有三个特性,我管它叫「三件套」:趋势性、季节性和随机性。

  • 趋势性:数据长期往一个方向走。比如电池老化,内阻会慢慢变大,这就是趋势。
  • 季节性:周期性波动。储能系统白天充晚上放,一天一个周期;夏天和冬天用电量不同,一年一个周期。
  • 随机性:说不清道不明的噪声。比如某个时刻突然有个干扰信号,或者传感器抽风了一下。

我在项目中遇到过一件事:某储能电站的电压数据,看起来挺平稳的,但仔细一分析,其实藏着微弱的周期性波动。那是冷却风扇启停造成的。如果不把这个季节性成分剥离出来,后续的异常检测就会误报。

重点提醒:做时间序列分析,第一步不是建模,而是先看看数据长什么样。画个图,肉眼扫一遍,比啥都管用。

4.2 平稳性检验——数据能不能直接用?

平稳性,说白了就是数据的统计性质不随时间变化。均值不变、方差不变、自相关结构也不变。

为什么要关心这个?因为很多时间序列模型(比如ARIMA)都要求数据是平稳的。如果数据不平稳,模型会学出虚假的关系,预测结果就是扯淡。

检验平稳性,最常用的方法是ADF检验(Augmented Dickey-Fuller test)。

from statsmodels.tsa.stattools import adfuller

# 假设 data 是你的时间序列数据
result = adfuller(data)
print(f'ADF统计量: {result[0]}')
print(f'p值: {result[1]}')

if result[1] < 0.05:
    print('数据平稳,可以直接用')
else:
    print('数据不平稳,需要差分处理')

嗯,这里要注意:p值小于0.05,说明拒绝原假设,数据是平稳的。反之,就不平稳。

我曾经吃过一次亏。有个项目的数据,ADF检验显示p值0.04,刚好小于0.05,我就当它平稳了。结果模型跑出来效果很差。后来仔细一看,数据里有个明显的结构突变点——某天换了块新电池。所以啊,统计检验只是参考,还得结合业务知识来判断。

避坑指南:ADF检验对结构突变不敏感。如果数据中间有跳变,ADF可能误判为平稳。我建议你同时做KPSS检验,两个结果对照着看。

4.3 差分——让不平稳的数据变老实

数据不平稳怎么办?差分。说白了就是拿今天的值减去昨天的值。

一阶差分:diff_1 = data[t] - data[t-1]

二阶差分:diff_2 = diff_1[t] - diff_1[t-1]

大多数储能数据,一阶差分就够了。比如电池的SOC曲线,虽然整体趋势在下降,但差分之后基本就平稳了。

import numpy as np

# 一阶差分
diff_1 = np.diff(data, n=1)

# 二阶差分
diff_2 = np.diff(data, n=2)

# 差分后再做ADF检验
result_diff = adfuller(diff_1)
print(f'差分后p值: {result_diff[1]}')

你想想看,差分其实就是在消除趋势性。储能系统的充放电循环,差分之后,剩下的就是每个时刻的变化量,这往往更接近随机波动。

个人经验:不要过度差分。差分次数越多,丢失的信息也越多。我一般最多做到二阶差分,再高就没什么意义了。

4.4 滑动窗口——看局部,别看全局

滑动窗口,就是用一个固定大小的窗口,在时间序列上滑动,每次计算窗口内的统计量(均值、方差、最大值等)。

为什么要用滑动窗口?因为储能系统的状态是时变的。电池刚充满和快放完时,特性完全不一样。用全局的统计量去判断异常,会漏掉很多局部问题。

import pandas as pd

# 假设 df 是包含时间序列的DataFrame
# 计算滑动窗口均值(窗口大小=10)
df['rolling_mean'] = df['voltage'].rolling(window=10).mean()

# 计算滑动窗口标准差
df['rolling_std'] = df['voltage'].rolling(window=10).std()

# 异常检测:超过均值±3倍标准差
df['anomaly'] = (df['voltage'] > df['rolling_mean'] + 3 * df['rolling_std']) | \
                (df['voltage'] < df['rolling_mean'] - 3 * df['rolling_std'])

窗口大小怎么选?我个人的习惯是:先试几个值,看看效果。对于储能数据,5分钟一个采样点的话,窗口大小30(即2.5小时)通常是个不错的起点。

我记得有一次,某储能柜的电压数据,用全局阈值检测,一个异常都找不到。但换成滑动窗口后,发现了好几个微小的电压突降——那是某个电芯开始出现微短路的征兆。嗯,这就是滑动窗口的价值。

核心逻辑:滑动窗口的本质是「局部化」。它假设数据在短时间内是平稳的,这比假设整个时间序列平稳要合理得多。

4.5 知识体系总览

下面这张图,是我自己梳理的时间序列基础框架。你可以把它当作一个检查清单,做分析时对照着来。

时间序列基础:知识体系 时间序列数据 趋势性 季节性 随机性 平稳性检验(ADF / KPSS) 差分(一阶 / 二阶) 滑动窗口 应用:异常检测 / 预测 / 状态评估 三者关系:先看特性 → 检验平稳性 → 不平稳则差分 → 用滑动窗口做局部分析

4.6 实战中的组合拳

在实际项目中,我很少只用一种方法。通常是组合着来:

  1. 先画图:看看数据有没有明显的趋势或周期。
  2. 做ADF检验:确认平稳性。
  3. 如果不平稳:做一阶差分,再检验。
  4. 用滑动窗口:计算局部均值和标准差,做实时异常检测。

举个例子。某储能电站的电池组电压数据,原始数据不平稳(p=0.32)。一阶差分后,p值变成0.001,平稳了。然后用窗口大小为20的滑动窗口,成功检测出3次异常电压波动——后来查明是BMS采样模块的接触不良。

小技巧:滑动窗口的窗口大小,可以用自相关函数(ACF)来辅助确定。看ACF图,找到自相关系数降到0附近的滞后阶数,那就是一个合理的窗口大小。

好了,时间序列的基础就聊到这儿。这些东西看着简单,但真用好了,能解决储能系统里80%的数据问题。下次遇到异常数据,别急着上深度学习,先把这些基本功过一遍。


公众号:蓝海资料掘金营,微信deep3321