时间序列分解是时序分析中的重要方法 , 广泛应用于时间序列预测 , 时间序列异常检测 , 时间序列聚类等场景 , 在工业界有很多的落地应用 。一个时间序列往往是以下几类变化形式的叠加或耦合:
- 长期趋势(Secular trend, T):长期趋势指现象在较长时期内持续发展变化的一种趋向或状态 。
- 季节变动(Seasonal Variation, S):季节波动是由于季节的变化引起的现象发展水平的规则变动
- 循环波动(Cyclical Variation, C):循环波动指以若干年为期限 , 不具严格规则的周期性连续变动
- 不规则波动(Irregular Variation, I): 不规则波动指由于众多偶然因素对时间序列造成的影响
本次文章为大家整理分解周期序列和趋势序列的方法 。
概述这里主要使用的技术是奇异谱分析(SSA) , 其是根据观测到的时间序列构造轨迹矩阵 , 并对轨迹矩阵进行分解和重构 , 从而提取出代表原时间序列不同成分的信号 , 如长期趋势信号、周期信号、噪声信号等 , 从而进一步对分解得到的信号进行分析 。
算法流程如下:
- 根据原始时间序列构建轨迹矩阵 X
- 对矩阵X进行奇异值分解
- 按奇异值生成r个子矩阵
- 根据某一分组原则将子矩阵 Xi 分为 m个组
- 对子矩阵 Xi 进行对角均值化处理得到子序列!
- 对m个组中的子序列相加得到分组子序列 。
显然矩阵X是一个汉克尔矩阵(每一条副对角线的元素都相等) , 矩阵的行数为窗口长度L , 列数为N-L+1
02 奇异值分解其中:
奇异值分解将轨迹矩阵 X 分解为酉矩阵 U , 对角阵 ∑ 和酉矩阵V的线性组合 。这意味着:
【如何将一个时间序列分解为周期序列和趋势序列的和?】r表示矩阵X的非零特征根数也即矩阵的秩 。
03 特征分组不妨设根据某一分组原则将子矩阵分为了trend、periodic、noise 3组 , 对应的矩阵X分解得到的子序列将被组合为3部分 。
04 对角平均化其中:
代码实例
'''数据处理'''import pandas as pdimport numpy as np'''数据可视化'''import matplotlib.pyplot as pltplt.rcParams['figure.dpi'] = 800 #调整分辨率plt.rcParams['font.sans-serif']=['SimHei'] #显示中文plt.rcParams['axes.unicode_minus']=False #正常显示负号###加载数据集###file = r'D:/关注@公众号|机器学习研习院/crude-oil-price.csv'oil_info = pd.read_csv(file,index_col='date')price = oil_info['price']# 算法封装class SSA(object): __supported_types = (pd.Series,np.ndarray,list) #限制时间序列的输入类型 def __init__(self,tseries,L): ''' Args: tseries:原始时间序列 L:窗口长度 ''' if not isinstance(tseries, self.__supported_types): raise TypeError("请确保时间序列的数据类型为Pandas Series,NumpPy array 或者list") else: self.orig_TS = pd.Series(tseries) self.N = len(tseries) #原始时间序列长度 if not 2 <= L <= self.N / 2: raise ValueError("窗口长度必须介于[2,N/2]") self.L = L #窗口长度 , 轨迹矩阵的行数 self.K = self.N - self.L + 1 #轨迹矩阵的列数 self.X = np.array([self.orig_TS.values[i:L+i] for i in range(0,self.K)]).T #奇异值分解 self.U,self.Sigma,VH = np.linalg.svd(self.X) self.r = np.linalg.matrix_rank(self.X) #矩阵的秩等于非零特征值的数量 #每一个非零特征值都对应一个子矩阵 , 子矩阵对角平均化后得到原始时间序列的一个子序列 self.TS_comps = np.zeros((self.N,self.r)) #对角平均还原 for i in range(self.r): X_elem = self.Sigma[i] * np.outer(self.U[:,i], VH[i,:]) X_rev = X_elem[::-1] self.TS_comps[:,i] = [X_rev.diagonal(j).mean() for j in range(-X_rev.shape[0]+1, X_rev.shape[1])] def comps_to_df(self): ''' 将子序列数组转换成DataFrame类型 ''' cols = ["F{}".format(i) for i in range(self.r)] return pd.DataFrame(data=https://www.isolves.com/it/cxkf/sf/2023-03-07/self.TS_comps,columns=cols,index=self.orig_TS.index) def reconsruct(self,indices): '''重构 , 可以是部分重构(相当于子序列的分组合并) , 也可以是全部合并(重构为原序列) Args: indices 重构所选择的子序列 ''' if isinstance(indices,int): indices = [indices] ts_vals = self.TS_comps[:,indices].sum(axis=1) return pd.Series(ts_vals,index=self.orig_TS.index) def vis(self): ''' 可视化子序列 ''' fig,axs = plt.subplots(self.r,sharex='all') for i in range(self.r): axs[i].plot(self.reconsruct(i),lw=1) price_SSA = SSA(price,7)comps_df = price_SSA.comps_to_df()
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 胆囊炎是什么原因造成的 胆囊炎的原因
- 如何包书 礼物 如何包书
- 如何录歌曲?如何进行伴奏录歌?
- 迪丽热巴|好事将近?迪丽热巴现身无名指戴钻戒,心情大好翘脚打招呼
- 如何减少眼角皱纹的方法 如何减少眼角皱纹
- 一个母亲的复仇 硬核母亲为女复仇太暴力 德里黑公交案案情
- 老版绝代双骄3 绝代双骄3
- 集结季第一个任务没有光圈 集结季第一个任务
- 怎么弄繁体字输入法 如何输入繁体字
- 司马南|老戏骨李立群紧急发视频向大家求助,司马南公开骂我,如何回应