1 在時間序列中ACF圖和PACF圖是非常重要的兩個概念,如果運用時間序列做建模、交易或者預測的話。這兩個概念是必須的。
2 ACF和PACF分別為:自相關函數(系數)和偏自相關函數(系數)。
3 在許多軟件中比如Eviews分析軟件可以調出某一個序列的ACF圖和PACF圖,如下:

3.1 有時候這張圖是橫躺着的,不過這個不重要,反正一側為小於0的負值范圍,一側為大於0的正值范圍,均值(准確的說是坐標y軸為0,有些橫着的圖,會把x軸和y軸表示出來,值都在x軸上下附近呈現出來)。
3.2 紅色框框部分就是ACF圖,青色框框部分就是PACF圖,其中對應左邊的Autocorrelation就是英文單詞自相關的全稱;Partial Correlation就是英文單詞偏自相關的全稱。
3.3 我們要計算的就是這兩列數值。
3.4 其中紫色箭頭標注出來的是指的2倍標准誤范圍,后面可以用對應的數值是否超過范圍來判斷截尾、拖尾等信息,進而判斷采用哪種模型。
3.5 這里特別注明一下在默認狀態下,這兩根線是如何計算的:
在大樣本下(T很大的時候,這里T指的是樣本的個數;其實准確的說樣本符合均值為0的正太分布)。因此這里的對於ACF或PACF屬於一種分位點檢驗,這個東西在很多baidu可以找到一個正太分布圖,然后左右畫線會得到99%,90%...的分位點,這里的這兩根線就是指的這個。我們這里要做的是雙側對稱檢驗,所以上下兩根線分布式0±分位點的值。分位點=2倍×sqrt開方(1/T),這里的T指的是樣本的個數,樣本個數指的是原始的那個樣本的個數,不是ACF或PACF計算完的樣本個數。如果某一個值大於2倍標准誤,也就是說大於正態分布左/右的95%分位點,於是,在拒絕域,則拒絕0假設(也就是拒絕均值為0的假設)
例如:樣本共10個,ACF或PACF計算完畢后,他們的數量都為9個,其分為點為:2×sqrt(1/10) = 0.6324555320...,雙側檢驗邊沿值為:(0-0.6324555320,0+0.6324555320) = [-0.6324555320,+0.6324555320](這就是兩根虛線的值)。 (3.5.1)
另外我們還通過另外一個公式Sρk ≈ sqrt(1/n * (1+2*ρ12 + ... + 2*ρk-12))來計算acf的雙側檢驗值。具體在這篇博文中得出。http://www.cnblogs.com/noah0532/p/8453959.html
4 ACF算法的實現(python):
4.1 首先:給定一組數據,這里的是線性數據,不用太多10個元素,一組的時間序列。
1 TimeSeries = [13, 8, 15, 4, 4, 12, 11, 7, 14, 12] # 列表形式 2 print(TimeSeries) 3 # 顯示結果:[13, 8, 15, 4, 4, 12, 11, 7, 14, 12]
4.2 算法表達式。

4.3 算法解析:
計算ρ1:
給定一階滯后序列:其中滯后用L來表示,具體表示方法見鏈接:http://www.cnblogs.com/noah0532/p/8449991.html
1 LZt = TimeSeries[:-1:] 2 print(LZt) 3 # 顯示結果:[13, 8, 15, 4, 4, 12, 11, 7, 14]
對應的這兩個序列分別為:
[13, 8, 15, 4, 4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12, 11, 7, 14]
我們在把原始序列與一階滯后序列進行對齊:
1 Zt = TimeSeries[1::] 2 print(Zt) 3 # 顯示結果:[8, 15, 4, 4, 12, 11, 7, 14, 12]
這樣兩個序列變為:
[ 8, 15, 4, 4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12, 11, 7, 14]
對這兩個序列進行計算就會得出ρ1的結果(也就是ACF的第一個值)
4.4 其實我們可以得到這樣一個結果,就是這樣不斷的比較下去,計算每一個滯后,原始序列會少一個值來對應滯后的序列,然后分別來進行計算。我們把這個過程化簡一下,代碼如下(以如上10個序列為例,最后會得出9對)
1 TimeSeries = [13, 8, 15, 4, 4, 12, 11, 7, 14, 12] # 列表形式 2 Zt = [] 3 LZt = [] 4 i = 1 5 while i < len(TimeSeries): 6 L = TimeSeries[i::] 7 LL = TimeSeries[:-i:] 8 Zt.append(L) 9 LZt.append(LL) 10 i += 1 11 print(TimeSeries) 12 print(Zt) 13 print(LZt) 14 # 顯示結果: 15 # [13, 8, 15, 4, 4, 12, 11, 7, 14, 12] 16 # [[8, 15, 4, 4, 12, 11, 7, 14, 12], [15, 4, 4, 12, 11, 7, 14, 12], [4, 4, 12, 11, 7, 14, 12], [4, 12, 11, 7, 14, 12], [12, 11, 7, 14, 12], [11, 7, 14, 12], [7, 14, 12], [14, 12], [12]] 17 # [[13, 8, 15, 4, 4, 12, 11, 7, 14], [13, 8, 15, 4, 4, 12, 11, 7], [13, 8, 15, 4, 4, 12, 11], [13, 8, 15, 4, 4, 12], [13, 8, 15, 4, 4], [13, 8, 15, 4], [13, 8, 15], [13, 8], [13]]
4.5 通過這種遍歷加減方式,如果原始列表為10個元素的話會得到9對ACF帶計算序列,以此類推,這樣做的目的就是為了把序列進行對齊計算。把這9對列出來看一下:
[8, 15, 4, 4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12, 11, 7, 14]
[15, 4, 4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12, 11, 7]
[4, 4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12, 11]
[4, 12, 11, 7, 14, 12]
[13, 8, 15, 4, 4, 12]
[12, 11, 7, 14, 12]
[13, 8, 15, 4, 4]
[11, 7, 14, 12]
[13, 8, 15, 4]
[7, 14, 12]
[13, 8, 15]
[14, 12]
[13, 8]
[12]
[13]
4.6 根據算法公式,其分母為原始序列每一個元素距離平均值的平方。因此我們也可以把分母計算出來。
1 TimeSeries = [13, 8, 15, 4, 4, 12, 11, 7, 14, 12] # 列表形式 2 Zt = [] 3 LZt = [] 4 sum = 0 5 i = 1 6 while i < len(TimeSeries): 7 L = TimeSeries[i::] 8 LL = TimeSeries[:-i:] 9 sum = sum + TimeSeries[i - 1] 10 Zt.append(L) 11 LZt.append(LL) 12 i += 1 13 sum = sum + TimeSeries[-1] 14 avg = sum / len(TimeSeries) 15 print(TimeSeries) 16 print(Zt) 17 print(LZt) 18 print(avg) 19 # 顯示結果: 20 # 10.0
4.7 我們具備了這些所有的元素后開始計算這些所有ACF的ρ值,最終我們還是用列表來表示:
1 k = 0 2 result_Deno = 0 3 # 首先計算分母=分母都為通用 4 while k < len(TimeSeries): 5 result_Deno = result_Deno + pow((TimeSeries[k] - avg), 2) 6 k += 1 7 print(result_Deno) 8 # 顯示結果:144.0 9 10 # 然后計算分子 11 p = 0 12 q = 0 13 14 acf = [] 15 while p < len(Zt): 16 # print(Zt[p]) 17 # print(LZt[p]) 18 q = 0 19 result_Mole = 0 20 while q < len(Zt[p]): 21 result_Mole = result_Mole + (Zt[p][q] - avg) * (LZt[p][q] - avg) 22 q += 1 23 acf.append(round(result_Mole / result_Deno, 3)) # 保留小數點后三位 24 p += 1 25 print(acf) 26 # 顯示結果: 27 # [-0.188, -0.201, 0.181, -0.132, -0.326, 0.118, -0.049, 0.056, 0.042]
4.7 結果驗證:與用Eviews軟件計算的結果一致,算法結束:
[-0.188, -0.201, 0.181, -0.132, -0.326, 0.118, -0.049, 0.056, 0.042]

5 PACF算法的實現(python)
5.1 PACF算法源於ACF算法,必須先算出ACF的值來,然后再計算PACF,PACF這里用的是一個遞推形式的公式,計算每一個φ值。在書中有些φ值用φkk來表示,有時候也用一個大Pk來表示,其實道理都一樣。
5.2 遞推式如下:

5.3 這個遞推式,紫色框內為每期值,下面的屬於過度值,用於遞推累計。
5.4 這個遞推式算法看起來無從下手比較麻煩。我們先分解一下:
5.5 j和k的下標解析:
如果k = 1,那么 j = 1,2
如果k = 2,那么 j = 1,2
如果k = 3,那么 j = 1,2,3
如果k = 4,那么 j = 1,2,3,4
如果k = 5,那么 j = 1,2,3,4,5
如果k = 6,那么 j = 1,2,3,4,5,6
如果k = 7,那么 j = 1,2,3,4,5,6,7
如果k = 8,那么 j = 1,2,3,4,5,6,7,8
如果k = 9,那么 j = 1,2,3,4,5,6,7,8,9
... ....
從下標可以看出這個遞推關系,如果處於k的某種狀態(數值),那么 j 將從當前狀態遞減到1。這也就是 j = 1,...,k的解析。
5.6 從這個下標解析可以看出,這兩部分的遞推式,第一部分(紫色框框中)的是計算PACF當前值,而第二部分是計算每階段的PACF值的缺省值(所需值)的計算。因此每期的PACF值的計算,需要遞推第二個公式中的值,然后再去計算下一期PACF值。這個邏輯是這樣。
5.7 從5.5的這個規律可以看出,j 是 k遞減數,也就是當前值如果為k = 3的話,我們需要有3個第二個公式的值;如果k = 7的話,我們需要有7個第二個公式的值;這個邏輯是沒有問題的,我們以一個多的為例子
k = 8 通式如下:
φ9,j = φ8j - φ99*φ8,9-j
j = 1
φ91 = φ81 - φ99*φ88
j = 2
φ92 = φ82 - φ99*φ87
j = 3
φ93 = φ83 - φ99*φ86
j = 4
φ94 = φ84 - φ99*φ85
j = 5
φ95 = φ85 - φ99*φ84
j = 6
φ96 = φ86 - φ99*φ83
j = 7
φ97 = φ87 - φ99*φ82
j = 8
φ98 = φ88 - φ99*φ81
5.8 正如上面所示,我們會得到8個表達式。其每一期的表達式的值我們需要用列表來進行記錄;這里的規律是φ99來自於第一個部分的公式,其φ8x和后面的φ8x是來自第二部分公式計算的上一期的值。這樣這個第二部分的表達式我們就可以計算出來了。
5.9 我們再來遞推第一部分的那個公式。還是以上面的為例子。我們舉一個小點兒的數。
k = 2 通式如下
φ33 = φ3 - sigma(φ2j*ρ3-j) / 1 - sigma(φ2j*ρj)
j = 1
φ33 = φ3 - sigma(φ21*ρ2) / 1 - sigma(φ21*ρ1)
j = 2
φ33 = φ3 - sigma(φ22*ρ1) / 1 - sigma(φ22*ρ2)
我們把sigma中的兩部分進行合並得:
φ33 = φ3 - (φ21*ρ2 + φ22*ρ1) / 1 - (φ21*ρ1 + φ22*ρ2)
5.10 我們發現一個規律。如果要計算33的時候,其括號內的值φ的順序不變,其ρ值為反向求積然后再求和。
5.11 我們現在試着來求一下這段代碼的寫法,以上面計算出來的ACF值,接着來計算PACF值:
ACF = [-0.188, -0.201, 0.181, -0.132, -0.326, 0.118, -0.049, 0.056, 0.042]
ACF[0] = ρ1 = -0.188
ACF[1] = ρ2 = -0.201
ACF[2] = ρ4 = 0.181
ACF[3] = ρ5 = -0.132
ACF[4] = ρ6 = -0.326
ACF[5] = ρ7 = 0.118
ACF[6] = ρ8 = -0.049
ACF[7] = ρ9 = 0.056
ACF[8] = ρ9 = 0.042
5.12 (具體過程稍顯復雜,所以下面用這個顏色寫,這樣更清楚一些):
(1):遞推算法,在編寫的時候可能會對初學者燒腦一些。重要的是對於每一種算法,我們首先要找到他們的規律,再不會算,可以找張紙先手工驗算一下。
(2):acf值與pacf值的長度都是一樣,且第一個值都相等。φkk如果是兩個整數,比如φ11,φ33,這就是我們要求的pacf對應每期的值。
(3):正如上面所說的,都是整數的話是所求的值,那么不是整數的話,類似於φ41,φ42,φ43...等等這些,就是第二個遞推式所需要計算的值。這是值是為了求下一組數值所用。
(4):因此我們得出這樣一個規律,除去第一個值(pacf和acf第一個值都相等)。后面每期值分兩組來進行計算,然后一組循環結束,以此類推,知道pacf長度減-1都完成為止。
(5):那么一組是什么樣子的?比如第一組是:φ22,φ21;第二組:φ33,φ32,φ31;第三組:φ44,φ43,φ42,φ41...以此類推。一組等於一個大循環。
(6):每組值分第一部分整值計算和第二部分非整值計算,每一組所需的φ值(看遞推公式),是來自於上一組的值。因此我們計算完一組后,把所需要的值(代碼中是用個過渡變量bridge來記錄),送給下一組計算用,計算完畢刪除,再賦新值,再給下一組。循環往復,直到結束。
(7):那每組需要的值是什么樣子,以計算到φ44,這個為例子,所需要的bridge值為[φ31,φ32,φ33]。用完了后,bridge更新為[φ41,φ42,φ43,φ44],供下一組φ55使用,其他每組都是這個原理。在這里引入了一個橋變量。
(8):由於精度可能略有差別,這樣我們把之前計算的acf值先不要保留小數點后三位,計算完畢后,我們統一保留小數點后三位。
(9):最后再把上邊界和下邊界的值算出來。這個公式上面有。
5.13 具體代碼如下:
1 TimeSeries = [13, 8, 15, 4, 4, 12, 11, 7, 14, 12] # 列表形式 2 Zt = [] 3 LZt = [] 4 total = 0 5 i = 1 6 while i < len(TimeSeries): 7 L = TimeSeries[i::] 8 LL = TimeSeries[:-i:] 9 total = total + TimeSeries[i-1] 10 Zt.append(L) 11 LZt.append(LL) 12 i += 1 13 total = total + TimeSeries[-1] 14 avg = total / len(TimeSeries) 15 16 k = 0 17 result_Deno = 0 18 # 首先計算分母=分母都為通用 19 while k < len(TimeSeries): 20 result_Deno = result_Deno + pow((TimeSeries[k] - avg), 2) 21 k += 1 22 # print(result_Deno) 23 # 顯示結果:144.0 24 25 # 然后計算分子 26 p = 0 27 q = 0 28 acf = [] 29 while p < len(Zt): 30 # print(Zt[p]) 31 # print(LZt[p]) 32 q = 0 33 result_Mole = 0 34 while q < len(Zt[p]): 35 result_Mole = result_Mole + (Zt[p][q] - avg) * (LZt[p][q] - avg) 36 q += 1 37 acf.append(result_Mole / result_Deno) # 我們把前面計算的acf值先不要保留小數點后三位。 38 # acf.append(result_Mole / result_Deno) 39 p += 1 40 # print(acf) 41 42 # 初始化pacf 43 pacf = [] 44 bridge = [] 45 bridge.append(acf[0]) 46 pacf.append(acf[0]) # 第一個φ11等於ρ1,再初始化pacf的第一個值。 47 # 這里采用的是bridge這個循環變量。計算的邏輯是每次重新賦值一遍共下一輪計算所用。 48 49 50 T = 0 # 初始化大循環的值為0,9個數值一共循環7次。原因是下面。 51 while T < len(acf)-1: # pacf的初始值已經計算過一個了,acf的長度和pacf的長度是一樣的,因此,len(acf)此時要減去一個1 52 # 每進行一輪循環,這些變量都重新初始化一遍 53 Mole = 0 # 計算紫框中Σ累加的分子值 54 Deno = 0 # 計算紫框中Σ累計的分母值 55 cross = 0 # 每計算完一遍第一部分的值,暫時用cross這個變量先保存起來。 56 UnderCross = [] # 每計算完第二步的值,用UnderCross的值先保存起來,因為有時候第二步的值不是一個,所以用列表形式。 57 58 # 計算第一部分 59 t = 0 60 while t < len(bridge): # 計算φ22,φ33,φ44,φ55,φ66,φ22,φ77,φ88...這些值 61 Mole = Mole + (bridge[t] * acf[len(bridge)-1-t]) 62 Deno = Deno + (bridge[t] * acf[t]) 63 t += 1 64 cross = (acf[t] - Mole) / (1 - Deno) # acf[t] 正確,每次迭代完成,都是分子的第一個值。 65 # print(cross) 66 # print(t) 67 68 # 計算第二部分 69 p = 0 70 while p < len(bridge): # 計算像φ21,φ32,φ31...這樣的值 71 UnderCross.append(bridge[p] - cross * bridge[len(bridge)-1-p]) 72 p += 1 73 74 bridge = [] # bridge使用完畢,初始化,再次賦值供下一次計算 75 bridge.extend(UnderCross) # 把計算完畢的Under值賦值到bridge 76 bridge.append(cross) # 把計算完畢的cross值存放到最后的位置 77 pacf.append(cross) # 把pacf值累計進行添加並記錄 78 # print(bridge) 79 # print(UnderCross) 80 81 T += 1 82 # print(pacf) 83 # 整個的一段循環完畢。 84 85 86 # 最后我們重新遍歷一遍,然后把acf和pacf都保留小數點后三位。 87 AcfValue = [] 88 PacfValue = [] 89 lag = 0 90 for sht in acf: 91 lag = round(sht, 3) 92 AcfValue.append(lag) 93 lag = 0 94 print("acf值(保留小數點后三位)為:", AcfValue) 95 96 for sht in pacf: 97 lag = round(sht, 3) 98 PacfValue.append(lag) 99 lag = 0 100 print("pacf值(保留小數點后三位)為:", PacfValue) 101 102 103 # 最后把bounds值算出來。 104 import math 105 bound = [] 106 bound.append(-2*math.sqrt(1/len(TimeSeries))) 107 bound.append(2*math.sqrt(1/len(TimeSeries))) 108 print("上下邊界值分別為:", bound) 109 110 # 最終顯示結果: 111 # acf值(保留小數點后三位)為: [-0.188, -0.201, 0.181, -0.132, -0.326, 0.118, -0.049, 0.056, 0.042] 112 # pacf值(保留小數點后三位)為: [-0.188, -0.245, 0.097, -0.134, -0.361, -0.126, -0.213, 0.036, -0.119] 113 # 上下邊界值分別為: [-0.6324555320336759, 0.6324555320336759]
5.14 最終結果驗證:
acf值(保留小數點后三位)為: [-0.188, -0.201, 0.181, -0.132, -0.326, 0.118, -0.049, 0.056, 0.042]
pacf值(保留小數點后三位)為: [-0.188, -0.245, 0.097, -0.134, -0.361, -0.126, -0.213, 0.036, -0.119]
上下邊界值分別為: [-0.6324555320336759, 0.6324555320336759]這里是計算的pacf的bound值。在看圖的時候直接通用pacf的bound值。但是實際過程中這兩個標准差是不太一樣。具體見這篇博文http://www.cnblogs.com/noah0532/p/8453959.html
對照上面給出的Eviews軟件自動統計的值:

結果完全一致!
6 TB(交易開拓者代碼)
(持續編輯中。。。。。。。。。。)
