時間序列之間的相關性檢測

爲了檢測時間序列的相關性,我們經常使用自相關,互相關或歸一化互相關。

互相關(Cross-Correlation)

互相關是兩個不同時間序列的比較,以檢測具有相同最大值和最小值的指標之間是否存在相關性。例如:“兩個音頻信號同相嗎?”

爲了檢測兩個信號之間的相關程度,我們使用互相關。 只需將兩個時間序列相乘和相加即可計算得出。

在以下示例中,序列A和B是互相關的,但序列C都不與此相關。

cross-correlation

a = [1, 2, -2, 4, 2, 3, 1, 0]
b = [2, 3, -2, 3, 2, 4, 1, -1]
c = [-2, 0, 4, 0, 1, 1, 0, -2]

pyplot.figure()
pyplot.title("Series")
pyplot.plot(range(len(a)), a, color="#f44e2e", label="a")
pyplot.plot(range(len(b)), b, color="#27ccc0", label="b")
pyplot.plot(range(len(c)), c, color="#273ecc", label="c")
pyplot.ylabel("value")
pyplot.xlabel("index")
pyplot.show()

cross_corr(x,y)=i=0n1xiyi cross\_corr(x, y) = \sum_{i=0}^{n-1} x_i*y_i

使用上面的互相關公式,我們可以計算序列之間的相關度。

import numpy as np
def cross_corr(set1, set2):
    return np.sum(set1 * set2)

a = np.array([1, 2, -2, 4, 2, 3, 1, 0])
b = np.array([2, 3, -2, 3, 2, 4, 1, -1])
c = np.array([-2, 0, 4, 0, 1, 1, 0, -2])

print(f"Cross Correlation a,b: {cross_corr(a, b)}")
print(f"Cross Correlation a,c: {cross_corr(a, c)}")
print(f"Cross Correlation b,c: {cross_corr(b, c)}")

OUTPUT:
Cross Correlation a,b: 41
Cross Correlation a,c: -5
Cross Correlation b,c: -4

序列a和b互相關性較大,值爲41。序列a和c,a和b相關性較小,值分別爲:-5,-4.

標準化的互相關(Normalized Cross-Correlation)

標準化的互相關也是用於兩個時間序列的比較,但是使用了不同的評分結果。除了簡單的互相關,它還可以比較具有不同值範圍的指標。例如:“商店中的顧客數量與每天的銷售數量之間是否存在相關性?”

使用互相關值評價相關性存在三個問題:

  1. 互相關的評分值難以理解。
  2. 兩個序列必須具有相同的幅度。如如果一個序列值縮小了一半,那麼他的相關性會減少。cross_corr(a,a/2)=19.5cross\_corr(a,a/2)=19.5
  3. 無法解決序列值爲0的問題,根據互相關公式,由於00=00*0=00200=00*200=0,因此無法解決0值。

爲了解決這些問題,使用標準化的互相關

norm_corr(x,y)=n=0n1xiyin=0n1xi2n=0n1yi2 norm\_corr(x,y)=\dfrac{\sum_{n=0}^{n-1} x_i*y_i}{\sqrt{\sum_{n=0}^{n-1} x_i^2 * \sum_{n=0}^{n-1} y_i^2}}

計算上邊a,b,c序列的標準化的相關性如下:

import numpy as np
def norm_cross_corr(set1, set2):
    # python中求Normalized Cross Correlation的函數是: statsmodels.tsa.stattools.ccf
    return np.sum(set1 * set2) / (np.linalg.norm(set1) * np.linalg.norm(set2))

a = np.array([1, 2, -2, 4, 2, 3, 1, 0])
b = np.array([2, 3, -2, 3, 2, 4, 1, -1])
c = np.array([-2, 0, 4, 0, 1, 1, 0, -2])

print(f"Normalized Cross Correlation a,b: {norm_cross_corr(a, b)}")
print(f"Normalized Cross Correlation a,c: {norm_cross_corr(a, c)}")
print(f"Normalized Cross Correlation b,c: {norm_cross_corr(b, c)}")

OUTPUT:
Normalized Cross Correlation a,b: 0.9476128352180998
Normalized Cross Correlation a,c: -0.15701857325533194
Normalized Cross Correlation b,c: -0.11322770341445959

標準化的互相關值很容易理解:

  1. 值越高,相關性越高。
  2. 當兩個信號完全相同時,最大值爲1:norm_cross_corr(a,a)=1norm\_cross\_corr(a,a)=1
  3. 當兩個信號完全相反時,最小值爲-1:norm_cross_corr(a,a)=1norm\_cross\_corr(a,-a)=-1

標準化的互相關可以檢測兩個幅度不同的信號的相關性,如:norm_cross_corr(a,a/2)=1norm\_cross\_corr(a,a/2)=1。信號A和具有一半振幅的同一信號之間具有最高的相關性!

自相關(Auto-Correlation)

自相關是時間序列在不同時間與其自身的比較。例如,其目的是檢測重複模式或季節性。例如:“服務器網站上是否有每週的季節性?”“本週的數據與前一週的數據高度相關嗎?”

自相關的用途非常廣泛,其中之一就是檢測由於季節性導致的可重複的模式。

下圖展示了具有季節性爲8的序列:

image

import numpy as np
np.random.seed(10)
a = np.tile(np.array(range(8)), 8) + np.random.normal(loc=0.0, scale=0.5, size=64)
pyplot.figure(figsize=(12, 4))
pyplot.title("Series")
pyplot.plot(range(len(a)), a, color="#f44e2e", label="a")
pyplot.ylabel("value")
pyplot.xlabel("index")
pyplot.legend(loc="upper right")
pyplot.show()

圖像由如上代碼生成,他是1~8的可重複序列,並且夾雜了一些隨機噪聲。

接下來計算時間偏移爲4和時間偏移爲8時信號與自身之間的自相關。
image

import numpy as np
np.random.seed(10)
a = np.tile(np.array(range(8)), 8) + np.random.normal(loc=0.0, scale=0.5, size=64)

ar4 = a[:len(a) - 4]
ar4_shift = a[4:]
print(f"Auto Correlation with shift 4: {cross_correlation(ar4, ar4_shift)}")

pyplot.figure(figsize=(12, 4))
pyplot.title("Series")
pyplot.plot(range(len(ar4)), ar4, color="blue", label="ar4")
pyplot.plot(range(len(ar4_shift)), ar4_shift, color="green", label="ar4_shift")
pyplot.ylabel("value")
pyplot.xlabel("index")
pyplot.legend(loc="upper right")
pyplot.show()

OUTPUT:
Auto Correlation with shift 4: 623.797612892277

image

import numpy as np
np.random.seed(10)
a = np.tile(np.array(range(8)), 8) + np.random.normal(loc=0.0, scale=0.5, size=64)

ar8 = a[:len(a) - 8]
ar8_shift = a[8:]
print(f"Auto Correlation with shift 4: {cross_correlation(ar8, ar8_shift)}")

pyplot.figure(figsize=(12, 4))
pyplot.title("Series")
pyplot.plot(range(len(ar4)), ar4, color="blue", label="ar4")
pyplot.plot(range(len(ar4_shift)), ar4_shift, color="green", label="ar4_shift")
pyplot.ylabel("value")
pyplot.xlabel("index")
pyplot.legend(loc="upper right")
pyplot.show()

OUTPUT:
Auto Correlation with shift 8: 996.8417240253186

上圖清楚地顯示了時間偏移8處的高自相關,而時間偏移4處沒有。計算出的自相關值可以看到shift爲8時的自相關值大於shift爲4時的值。所以相比於4,我們覺得季節性更可能爲8。

標準化的自相關(Normalized Auto-Correlation)

如前所述,用標準化的互相關可以更好地表達兩個序列的相關性

print(f"Auto Correlation with shift 4: {norm_cross_correlation(ar4, ar4_shift)}")
print(f"Auto Correlation with shift 8: {norm_cross_correlation(ar8, ar8_shift)}")

OUTPUT:
Auto Correlation with shift 4: 0.5779124931215941
Auto Correlation with shift 8: 0.9874907451139486

相關性與時移(Correlation with Time Shift)

標準化的互相關與時移(Normalized Cross-Correlation with Time Shift)

時移可以應用於所有上述算法。想法是將指標與具有不同“時間偏移”的另一個指標進行比較。將時間偏移應用於歸一化互相關函數將導致“具有X的時間偏移的歸一化互相關”。這可以用來回答以下問題:“當許多顧客進我的商店時,我的銷售額在20分鐘後會增加嗎?”

爲了處理時移,我們將原始信號與由x元素向右或向左移動的另一個信號關聯。就像我們爲自相關所做的一樣。

爲了檢測兩個指標是否與時移相關,我們需要計算所有可能的時移。

image

image

from statsmodels.tsa.stattools import ccf
a = np.array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4])
b = np.array([1, 2, 3, 3, 0, 1, 2, 3, 4, 0, 1, 1, 4, 4, 0, 1, 2, 3, 4, 0])
res_ary = ccf(a, b, unbiased=False)  # 計算各種time shift下的互相關值

print(res_ary)

pyplot.bar(range(len(res_ary)), res_ary, fc="blue")
pyplot.show()

OUTPUT:
[-3.29180823e-17  8.67261700e-01  1.27247799e-01 -4.65751654e-01
 -3.92862138e-01 -1.09726941e-17  6.20178595e-01  1.27247799e-01
 -2.92793480e-01 -2.94028896e-01 -2.47083106e-02  3.48387179e-01
  1.02539489e-01 -1.19835306e-01 -1.70487343e-01 -2.47083106e-02
  1.01304073e-01  5.31228677e-02 -2.10020640e-02 -4.69457901e-02]

從上圖以及數據可以看出,當time shift爲1時,序列a和b高度相關。

標準化的自相關與時移(Normalized Auto-Correlation with Time Shift)

image

image

from statsmodels.tsa.stattools import acf

np.random.seed(5)
a = np.tile(np.array(range(8)), 8) + np.random.normal(loc=0.0, scale=0.5, size=64)
res_ary = acf(a, nlags=30, fft=False)
print(res_ary)
pyplot.bar(range(len(res_ary)), res_ary, fc="blue")
pyplot.show()

OUTPUT:
[ 1.          0.33436187 -0.06325233 -0.39112367 -0.46168766 -0.43537856
 -0.15175395  0.24403119  0.86075234  0.31051923 -0.03270547 -0.32083613
 -0.40031665 -0.38383002 -0.14645852  0.20649692  0.72864895  0.2693097
 -0.01707009 -0.2658133  -0.3339456  -0.32771517 -0.1214095   0.17008013
  0.60740024  0.22492556 -0.01633838 -0.21157885 -0.27896827 -0.27765971
 -0.10762048]

原始序列是有8的季節性,自相關檢測也看到了在8的整數倍的偏移處得到了較高的自相關值。

將相關指標聚類(Cluster Correlated Metrics Together)

我們可以用Normalized Cross Correlation,根據相似度將相關性較大的metric聚類。

使用數據集:graphs45.csv

import numpy as np
import pandas as pd

def get_correlation_table(metric_df):
    metric_cnt = metric_df.shape[1]
    correlation_table = np.zeros((metric_cnt, metric_cnt))
    for i in range(metric_cnt):
        metric_1 = metric_df.iloc[:, i]
        for j in range(metric_cnt):
            if i == j:
                continue
            else:
                metric_2 = metric_df.iloc[:, j]
                cc_ary = ccf(metric_1, metric_2, unbiased=False)
                correlation_table[i, j] = cc_ary[0]
    return correlation_table


def find_related_metric(correlation_table, orig, high_corr):
    metric_corr_table = correlation_table[orig]
    corr_metric_lst = []
    for i in range(len(metric_corr_table)):
        if metric_corr_table[i] > high_corr:
            corr_metric_lst.append(i)

    return corr_metric_lst

if __mian__:
    metric_df = pd.read_csv("./graphs45.csv")
    correlation_table_ary = get_correlation_table(metric_df)

    orig = 3
    high_corr = 0.9
    corr_metric_lst = find_related_metric(correlation_table_ary, orig, high_corr)
    corr_metric_lst.append(orig)
    print(corr_metric_lst)

    pyplot.figure()
    pyplot.title("Series")
    for idx in corr_metric_lst:
        metric = metric_df.iloc[:, idx]
        pyplot.plot(range(len(metric)), metric, label=f"graph_{idx + 1}")
    pyplot.ylabel("value")
    pyplot.xlabel("index")
    pyplot.legend(loc="upper right")
    pyplot.show()

get_correlation_table()計算了所有metric之間的標準化的互相關值,結果存在correlation_table中,correlation_table[i,j]代表第i個metric和第j個metric之間的相關值。

然後find_related_metric()根據相關值表找出和graph_4相關值大於0.9的序列,最後找出的相關序列畫圖如下:

image

參考

  1. Understanding Cross-Correlation, Auto-Correlation, Normalization and Time Shift
  2. Detecting Correlation Among Multiple Time Series
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章