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 知识体系总览
下面这张图,是我自己梳理的时间序列基础框架。你可以把它当作一个检查清单,做分析时对照着来。
4.6 实战中的组合拳
在实际项目中,我很少只用一种方法。通常是组合着来:
- 先画图:看看数据有没有明显的趋势或周期。
- 做ADF检验:确认平稳性。
- 如果不平稳:做一阶差分,再检验。
- 用滑动窗口:计算局部均值和标准差,做实时异常检测。
举个例子。某储能电站的电池组电压数据,原始数据不平稳(p=0.32)。一阶差分后,p值变成0.001,平稳了。然后用窗口大小为20的滑动窗口,成功检测出3次异常电压波动——后来查明是BMS采样模块的接触不良。
小技巧:滑动窗口的窗口大小,可以用自相关函数(ACF)来辅助确定。看ACF图,找到自相关系数降到0附近的滞后阶数,那就是一个合理的窗口大小。
好了,时间序列的基础就聊到这儿。这些东西看着简单,但真用好了,能解决储能系统里80%的数据问题。下次遇到异常数据,别急着上深度学习,先把这些基本功过一遍。
公众号:蓝海资料掘金营,微信deep3321