最近做時間序列分析的時候需要用到奇異譜分析,發現網上可以查到的資料很有限,看paper的時候發現大部分也說得有些簡略,所以這裏看完之後總結一下。
奇異譜分析(Singular Spectrum Analysis, SSA)是一種處理非線性時間序列數據的方法,通過對所要研究的時間序列的軌跡矩陣進行分解、重構等操作,提取出時間序列中的不同成分序列(長期趨勢,季節趨勢,噪聲等),從而進行對時間序列進行分析或去噪並用於其他一些任務。
奇異譜分析主要包括四個步驟:嵌入——分解——分組——重構。
1. 嵌入
SSA的分析對象是有限長一維時間序列 , 爲序列長度。首先需要選擇合適的窗口長度 將原始時間序列進行滯後排列得到軌跡矩陣:
通常情況下取 。令 ,則軌跡矩陣 爲的矩陣
2. 分解
接下來對軌跡矩陣進行奇異值分解,注意,這裏是對軌跡矩陣進行SVD分解。看資料的時候就是在奇異值分解這裏困惑了很久,具體來說就是將 分解爲以下形式:
其中 稱爲左矩陣; 僅在主對角線上有值,就是奇異值,其他元素均爲零; 稱爲右矩陣。此外 均爲單位正交陣,滿足 。
由於直接對軌跡矩陣分解比較困難,因此首先計算軌跡矩陣的協方差矩陣:
接下來對 進行特徵值分解得到特徵值 和對應的特徵向量 。此時,爲原序列的奇異譜 。並且有
這裏 對應的特徵向量 反映了時間序列的演變型,稱爲時間經驗正交函數(T-EOF)。
實際上python已經提供了奇異值分解的函數np.linalg.svd()
可以很方便的計算。關於奇異值分解更詳細的介紹可以看這篇博客。
3. 分組
關於分組,文獻中很常見的敘述是下面這樣:
簡單來說將所有的 個成分分爲 個不相交的組,代表着不同的趨勢成分。這樣接下來選擇主要的成分進行重構得到重構序列。Emmm。。。。這樣介紹可真是太簡潔明瞭導致動手實現的時候真是一臉懵。
因此在實現的時候參考了另一個版本,這裏將分組和重構放到一塊吧。。。。。這個版本有助於實現但是ran半天ran不清哪裏是分組,被自己菜哭。。。。。。。。。。
4. 重構
所以這裏接分解步。首先計算遲滯序列 在 上的投影:
表示軌跡矩陣 的第 列, 是 所反映的時間演變型在原序列的時段的權重, 稱爲時間主成分(TPC)。看到這裏應當發現了,由 構成的矩陣實際上就是沒有歸一化的右矩陣, 即 !
接下來就可以通過時間經驗正交函數和時間主成分來進行重建,具體重構過程如下:
這樣,所有重構序列的和應當等於原序列,即
通常情況下我們使用SSA只是爲了提取原序列的主要成分,以去噪爲例,我們只需要根據奇異值的大小選擇前 個貢獻大的成分重構原序列即可。
python程序
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''
@Date : 2019/11/11
@Author : Rezero
'''
import numpy as np
import matplotlib.pyplot as plt
path = "xxxx" # 數據集路徑
series = np.loadtxt(path)
series = series - np.mean(series) # 中心化(非必須)
# step1 嵌入
windowLen = 20 # 嵌入窗口長度
seriesLen = len(series) # 序列長度
K = seriesLen - windowLen + 1
X = np.zeros((windowLen, K))
for i in range(K):
X[:, i] = series[i:i + windowLen]
# step2: svd分解, U和sigma已經按升序排序
U, sigma, VT = np.linalg.svd(X, full_matrices=False)
for i in range(VT.shape[0]):
VT[i, :] *= sigma[i]
A = VT
# 重組
rec = np.zeros((windowLen, seriesLen))
for i in range(windowLen):
for j in range(windowLen-1):
for m in range(j+1):
rec[i, j] += A[i, j-m] * U[m, i]
rec[i, j] /= (j+1)
for j in range(windowLen-1, seriesLen - windowLen + 1):
for m in range(windowLen):
rec[i, j] += A[i, j-m] * U[m, i]
rec[i, j] /= windowLen
for j in range(seriesLen - windowLen + 1, seriesLen):
for m in range(j-seriesLen+windowLen, windowLen):
rec[i, j] += A[i, j - m] * U[m, i]
rec[i, j] /= (seriesLen - j)
rrr = np.sum(rec, axis=0) # 選擇重構的部分,這裏選了全部
plt.figure()
for i in range(10):
ax = plt.subplot(5,2,i+1)
ax.plot(rec[i, :])
plt.figure(2)
plt.plot(series)
plt.show()
運行程序結果如下,左邊是原始序列,右邊是按奇異值排序的前十個成分序列,可以看到除了前幾個剩餘的基本都可以視爲噪聲序列。
如果取前五個序列重構,最後重構出的序列如下
相比原序列可以看到重構出的序列明顯比原序列平滑,但是同時保持了總體的變化情況。
參考資料
https://www.cnblogs.com/endlesscoding/p/10033527.html
基於SSA的GPS座標序列去噪及季節信號提取