概述
奇异谱分析(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(2≤L≤N/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=⎣⎢⎢⎢⎢⎢⎡y1y2y3⋮yLy2y3y4⋮yL+1y3y4y5⋮yL+2y4y5y6⋮yL+3………⋱…yN−L+1yN−L+2yN−L+3⋮yN⎦⎥⎥⎥⎥⎥⎤(1)
显然矩阵
X
X
X是一个汉克尔矩阵(每一条副对角线的元素都相等),矩阵的行数为窗口长度
L
L
L,矩阵的列数为
N
−
L
+
1
N-L+1
N−L+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=1∑rσiUiViT=i=1∑rXi(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)=t∈trend∑X~t=p∈periodic∑X~p=n∈noise∑X~n⟹⟹⟹F~(trend)=t∈trend∑F~tF~(periodic)=p∈periodic∑F~pF~(noise)=sumn∈noiseF~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=⎩⎪⎨⎪⎧k1∑m=1kxm,k−m+1L∗1∑m=1L∗xm,k−m+1N−k+11∑m=k−K∗+1N−K∗+1xm,k−m+1 1≤k<L∗ L∗≤k≤K∗ K∗<k≤N(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,N−L+1),K∗=max(L,N−L+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时间序列分解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复