【python資料探勘課程】二十三.時間序列金融資料預測及Pandas庫詳解

Eastmount發表於2018-05-09

這是《Python資料探勘課程》系列文章,也是我上課內容及書籍中的一個案例。本文主要講述時間序列演算法原理,Pandas擴充套件包基本用法以及Python呼叫statsmodels庫的時間序列演算法。由於作者數學比較薄弱,自己也還在學習,所以原理推導部分本文只簡單敘述,同時參考了《Python金融大資料分析 ·Yves Hilpisch》書籍和其他大神的文章。

本篇文章為基礎性文章,希望對你有所幫助,提供些思路,也是自己教學的內容。如果文章中存在錯誤或不足之處,還請海涵。同時,推薦大家閱讀我以前的文章瞭解其他知識。

前文參考:
【Python資料探勘課程】一.安裝Python及爬蟲入門介紹
【Python資料探勘課程】二.Kmeans聚類資料分析及Anaconda介紹
【Python資料探勘課程】三.Kmeans聚類程式碼實現、作業及優化
【Python資料探勘課程】四.決策樹DTC資料分析及鳶尾資料集分析
【Python資料探勘課程】五.線性迴歸知識及預測糖尿病例項
【Python資料探勘課程】六.Numpy、Pandas和Matplotlib包基礎知識
【Python資料探勘課程】七.PCA降維操作及subplot子圖繪製
【Python資料探勘課程】八.關聯規則挖掘及Apriori實現購物推薦
【Python資料探勘課程】九.迴歸模型LinearRegression簡單分析氧化物資料
【python資料探勘課程】十.Pandas、Matplotlib、PCA繪圖實用程式碼補充
【python資料探勘課程】十一.Pandas、Matplotlib結合SQL語句視覺化分析
【python資料探勘課程】十二.Pandas、Matplotlib結合SQL語句對比圖分析
【python資料探勘課程】十三.WordCloud詞雲配置過程及詞頻分析
【python資料探勘課程】十四.Scipy呼叫curve_fit實現曲線擬合
【python資料探勘課程】十五.Matplotlib呼叫imshow()函式繪製熱圖
【python資料探勘課程】十六.邏輯迴歸LogisticRegression分析鳶尾花資料
【python資料探勘課程】十七.社交網路Networkx庫分析人物關係(初識篇)

【python資料探勘課程】十八.線性迴歸及多項式迴歸分析四個案例分享

【python資料探勘課程】十九.鳶尾花資料集視覺化、線性迴歸、決策樹花樣分析

【python資料探勘課程】二十.KNN最近鄰分類演算法分析詳解及平衡秤TXT資料集讀取

【python資料探勘課程】二十一.樸素貝葉斯分類器詳解及中文文字輿情分析
【python資料探勘課程】二十二.Basemap地圖包安裝入門及基礎知識講解



一. 時間序列基礎知識


社會經濟現象總是隨著時間的推移而變遷,呈現動態性。一個或一組變數x(t)進行觀測,將在一系列時刻t1、t2、...、tn得到離散數字組成的序列集合,稱之為時間序列。通過時間序列演算法,我們對事物進行動態的研究。

時間序列表示按時間先後順序排列的數列,通常X軸為時間要素,Y軸為資料要素,比如1986-2000年的人均GDP為y1、y2、...、yn,再如下圖所示太陽黑子運動規律。




指標通常包括時期指標(年度、月度)和時點指標(時刻)。時間序列分為以下三類:

    1.隨機性時間序列:各指標變動受隨機因素影響
    2.平穩時間序列:基本穩定在某個水平附近波動
    3.非平穩時間序列:存在某種規律性變動,比如趨勢性、季節性
時間序列常用的特徵統計量如下所示:(參考:
百度文庫




二. 金融時間序列-Pandas庫


該部分是作者學習《Python金融大資料分析》書籍第6章的內容,僅供大家學習:

金融學中最重要的資料型別之一是金融時間序列,以日期時間作為索引的資料,例如股票、GDP、匯率等。Python處理時間序列主要使用Pandas庫,其DataFrame和Series等基本類靈感來源於R語言。Pandas庫允許從Web上讀取資料,比如雅虎財經、谷歌財經等,也可以讀取csv檔案(逗號分割)。下面詳細介紹Pandas庫的用法:


1.DataFrame類

首先我們通過DataFrame定義資料,包括資料、標籤和索引三部分,其中資料包括列表、元組、字典、ndarray等型別,索引包括數值、字串和時間等。示例程式碼如下:

import pandas as pd
import numpy as np

df = pd.DataFrame([10,20,30,40],columns=['num'],
                  index=['a','b','c','d'])

print df.index
print df.columns
print df.ix['c']
print df.ix[df.index[1:3]]
print df.sum()
print df.apply(lambda x:x**2)

輸出結果如下所示,包括輸出索引、標籤值,獲取“c”對應數值等,通過df.sum()對資料進行求和、df.mean()求平均值、df.apply(lambda x:x**2)實現數值平方計算。

Index([u'a', u'b', u'c', u'd'], dtype='object')
Index([u'num'], dtype='object')
num    30
Name: c, dtype: int64
   num
b   20
c   30
num    100
dtype: int64
    num
a   100
b   400
c   900
d  1600

DataFrame物件總體上比較方便、高效,相比ndarray物件更專業化。下面程式碼是進維度擴增,增加了一個float型別。

df['floats']=(1.5, 2.5, 3.5, 4.5)
print df

輸出結果如下所示:

   num  floats
a   10     1.5
b   20     2.5
c   30     3.5
d   40     4.5

接下來再增加一個維度,通過索引進行對應。程式碼如下:

print df['floats']
df['names'] = pd.DataFrame(["Ya","Ga","Ha","Da"],
                           index=['d','a','b','c'])
print df

輸出結果如下:

a    1.5
b    2.5
c    3.5
d    4.5
Name: floats, dtype: float64
   num  floats names
a   10     1.5    Ga
b   20     2.5    Ha
c   30     3.5    Da
d   40     4.5    Ya


2.DatetimeIndex類

接下來我們講解DatetimeIndex類,通過它定義時間。首先呼叫numpy.random函式 生成一個9*4的標準正態分佈偽隨機數,然後定義列標籤,程式碼如下:

# -*- coding: cp936 -*-
import pandas as pd
import numpy as np

a = np.random.standard_normal((9,4))
print a.round(6) #6位小數
#print a
df = pd.DataFrame(a)
df.columns = ["No1", "No2", "No3", "No4"]
print df

輸出結果如下,如果需要進行訪問則呼叫df['No2'][3]實現。

        No1       No2       No3       No4
0 -0.320854 -0.625805 -0.421955  0.389512
1  0.370532 -1.221835  0.010364  1.393511
2 -0.229514 -0.477147  0.128166 -0.619752
3 -0.595702  1.799746  0.330161  1.669275
4 -0.692837 -0.208061  0.576877  1.007649
5 -1.021873 -0.358089 -0.967342  0.894291
6  0.490543  0.261311 -0.366073  0.435141
7 -0.566304  1.673199 -1.733883 -0.292425
8 -0.968336  0.648280 -0.489114 -2.275192
為高效處理金融事件序列資料,必須很好地處理時間索引,接下來通過date_range()函式對9行資料對應上時間,從2015-1-1開始,程式碼如下:
dates = pd.date_range('2015-1-1',periods=9,freq='M')
print dates
df.index = dates
print df
輸出結果如下所示,可以看到每行資料對應一個年份,其中freq參數列示頻率引數,常見的值包括:

B-交易日 D-日 W-每週 M-每月底 MS-月初 BM-每月最後一個交易日 A-每年底 H-每小時

DatetimeIndex(['2015-01-31', '2015-02-28', '2015-03-31', '2015-04-30',
               '2015-05-31', '2015-06-30', '2015-07-31', '2015-08-31',
               '2015-09-30'],
              dtype='datetime64[ns]', freq='M')
                 No1       No2       No3       No4
2015-01-31 -0.320854 -0.625805 -0.421955  0.389512
2015-02-28  0.370532 -1.221835  0.010364  1.393511
2015-03-31 -0.229514 -0.477147  0.128166 -0.619752
2015-04-30 -0.595702  1.799746  0.330161  1.669275
2015-05-31 -0.692837 -0.208061  0.576877  1.007649
2015-06-30 -1.021873 -0.358089 -0.967342  0.894291
2015-07-31  0.490543  0.261311 -0.366073  0.435141
2015-08-31 -0.566304  1.673199 -1.733883 -0.292425
2015-09-30 -0.968336  0.648280 -0.489114 -2.275192


3.繪圖操作
接著我們進行繪圖操作,Pandas提供了Matplotlib的一個封裝器,專門為Dataframe物件設計。程式碼如下:

# -*- coding: cp936 -*-
import pandas as pd
import numpy as np

a = np.random.standard_normal((9,4))
df = pd.DataFrame(a)
df.columns = ["No1", "No2", "No3", "No4"]
dates = pd.date_range('2015-1-1',periods=9,freq='M')
df.index = dates

print df.cumsum()
df.plot(lw=2.0)

主要呼叫plot方法,引數包括x、y、title、grid(表格線)、ax、legend、kind(圖形型別,kde/line/bar/barh)、logx、yticks(刻度)、xlim(界限)、rot(旋轉度)等,繪製圖形如下所示:



4.Series類
從DataFrame物件中選擇一列時,則得到一個Series物件,程式碼如下:

# -*- coding: cp936 -*-
import pandas as pd
import numpy as np

a = np.random.standard_normal((9,4))
df = pd.DataFrame(a)
df.columns = ["No1", "No2", "No3", "No4"]
dates = pd.date_range('2015-1-1',periods=9,freq='M')
df.index = dates
print df['No1']

import matplotlib.pyplot as plt
df['No1'].cumsum().plot(style="r",lw=2.)
plt.xlabel('date')
plt.ylabel('value')

輸出結果如下:

2015-01-31   -0.349526
2015-02-28   -0.511097
2015-03-31    0.987965
2015-04-30   -1.045693
2015-05-31    0.237728
2015-06-30   -0.857229
2015-07-31    0.916528
2015-08-31    1.240449
2015-09-30   -0.687802
Freq: M, Name: No1, dtype: float64

僅僅獲取了"No1"資料並繪製如下圖所示圖形:



5.Groupby操作
Pandas具有靈活分組功能,工作方式類似於SQL中分組和Excel透視表,為進行分組,我們新增一組索引對應季度表,程式碼如下:

# -*- coding: cp936 -*-
import pandas as pd
import numpy as np

a = np.random.standard_normal((9,4))
df = pd.DataFrame(a)
df.columns = ["No1", "No2", "No3", "No4"]
dates = pd.date_range('2015-1-1',periods=9,freq='M')
df.index = dates

df['Quarter'] = ['Q1','Q1','Q1','Q2','Q2','Q2','Q3','Q3','Q3']
print df
groups = df.groupby('Quarter')
print groups.sum()
print groups.mean()
print groups.max()
print groups.size()

輸出結果如下所示:

                 No1       No2       No3       No4 Quarter
2015-01-31  0.674688 -0.201858 -0.399297 -0.706358      Q1
2015-02-28 -0.480280 -1.091486  0.667307 -0.039749      Q1
2015-03-31 -0.427795 -0.362502 -0.885939 -1.580051      Q1
2015-04-30 -0.493025 -0.297891  0.748269 -0.128684      Q2
2015-05-31 -1.217889  0.474075  0.146949  0.840250      Q2
2015-06-30 -0.092169  0.541781  0.231801  0.647952      Q2
2015-07-31  0.364487  1.682923  1.561643  0.391411      Q3
2015-08-31  0.353252 -1.086157  0.849758 -0.435598      Q3
2015-09-30  0.805192 -0.434796  0.842751 -0.657201      Q3
              No1       No2       No3       No4
Quarter                                        
Q1      -0.233388 -1.655847 -0.617930 -2.326157
Q2      -1.803084  0.717965  1.127018  1.359518
Q3       1.522931  0.161970  3.254152 -0.701388
              No1       No2       No3       No4
Quarter                                        
Q1      -0.077796 -0.551949 -0.205977 -0.775386
Q2      -0.601028  0.239322  0.375673  0.453173
Q3       0.507644  0.053990  1.084717 -0.233796
              No1       No2       No3       No4
Quarter                                        
Q1       0.674688 -0.201858  0.667307 -0.039749
Q2      -0.092169  0.541781  0.748269  0.840250
Q3       0.805192  1.682923  1.561643  0.391411
Quarter
Q1    3
Q2    3
Q3    3
dtype: int64




三. 時間序列演算法-ARIMA


作者本來想通過下面程式碼匯入雅虎財經資料,但是沒有成功,最終選擇自定義資料進行ARIMA演算法實驗。

時間序列是通過曲線擬合和引數估計來建立 數學模型的理論方法,基本步驟如下:
(1).獲取被觀測系統時間序列資料;

(2).對資料繪圖觀測是否為平穩時間序列、非平穩d階差分;
(3).得平穩時間序列,求其自相關係數ACF和偏自相關係數PACF,通過自相關和偏相關圖分析,得到最佳階層p和結束q;
(4).由d、q、p得到ARIMA模型,然後進行檢驗。

參考:

Python_Statsmodels包_時間序列分析_ARIMA模型
Python時間序列分析 - 部落格園

1.獲取資料匯入庫

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

dta=[10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422, 
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355, 
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767, 
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232, 
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248, 
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085,14722, 
11999,9390,13481,14795,15845,15271,14686,11054,10395]

dta = np.array(dta,dtype=np.float) #這裡要轉下資料型別,不然執行會報錯
df = pd.Series(dta)
print df
dates = pd.date_range('2001', periods=90, freq='A')
df.index = dates
print df
df.plot(figsize=(12,8))
程式碼從2001年到2090年共有90組資料,然後按照年份進行時間序列統計(freq='A'年份),輸出結果如下所示:
2001-12-31    10930.0
2002-12-31    10318.0
2003-12-31    10595.0
2004-12-31    10972.0
2005-12-31     7706.0
....
2086-12-31    15845.0
2087-12-31    15271.0
2088-12-31    14686.0
2089-12-31    11054.0
2090-12-31    10395.0
Freq: A-DEC, Length: 90, dtype: float64

繪製圖形如下:



2.時間序列差分d
ARIMA模型要求是平穩型,如果是非平穩型的時間序列需要先做時間序列的差分,得到一個平穩的時間序列。如果時間序列做d次差分才能得到一個平穩序列,則可使用ARIMA(p,d,q)模型,其中d表示差分次數。程式碼如下:
主要呼叫df.diff(1)實現一階差分的效果,此資料一階和二階差分的結果類似,均值和方差基本問題,這裡的差分d值就取1。

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

dta=[10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422, 
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355, 
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767, 
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232, 
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248, 
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085,14722, 
11999,9390,13481,14795,15845,15271,14686,11054,10395]

dta = np.array(dta,dtype=np.float) #這裡要轉下資料型別,不然執行會報錯
df = pd.Series(dta)
print df
dates = pd.date_range('2001', periods=90, freq='A')
df.index = dates
print df
df.plot(figsize=(12,8))

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,8))
ax1= fig.add_subplot(111)
diff1 = df.diff(1)
diff1.plot(ax=ax1)
得到的平穩圖形如下圖所示:


注意:差分推導過程作者還在學習中,包括q和p值的計算,如果學會了後面會補充,這裡主要是程式碼的講解。

3.合適的q和p值
得到一個平穩的時間序列後,需要選擇合適的ARIMA模型,即ARIMA模型中的p和q值。
注意:這裡需要呼叫"pip install statsmodels"安裝統計數學分析的包,有時您的版本過低會導致錯誤(尤其是Anaconda 2.7版本),則需要呼叫"pip install --upgrade statsmodels"升級包至0.8版本。
程式碼如下:

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

dta=[10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422, 
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355, 
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767, 
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232, 
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248, 
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085,14722, 
11999,9390,13481,14795,15845,15271,14686,11054,10395]

dta = np.array(dta,dtype=np.float) #這裡要轉下資料型別,不然執行會報錯
df = pd.Series(dta)
print df
dates = pd.date_range('2001', periods=90, freq='A')
df.index = dates
print df
df.plot(figsize=(12,8))


import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,8))
ax1= fig.add_subplot(111)
diff1 = df.diff(1)
diff1.plot(ax=ax1)

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
f = plt.figure(facecolor='white')
ax1 = f.add_subplot(211)
plot_acf(df, lags=40, ax=ax1)
ax2 = f.add_subplot(212)
plot_pacf(df, lags=40, ax=ax2)
plt.show()

輸出結果如下所示,主要 呼叫plot_acf和plot_pacf函式。



注意:這裡根據上述自相關圖選擇引數,最終選擇ARIMA(7,1)模型或ARIMA(8,0)模型,作者也還在研究學習中,後面會補充知識,還請讀者原諒。最重要的是第四部分的預測,我認為它是時間序列預測的重點知識。




四. 時間序列預測分析


最後給出選擇ARIMA(8,0)模型對未來10年資料進行的程式碼。程式碼如下:

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

dta=[10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422, 
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355, 
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767, 
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232, 
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248, 
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085,14722, 
11999,9390,13481,14795,15845,15271,14686,11054,10395]

dta = np.array(dta,dtype=np.float) 
df = pd.Series(dta)
print df
dates = pd.date_range('2001', periods=90, freq='A')
df.index = dates
print df
df.plot(figsize=(12,8))

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,8))
ax1= fig.add_subplot(111)
diff1 = df.diff(1)
diff1.plot(ax=ax1)

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
f = plt.figure(facecolor='white')
ax1 = f.add_subplot(211)
plot_acf(df, lags=40, ax=ax1)
ax2 = f.add_subplot(212)
plot_pacf(df, lags=40, ax=ax2)
plt.show()


#預測結果
import statsmodels.api as sm
arma_mod80 =  sm.tsa.ARMA(df,(8,0)).fit()
print(arma_mod80.aic, arma_mod80.bic, arma_mod80.hqic)

pre = arma_mod80.predict('2090', '2100', dynamic=True)
print(pre)

fig, ax = plt.subplots(figsize=(12, 8))
ax = df.ix['2000':].plot(ax=ax)
fig = arma_mod80.plot_predict('2090', '2100', dynamic=True, ax=ax, plot_insample=False)
plt.show()
輸出相關ARIMA(8,0)係數和預測的2090-2100年結果如下所示:
(1597.9359982097687, 1622.9340949130715, 1608.0167002177614)
2090-12-31     9542.908069
2091-12-31    12908.529213
2092-12-31    13982.046108
2093-12-31    14501.674565
2094-12-31    13894.459886
2095-12-31    13249.595991
2096-12-31    10960.986520
2097-12-31    10073.503381
2098-12-31    12684.834790
2099-12-31    13477.793055
2100-12-31    13616.117709
Freq: A-DEC, dtype: float64
輸出圖形如下所示,可以看到後面綠色部分為預測值,根據前面的波動規律近似得到。


本篇文章為基礎性文章,希望對你有所幫助,提供些思路,如果文章中存在錯誤或不足之處,還請海涵。同時,推薦大家閱讀我以前的文章瞭解基礎知識,自己要學習的東西好多啊。

(By:Eastmount 2018-05-09 晚上11點  http://blog.csdn.net/eastmount/ )



相關文章