我是靠谱客的博主 顺心哑铃,最近开发中收集的这篇文章主要介绍SSA时间序列分解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

奇异谱分析(SSA)根据观测到的时间序列构造轨迹矩阵,并对轨迹矩阵进行分解重构,从而提取出代表原时间序列不同成分的信号,如长期趋势信号、周期信号、噪声信号等,从而进一步对分解得到的信号进行分析。

算法流程

Step1:根据原始时间序列构建轨迹矩阵 X X X

Step2:对矩阵X进行奇异值分解: X = ∑ i = 1 r σ i U i V i T X=sum_{i=1}^{r} sigma_i U_i V_i^T X=i=1rσiUiViT

Step3:按奇异值生成 r r r个子矩阵: X i = σ i U i V i T X_i = sigma_i U_i V_{i}^T Xi=σiUiViT

Step4:根据某一分组原则将子矩阵 X i X_i Xi分为 m m m个组

Step5:对子矩阵 X i X_i Xi进行对角均值化处理得到子序列 F i ~ widetilde{F_i} Fi

Step6:对 m m m个组中的子序列相加得到分组子序列。

矩阵分解

时间序列嵌入形成轨迹矩阵

输入:原始时间序列 y y y,窗口长度 L ( 2 ≤ L ≤ N / 2 ) L (2 le L le N/2) L(2LN/2) N = l e n ( y ) N=len(y) N=len(y)
X = [ y 1 y 2 y 3 y 4 … y N − L + 1 y 2 y 3 y 4 y 5 … y N − L + 2 y 3 y 4 y 5 y 6 … y N − L + 3 ⋮ ⋮ ⋮ ⋮ ⋱ ⋮ y L y L + 1 y L + 2 y L + 3 … y N ] (1) mathbf{X} = begin{bmatrix}tag{1} y_1 & y_2 & y_3 & y_4 &ldots & y_{N-L+1} \ y_2 & y_3 & y_4 & y_5 &ldots & y_{N-L+2} \ y_3 & y_4 & y_5 & y_6 &ldots & y_{N-L+3} \ vdots & vdots & vdots & vdots & ddots & vdots \ y_{L} & y_{L+1} & y_{L+2} & y_{L+3} & ldots & y_{N} \ end{bmatrix} X=y1y2y3yLy2y3y4yL+1y3y4y5yL+2y4y5y6yL+3yNL+1yNL+2yNL+3yN(1)
显然矩阵 X X X是一个汉克尔矩阵(每一条副对角线的元素都相等),矩阵的行数为窗口长度 L L L,矩阵的列数为 N − L + 1 N-L+1 NL+1

奇异值分解(SVD)

X = U Σ V T (2) X = U Sigma V^T tag{2} X=UΣVT(2)

其中:

  • U U U L × L L times L L×L酉矩阵

  • Σ Sigma Σ L × K L times K L×K对角阵

  • V V V K × K K times K K×K酉矩阵

奇异值分解将轨迹矩阵 X X X分解为酉矩阵 U U U,对角阵 Σ Sigma Σ和酉矩阵 V V V的线性组合。这意味着:
X = ∑ i = 1 r σ i U i V i T = ∑ i = 1 r X i (3) begin{aligned}tag{3} mathbf{X} & = sum_{i=1}^{r}sigma_i U_i V_i^{text{T}} \ & = sum_{i=1}^{r}mathbf{X}_i end{aligned} X=i=1rσiUiViT=i=1rXi(3)

r r r表示矩阵 X X X的非零特征根数也即矩阵的秩。

序列重构

特征分组

不妨设根据某一分组原则将子矩阵分为了trend、periodic、noise 3组,对应地矩阵 X X X分解得到的子序列将被组合为3部分。
X ~ (trend) = ∑ t ∈ t r e n d X ~ t    ⟹    F ~ (trend) = ∑ t ∈ t r e n d F ~ t X ~ (periodic) = ∑ p ∈ p e r i o d i c X ~ p    ⟹    F ~ (periodic) = ∑ p ∈ p e r i o d i c F ~ p X ~ (noise) = ∑ n ∈ n o i s e X ~ n    ⟹    F ~ (noise) = s u m n ∈ n o i s e F ~ n (4) begin{aligned}tag{4} tilde{mathbf{X}}^{text{(trend)}} & = sum_{t in trend}tilde{mathbf{X}}_t & implies & tilde{F}^{text{(trend)}} = sum_{t in trend} tilde{F}_t \ tilde{mathbf{X}}^{text{(periodic)}} & = sum_{p in periodic}tilde{mathbf{X}}_p & implies & tilde{F}^{text{(periodic)}} = sum_{p in periodic} tilde{F}_p\ tilde{mathbf{X}}^{text{(noise)}} & = sum_{n in noise}tilde{mathbf{X}}_n & implies & tilde{F}^{text{(noise)}} = sum_{n in noise} tilde{F}_n end{aligned} X~(trend)X~(periodic)X~(noise)=ttrendX~t=pperiodicX~p=nnoiseX~nF~(trend)=ttrendF~tF~(periodic)=pperiodicF~pF~(noise)=sumnnoiseF~n(4)

对角平均(化矩阵为序列)

y ~ k = { 1 k ∑ m = 1 k x m , k − m + 1   1 ≤ k < L ∗ 1 L ∗ ∑ m = 1 L ∗ x m , k − m + 1   L ∗ ≤ k ≤ K ∗ 1 N − k + 1 ∑ m = k − K ∗ + 1 N − K ∗ + 1 x m , k − m + 1   K ∗ < k ≤ N (5) widetilde{y}_{k} = left{ begin{array}{lr}tag{5} frac{1}{k}sum_{m=1}^{k} x_{m, k-m+1} & 1 le k < L^* \ frac{1}{L^*}sum_{m=1}^{L^*} x_{m, k-m+1} & L^* le k le K^* \ frac{1}{N-k+1}sum_{m=k-K^*+1}^{N-K^*+1} x_{m, k-m+1} & K^* < k le N \ end{array} right. y k=k1m=1kxm,km+1L1m=1Lxm,km+1Nk+11m=kK+1NK+1xm,km+1 1k<L LkK K<kN(5)

其中, L ∗ = m i n ( L , N − L + 1 ) , K ∗ = m a x ( L , N − L + 1 ) L^* = min(L,N-L+1),K^*=max(L,N-L+1) L=min(L,NL+1),K=max(L,NL+1)

实例

数据来源于:Kaggle原油价格预测

'''数据处理'''
import pandas as pd
import numpy as np
'''数据可视化'''
import matplotlib.pyplot as plt
plt.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=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()

最后

以上就是顺心哑铃为你收集整理的SSA时间序列分解的全部内容,希望文章能够帮你解决SSA时间序列分解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部