根據相關知識完成下列任務:
- 求上個季度(僅含工作日)的平均值;
- 求每個月末(僅含工作日)的收盤價;
- 遷移數據
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
等。時間頻率組合使用:
In[0]:pd.timedelta_range(0,periods=9,freq="2H30T")
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
模塊中找到,比如直接創建一個工作日偏移序列:
In[1]:from pandas.tseries.offsets import BDay
pd.date_range('2015-07-01', periods=5, freq=BDay())
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
程序包(需要手動安裝)導入
In[2]: from pandas_datareader import data
goog = data.DataReader('GOOG',start="2014",end="2016",data_source="google")['Close'] # Close表示收盤價的列
In[3]: import matplotlib.pyplot as plt
import seaborn; seaborn.set()
goog.plot(); #數據可視化
輸出:
我們可以通過
resample()
方法和asfreq()
方法解決這個問題,resample()
方法是以數據累計爲基礎,而asfreq()
方法是以數據選擇爲基礎。
In[4]: goog.plot(alpha=0.5, style='-')
goog.resample('BA').mean().plot(style=':')
goog.asfreq('BA').plot(style='--');
plt.legend(['input', 'resample', 'asfreq'], loc='upper left');
輸出:
請注意這兩種取樣方法的差異:在每個數據點上,
resample
反映的是上一年的均值,而asfreq
反映的是上一年最後一個工作日的收盤價。數據集中經常會出現缺失值,從上面的例子來看,由於週末和節假日股市休市,週末和節假日就會產生缺失值,上面介紹的兩種方法默認使用的是向前取樣作爲缺失值處理。與前面介紹過的
pd.fillna()
函數類似,asfreq()
有一個method
參數可以設置填充缺失值的方式。
In[5]:fig, ax = plt.subplots(2, sharex=True)
data = goog.iloc[:10]
data.asfreq('D').plot(ax=ax[0], marker='o')
data.asfreq('D', method='bfill').plot(ax=ax[1], style='-o')
data.asfreq('D', method='ffill').plot(ax=ax[1], style='--o')
ax[1].legend(["back-fill", "forward-fill"]);
輸出:
另一種常用的時間序列操作是對數據按時間進行遷移,
Pandas
有兩種解決這類問題的方法:shift()
和tshift()
。簡單來說,shift()
就是遷移數據,而tshift()
就是遷移索引。兩種方法都是按照頻率代碼進行遷移。下面我們將用
shift()
和tshift()
這兩種方法讓數據遷移900
天:
In[6]:fig, ax = plt.subplots(3, sharey=True)
# 對數據應用時間頻率,用向後填充解決缺失值
goog = goog.asfreq('D', method='pad')
goog.plot(ax=ax[0])
goog.shift(900).plot(ax=ax[1])
goog.tshift(900).plot(ax=ax[2])
# 設置圖例與標籤
local_max = pd.to_datetime('2007-11-05')
offset = pd.Timedelta(900, 'D')
ax[0].legend(['input'], loc=2)
ax[0].get_xticklabels()[4].set(weight='heavy', color='red')
ax[0].axvline(local_max, alpha=0.3, color='red')
ax[1].legend(['shift(900)'], loc=2)
ax[1].get_xticklabels()[4].set(weight='heavy', color='red')
ax[1].axvline(local_max + offset, alpha=0.3, color='red')
ax[2].legend(['tshift(900)'], loc=2)
ax[2].get_xticklabels()[1].set(weight='heavy', color='red')
ax[2].axvline(local_max + offset, alpha=0.3, color='red');
輸出:
shift(900)
將數據向前推進了900
天,這樣圖形中的一段就消失了(最左側就變成了缺失值),而tshift(900)
方法是將時間索引值向前推進了900
天。
Pandas
處理時間序列數據的第3
種操作是移動統計值。這些指標可以通過Series
和DataFrame
的rolling()
屬性來實現,它會返回與groupby
操作類似的結果,移動視圖使得許多累計操作成爲可能,rolling
函數的具體參數可以查看其 官方文檔 。
In[7]: rolling = goog.rolling(365, center=True)
data = pd.DataFrame({'input': goog, 'one-year rolling_mean': rolling.mean(), 'one-year rolling_std': rolling.std()})
ax = data.plot(style=['-', '--', ':'])
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")