Educoder python Pandas高效化運算與時間序列處理 第3關:Pandas時間序列的高級應用


任務描述

根據相關知識完成下列任務:

  • 求上個季度(僅含工作日)的平均值;
  • 求每個月末(僅含工作日)的收盤價;
  • 遷移數據365天;
  • 求一年期移動標準差。

相關知識

學習本關卡知識之前,建議先了解matplotlib模塊基礎知識。

Pandas時間序列工具的基礎是時間頻率或偏移量代碼。就像之前見過的D(day)H(hour)代碼,我們可以用這些代碼設置任意需要的時間間隔。

Pandas頻率代碼表如下:

代碼 描述
D 天(calendar day,按日曆算,含雙休日)
W 周(weekly) M 月末(month end
Q 季末(quarter end
A 年末(year end
H 小時(hours
T 分鐘(minutes
S 秒(seconds
L 毫秒(milliseonds
U 微秒(microseconds
N 納秒(nanoseconds
B 天(business day,僅含工作日)
BM 月末(business month end,僅含工作日)
BQ 季末(business quarter end,僅含工作日)
BA 年末(business year end,僅含工作日)
BH 小時(business hours,工作時間)
MS 月初(month start
BMS 月初(business month start,僅含工作日)
QS 季初(quarter start
BQS 季初(business quarter start,僅含工作日)
AS 年初(year start
BAS 年初(business year start,僅含工作日)

時間頻率與偏移量

我們可以在頻率代碼後面加三位月份縮寫字母來改變季、年頻率的開始時間,也可以再後面加三位星期縮寫字母來改變一週的開始時間:

  • Q-JAN、BQ-FEB、QS-MAR、BQS-APR 等;

  • W-SUN、W-MON、W-TUE、W-WED 等。

時間頻率組合使用:


 
  1. In[0]:pd.timedelta_range(0,periods=9,freq="2H30T")
  2. Out[0]:TimedeltaIndex(['00:00:00', '02:30:00', '05:00:00', '07:30:00', '10:00:00', '12:30:00', '15:00:00', '17:30:00', '20:00:00'], dtype='timedelta64[ns]', freq='150T')

所有這些頻率代碼都對應Pandas時間序列的偏移量,具體內容可以在 pd.tseries.offsets 模塊中找到,比如直接創建一個工作日偏移序列:


 
  1. In[1]:from pandas.tseries.offsets import BDay
  2. pd.date_range('2015-07-01', periods=5, freq=BDay())
  3. Out[1]:DatetimeIndex(['2015-07-01', '2015-07-02', '2015-07-03', '2015-07-06', '2015-07-07'], dtype='datetime64[ns]', freq='B')

重新取樣、遷移和窗口

重新取樣

處理時間序列數據時,經常需要按照新的頻率(更高頻率、更低頻率)對數據進行重新取樣。舉個例子,首先通過pandas—datareader程序包(需要手動安裝)導入Google的歷史股票價格,只獲取它的收盤價:


 
  1. In[2]: from pandas_datareader import data
  2. goog = data.DataReader('GOOG',start="2014",end="2016",data_source="google")['Close'] # Close表示收盤價的列
  3. In[3]: import matplotlib.pyplot as plt
  4. import seaborn; seaborn.set()
  5. goog.plot(); #數據可視化

輸出:

我們可以通過resample()方法和asfreq()方法解決這個問題,resample()方法是以數據累計爲基礎,而asfreq()方法是以數據選擇爲基礎。


 
  1. In[4]: goog.plot(alpha=0.5, style='-')
  2. goog.resample('BA').mean().plot(style=':')
  3. goog.asfreq('BA').plot(style='--');
  4. plt.legend(['input', 'resample', 'asfreq'], loc='upper left');

輸出:

請注意這兩種取樣方法的差異:在每個數據點上,resample反映的是上一年的均值,而asfreq反映的是上一年最後一個工作日的收盤價

數據集中經常會出現缺失值,從上面的例子來看,由於週末和節假日股市休市,週末和節假日就會產生缺失值,上面介紹的兩種方法默認使用的是向前取樣作爲缺失值處理。與前面介紹過的pd.fillna()函數類似,asfreq()有一個method參數可以設置填充缺失值的方式。


 
  1. In[5]:fig, ax = plt.subplots(2, sharex=True)
  2. data = goog.iloc[:10]
  3. data.asfreq('D').plot(ax=ax[0], marker='o')
  4. data.asfreq('D', method='bfill').plot(ax=ax[1], style='-o')
  5. data.asfreq('D', method='ffill').plot(ax=ax[1], style='--o')
  6. ax[1].legend(["back-fill", "forward-fill"]);

輸出:

時間遷移

另一種常用的時間序列操作是對數據按時間進行遷移,Pandas有兩種解決這類問題的方法:shift()tshift()。簡單來說,shift()就是遷移數據,而 tshift()就是遷移索引。兩種方法都是按照頻率代碼進行遷移。

下面我們將用shift()tshift()這兩種方法讓數據遷移900天:


 
  1. In[6]:fig, ax = plt.subplots(3, sharey=True)
  2. # 對數據應用時間頻率,用向後填充解決缺失值
  3. goog = goog.asfreq('D', method='pad')
  4. goog.plot(ax=ax[0])
  5. goog.shift(900).plot(ax=ax[1])
  6. goog.tshift(900).plot(ax=ax[2])
  7. # 設置圖例與標籤
  8. local_max = pd.to_datetime('2007-11-05')
  9. offset = pd.Timedelta(900, 'D')
  10. ax[0].legend(['input'], loc=2)
  11. ax[0].get_xticklabels()[4].set(weight='heavy', color='red')
  12. ax[0].axvline(local_max, alpha=0.3, color='red')
  13. ax[1].legend(['shift(900)'], loc=2)
  14. ax[1].get_xticklabels()[4].set(weight='heavy', color='red')
  15. ax[1].axvline(local_max + offset, alpha=0.3, color='red')
  16. ax[2].legend(['tshift(900)'], loc=2)
  17. ax[2].get_xticklabels()[1].set(weight='heavy', color='red')
  18. ax[2].axvline(local_max + offset, alpha=0.3, color='red');

輸出:

shift(900)將數據向前推進了900天,這樣圖形中的一段就消失了(最左側就變成了缺失值),而tshift(900)方法是將時間索引值向前推進了900天。

移動時間窗口

Pandas處理時間序列數據的第3種操作是移動統計值。這些指標可以通過 SeriesDataFramerolling()屬性來實現,它會返回與groupby操作類似的結果,移動視圖使得許多累計操作成爲可能,rolling函數的具體參數可以查看其 官方文檔


 
  1. In[7]: rolling = goog.rolling(365, center=True)
  2. data = pd.DataFrame({'input': goog, 'one-year rolling_mean': rolling.mean(), 'one-year rolling_std': rolling.std()})
  3. ax = data.plot(style=['-', '--', ':'])
  4. ax.lines[0].set_alpha(0.3)

輸出:

編程要求

本關的編程任務是補全右側上部代碼編輯區內的相應代碼,要求實現如下功能:

  • 求上個季度(僅含工作日)的平均值;
  • 求每個月末(僅含工作日)的收盤價;
  • 遷移數據365天;
  • 求一年期移動標準差;
  • 具體要求請參見後續測試樣例。

請先仔細閱讀右側上部代碼編輯區內給出的代碼框架,再開始你的編程工作!

測試說明

平臺會對你編寫的代碼進行測試,對比你輸出的數值與實際正確的數值,只有所有數據全部計算正確才能進入下一關。

測試輸入:

無測試輸入

預期輸出:

import matplotlib.pyplot as plt
import pandas as pd
def demo():
    yahoo = pd.read_csv("./step3/yahoo_data.csv")
    yahoo.set_index(pd.to_datetime(yahoo["Date"]),inplace=True)
    # 取雅虎股票的收盤價
    yh = yahoo["Close"]
    fig, ax = plt.subplots(2, sharex=True)
    # 畫出收盤價的圖
    yh.plot(ax=ax[0], style="-")
    # 求上個季度(僅含工作日)的平均值
    # ********** Begin ********** #
    data1 = yh.resample("BQ").mean()
    # ********** End ********** #
    data1.plot(ax=ax[0], style=":")
    # 求每個月末(僅含工作日)的收盤價
    # ********** Begin ********** #
    data2 = yh.asfreq("BM")
    # ********** End ********** #
    data2.plot(ax=ax[0], style="--", color="red")
    ax[0].legend(['input', 'resample', 'asfreq'], loc='upper right')
    # 遷移數據365天
    # ********** Begin ********** #
    data3 = yh.shift(365)
    # ********** End ********** #
    data3.plot(ax=ax[1])
    data3.resample("BQ").mean().plot(ax=ax[1], style=":")
    data3.asfreq("BM").plot(ax=ax[1], style="--", color="red")
    # 設置圖例與標籤
    local_max = pd.to_datetime('2007-11-05')
    offset = pd.Timedelta(365, 'D')
    ax[0].axvline(local_max, alpha=0.3, color='red')
    ax[1].axvline(local_max + offset, alpha=0.3, color='red')
    # 求一年期移動標準差
    # ********** Begin ********** #
    rolling = yh.rolling(365)
    data4 = rolling.std()   
    # ********** End ********** #
    data4.plot(ax=ax[1], style="y:")
    data4.plot(ax=ax[0], style="y:")
    plt.savefig("./step3/result/2.png")

 

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