在對時間序列進行分析的時候,經常會碰到具有周期性和非週期性的時間序列,這兩種序列需要區別對待。那如何去判斷時間序列的週期性呢?本文將介紹一種方法來檢測時間序列的週期性。
檢測方法
平滑處理
時間序列經常會出現毛刺的點,需要做平滑處理才能分析,類似下圖中的資料。
消除資料的毛刺,可以用移動平均法,但是移動平均有時候處理完後並不能使資料平滑,本文中採用的方法很簡單:把每個點與上一點的變化值作為一個新的序列,對裡邊的異常值,也就是變化比較離譜的值剃掉,用前後資料的均值填充,詳細的程式碼如下diff_smooth函式,輸入的變數為時間序列以及要處理後的時間間隔,輸出為處理後的時間序列。
def diff_smooth(ts, interval):
‘’‘時間序列平滑處理’‘’
# 間隔為1小時
wide = interval/60
# 差分序列
dif = ts。diff()。dropna()
# 描述性統計得到:min,25%,50%,75%,max值
td = dif。describe()
# 定義高點閾值,1。5倍四分位距之外
high = td[‘75%’] + 1。5 * (td[‘75%’] - td[‘25%’])
# 定義低點閾值
low = td[‘25%’] - 1。5 * (td[‘75%’] - td[‘25%’])
i = 0
forbid_index = dif[(dif > high) | (dif < low)]。index
while i < len(forbid_index) - 1:
# 發現連續多少個點變化幅度過大
n = 1
# 異常點的起始索引
start = forbid_index[i]
if (i+n) < len(forbid_index)-1:
while forbid_index[i+n] == start + datetime。timedelta(minutes=n):
n += 1
i += n - 1
# 異常點的結束索引
end = forbid_index[i]
# 用前後值的中間值均勻填充
value = np。linspace(ts[start - datetime。timedelta(minutes=wide)], ts[end + datetime。timedelta(minutes=wide)], n)
ts[start: end] = value
i += 1
return ts
上圖的資料經過處理以後,變成了下圖的樣子。可以看到毛刺已經被去掉,序列變得平滑了很多,有利於我們進行下面的分析。
分段求DTW
對資料進行平滑處理以後,我們接下來就會檢測序列是否具有周期性。對週期性進行檢測,很容易想到的方法是:
找到時間序列的週期T;
以T為分割點,對序列進行分割。假設序列的長度是n,分割後就會有n/T個單元;
比較這n/T個單元的相似度,如果比較相似,則說明具有周期性,如果不是,則不具有周期性;
本文也按照上面的方法對序列進行切割,切割後形成三個單元,如下圖:
對於序列週期性檢測最終轉化為求三個單元的相似度。求時間序列的相似度有皮爾遜相關係數、有曲線擬合方法等,我們借鑑了NLP中求語音片段相似度的方法——DTW距離,來求三個單元的相似度。DTW透過把時間序列進行延伸和縮短,來計算兩個時間序列性之間的相似性。
如上圖所示,上下兩條實線代表兩個時間序列,時間序列之間的虛線代表兩個時間序列之間的相似的點。DTW使用所有這些相似點之間的距離的和,稱之為歸整路徑距離(Warp Path Distance)來衡量兩個時間序列之間的相似性。
python中有dtw包,直接可以計算兩個時間序列的相似度。
dist, cost, acc, path = dtw(x, y, dist=lambda x, y: np。linalg。norm(x - y, ord=1))
返回值中dist代表距離,如果dist越大,則距離越大,兩個時間序列越不相似。
本文比較了兩個相鄰時間單元的相似度,也就是求相鄰單元的DTW距離。假如有N個時間單元的話,就需要求N-1次距離。我們將所有的距離值儲存到dist_list這個列表中,根據設定的閾值,就可以判斷出是否具有周期性。
DTW的閾值到底設定多少比較合適?到目前為止,還沒有一個比較好的思路。我們是透過觀察所有曲線來區設定該閾值。如果您有比較好的方法,可以和我聯絡。
總結
上一篇部落格介紹了週期性時間序列的預測工作,這篇介紹了時間序列的檢測方法,至此,對於時間序列的分析將告一段落,但是我們的研究不會停止,接下來還會有智慧運維領域的更多研究成果,請您持續關注。