教你用Pandas玩轉時間序列數據

微信公衆號:「Python讀財」
如有問題或建議,請公衆號留言

進行金融數據分析或量化研究時,總避免不了時間序列數據的處理,時間序列是指在一定時間內按時間順序測量的某個變量的取值序列。常見的時間序列數據有一天內隨着時間變化的溫度序列,又或者交易時間內不斷波動的股票價格序列。Pandas也因其強大的時序處理能力而被廣泛應用於金融數據分析,這篇文章爲大家介紹一下Pandas中的時間序列處理,所使用的數據是上證指數2019年的行情數據。

數據.png

時間相關的數據類型

Pandas時序處理中最常見的兩種數據類型爲datetimetimedelta。一個datetime可以如下圖所示:

datetime.png

datetime顧名思義就是既有日期date也有時間time,表示一個具體的時間點(時間戳)。timedelta則表示兩個時間點之間的差,比如2020-01-012020-01-02之間的timedelta即爲一天,相信並不難理解。

將時間列轉換爲時間格式

大多數時候,我們是從csv文件中導入數據,此時Dataframe中對應的時間列是字符串的形式,如下:

In [5]: data.trade_date.head()
Out[5]:
0    20190102
1    20190103
2    20190104
3    20190107
4    20190108
Name: trade_date, dtype: object

運用pd.to_datetime(),可以將對應的列轉換爲Pandas中的datetime64類型,便於後期的處理

In [11]: data["trade_date"] = pd.to_datetime(data.trade_date)

In [12]: data.trade_date.head()
Out[12]:
0   2019-01-02
1   2019-01-03
2   2019-01-04
3   2019-01-07
4   2019-01-08
Name: trade_date, dtype: datetime64[ns]

時間序列的索引

時間序列中索引和Pandas普通的索引類似,大多時候調用.loc[index,columns]進行相應的索引,直接上代碼看看

In [20]: data1 = data.set_index("trade_date")

# 2019年6月的數據
In [21]: data1.loc["2019-06"].head()
Out[21]:
                close       open       high        low
trade_date
2019-06-03  2890.0809  2901.7424  2920.8292  2875.9019
2019-06-04  2862.2803  2887.6405  2888.3861  2851.9728
2019-06-05  2861.4181  2882.9369  2888.7676  2858.5719
2019-06-06  2827.7978  2862.3327  2862.3327  2822.1853
2019-06-10  2852.1302  2833.0145  2861.1310  2824.3554

# 2019年6月-2019年8月的數據
In [22]: data1.loc["2019-06":"2019-08"].tail()
Out[22]:
                close       open       high        low
trade_date
2019-08-26  2863.5673  2851.0158  2870.4939  2849.2381
2019-08-27  2902.1932  2879.5154  2919.6444  2879.4060
2019-08-28  2893.7564  2901.6267  2905.4354  2887.0115
2019-08-29  2890.9192  2895.9991  2898.6046  2878.5878
2019-08-30  2886.2365  2907.3825  2914.5767  2874.1028

提取出時間/日期的屬性

在時序數據處理過程中,經常需要實現下述需求:

  • 求某個日期對應的星期數(2019-06-05是第幾周)
  • 判斷一個日期是周幾(2020-01-01是周幾)
  • 判斷某一日期是第幾季度(2019-07-08屬於哪個季度)

……

當你數據中的時間列(本數據中爲trade_date列)已經轉換爲datetime64格式時,僅需調用.dt接口,即可快速求得想要的結果,下表中列出了.dt接口所提供的常見屬性:

dt表.png

具體演示一下(下面僅顯示2019-01-02的信息):

# 一年中的第幾天
In [13]: data.trade_date.dt.dayofweek[0]
Out[13]: 2

# 返回對應日期
In [14]: data.trade_date.dt.date[0]
Out[14]: datetime.date(2019, 1, 2)

# 返回週數
In [15]: data.trade_date.dt.weekofyear[0]
Out[15]: 1

# 返回周幾
In [16]: data.trade_date.dt.weekday_name[0]
Out[16]: 'Wednesday'

resample

resample翻譯過來是重採樣的意思,官方文檔中是這麼描述resample

resample() is a time-based groupby

翻譯過來就是基於時間groupby操作,我個人認爲這是Pandas時間序列處理中最重要的功能,也是本文的重中之重。

根據採樣是從低頻到高頻還是從高頻到低頻可以分爲升採樣降採樣兩種方式,先來看看降採樣是啥

  • 降採樣

以一個實例來引入,我們使用的數據是上證指數2019年的日級別數據,如果現在想求每季度的平均收盤價,應該怎麼操作呢?

從日級別數據求季度級別數據,是從高頻到低頻聚合操作,其實就類似於groupby按季度進行操作,用resample來寫是這樣子

In [32]: data.resample('Q',on='trade_date')["close"].mean()
Out[32]:
trade_date
2019-03-31    2792.941622
2019-06-30    3010.354672
2019-09-30    2923.136748
2019-12-31    2946.752270
Freq: Q-DEC, Name: close, dtype: float64

其中'Q'是以季度爲頻率進行採樣,on指定datetime列(如果索引爲Datetimeindex,則on不需要指定,默認依據索引進行降採樣)。整個過程圖解如下:

降採樣.png

整個過程其實就是一個groupby過程:

  • 對原有的數據按照指定的頻率進行切分,分到不同的group
  • 對不同的group執行操作
  • 整合操作結果

其中,切分的頻率可以爲任何時間頻率,可以爲季度Q、月度M、星期W、N天ND,也可以爲時H、分T,當然,如果切分後的頻率小於原有的時間頻率,就是我們下面要講的升採樣。

  • 升採樣

當採樣的頻率低於原有的頻率時,即爲升採樣。升採樣是對原有的時間粒度更爲細粒度的劃分,所以升採樣時會產生缺失值。下面取2019-01-022019-01-03的數據按照6H的頻率演示一下:

In [24]: example
Out[24]:
                close
trade_date
2019-01-02  2465.2910
2019-01-03  2464.3628

In [25]: example.resample('6H').asfreq()
Out[25]:
                         close
trade_date
2019-01-02 00:00:00  2465.2910
2019-01-02 06:00:00        NaN
2019-01-02 12:00:00        NaN
2019-01-02 18:00:00        NaN
2019-01-03 00:00:00  2464.3628

resample後的結果應用.asfreq()會返回新頻率下的結果。可以看到升採樣後產生了缺失值。如果想要填充缺失值可以採用向後填充.bfill()或向前填充.ffill()的方式:

# 向前填充,缺失值取2465.2910進行填充
In [29]: example.resample('6H').ffill()
Out[29]:
                         close
trade_date
2019-01-02 00:00:00  2465.2910
2019-01-02 06:00:00  2465.2910
2019-01-02 12:00:00  2465.2910
2019-01-02 18:00:00  2465.2910
2019-01-03 00:00:00  2464.3628

# 向後填充,缺失值取2464.3628進行填充
In [30]: example.resample('6H').bfill()
Out[30]:
                         close
trade_date
2019-01-02 00:00:00  2465.2910
2019-01-02 06:00:00  2464.3628
2019-01-02 12:00:00  2464.3628
2019-01-02 18:00:00  2464.3628
2019-01-03 00:00:00  2464.3628

總結一下resampleresample可以對原有的時間序列進行任何頻率freq的採樣,如果從低頻到高頻爲升採樣,高頻到低頻爲降採樣。整個操作過程和groupby基本一致,所以也可以對resample後的對象進行applytransform等操作,具體操作和原理這裏就不多解釋了,類比於groupby即可,參看這篇文章Pandas數據分析——超好用的Groupby詳解

掃碼關注公衆號 「Python讀財」,第一時間獲取乾貨!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章