《python for data analysis》第十章,時間序列

來源:互聯網
上載者:User

標籤:比較   ted   star   utf-8   工作日   expand   構造   [1]   儲存   

《 python for data analysis 》一書的第十章常式, 主要介紹時間序列(time series)資料的處理。
label:
1. datetime object、timestamp object、period object
2. pandas的Series和DataFrame object的兩種特殊索引:DatetimeIndex 和 PeriodIndex
3. 時區的表達與處理
4. imestamp object、period object的頻率概念,及其頻率轉換
5. 兩種頻率轉換:單個時間object——asfreq;以時間object為索引的時間序列——resample
6. 時間序列的移動視窗(rolling)

# -*- coding:utf-8 -*-
# 《python for data analysis》 第十章
# 時間序列
import pandas as pd
import numpy as np
import time
from datetime import datetime, timedelta
import matplotlib.pyplot as plt

start = time.time()
np.random.seed(10)

# 1、日期和時間資料類型及工具
# datetime以 year - month - date hour : minute : second 的格式儲存時間
tnow = datetime.now()
print(tnow)
# 可調用datetime中的各個部分:年月日時分秒
print(tnow.year)
print(tnow.day)
# datetime.timedelta 可用於表示兩個 datetime object之間的時間差, 以日,秒,毫秒的形式
print(tnow - timedelta(1, 10, 1000)) # 1天10秒100微秒之前
print(‘\n‘)
# 1.1、字串和datetime的相互轉換
# (1)str和strftime可以將datetime object轉換成字串
print(str(tnow)) # 按照年月日時分秒的格式全部轉換
print(tnow.strftime(‘%Y-%m-%d‘)) # 按照年月日格式匯出
print(‘‘)
# (2)strptime可以將字串轉換成datetime object
print(datetime.strptime(‘1999/1/2‘, ‘%Y/%m/%d‘))
print(datetime.strptime(‘1999/1/2‘, ‘%Y/%d/%m‘))
print(‘‘)
# datetime.strptime方法進行時間格式解析最為精準,但是需要自訂格式。
# 第三方包dateutil中的parser.parse方法可自適應地去進行時間格式解析,但有些時候可能會出現問題
from dateutil.parser import parse

print(‘Jan 22, 1999, 11:23, pm‘)
print(parse(‘Jan 22, 1999, 11:23, pm‘))
print(‘‘)
# 上述方法均是對單個字串進行解析,pandas的to_datetime方法可以成組解析
timestring = [‘2012/1/1‘, ‘2008/8/8‘]
df = pd.to_datetime(timestring)
print(df)
# pd.to-datetime還可以對缺失值進行處理,自動轉成NaT(not a time)
timestring = timestring + [None]
df = pd.to_datetime(timestring)
print(df)
print(‘------------------------------------------------↑, section1\n\n‘)
# 2、時間序列基礎
# pandas最基本的時間序列類型是以時間戳記(timestamp)為索引(index)的Series
dates = [datetime.strptime(‘2000/1/‘ + str(i), ‘%Y/%m/%d‘) for i in range(1, 11)]
print(dates)
ts = pd.Series(np.random.randn(10), index=dates)
print(ts)
print(type(ts.index[1])) # time series中的每個index均為timestamp object
print(‘‘)
# 2.1、索引、選取、子集構造(time series的)
# time series也是series的一種,所以原來的series的各種索引、切片方法等均適用
print(ts[2:3])
print(‘‘)
# 特別的,time series還可以通過日期(字串)進行索引
print(ts[‘2000/01/05‘])
# 對於時間跨度很大的時間序列,切片方式更加豐富
print(‘\n‘)
ts = pd.Series(np.random.randn(1000), index=pd.date_range(‘1/1/2000‘, periods=1000))
print(ts.describe())
# 通過年份切片
print(ts[‘2001‘].describe())
# 通過年月切片
print(ts[‘2001/01‘].describe())
# 時間段切片
print(ts[‘2002/05/01‘:‘2002/05/06‘])
print(‘\n‘)
# 上述索引、切片方法對dataframe同樣適用
# 2.2、帶有重複索引的時間序列
date = [‘2001/02/01‘, ‘2001/02/01‘, ‘2001/02/02‘]
ts = pd.Series(range(3), index=date)
print(ts)
print(ts[‘2001/02/01‘]) # 重複索引返回切片
print(ts[‘2001/02/02‘]) # 非重複索引返回標量值
print(‘‘)
# 通過彙總可以消除重複索引
print(ts.groupby(level=0).count())
print(‘---------------------------------↑, section2\n\n‘)
# 3、日期的範圍、頻率以及移動
# resample的詳細介紹見section 6
# 3.1、產生日期範圍
# 用pd.date_range產生指定長度的DateTime Index
# (1)給定起止時間
index = pd.date_range(‘2000/1/1‘, ‘2000/2/1‘)
print(index)
print(‘‘)
# (2)給定起始時間(或終止時間)加時間長度
index = pd.date_range(start=‘2000/1/1‘, periods=10) # 從2000/1/1開始,長度10天
print(index)
print(‘‘)
index = pd.date_range(end=‘2000/1/28‘, periods=10) # 到2000/1/28為止,長度10天
print(index)
# 上述函數中為給出產生時間序列的間隔,為預設值1天,通過freq關鍵字可以顯式指定
print(‘‘)
print(pd.date_range(‘2000/1/1‘, ‘2000/12/28‘, freq=‘BM‘)) # BM表示每個月的最後一個工作日
# freq的可選項:D(每天)、B(每工作日)、H(每小時)、T or min(每分鐘)、S(每秒)……P314~315
# 3.2、頻率與日期位移量
# 上述提到的頻率代碼可以 以字串形式 自由組合
print(‘‘)
print(pd.date_range(‘2000/1/10‘, ‘2000/1/15‘, freq=‘6H30T10S‘)) # 以6小時30分鐘10秒的間隔產生時間序列
# WOM日期(week of month)可以表示每月中的某些日子
print(‘‘)
print(pd.date_range(‘2000/1/10‘, ‘2001/1/10‘, freq=‘WOM-3FRI‘)) # 每個月的第三個周五
# 3.3、移動(超前或滯後)資料
print(‘\n‘)
ts = pd.DataFrame(
{‘a‘: range(6),
‘b‘: np.random.randn(6)}
)
ts.index = pd.date_range(‘2000/10/1‘, ‘2000/10/6‘)
print(ts)
print(ts.shift(2)) # 資料往時間增大方向推移2天
print(ts.shift(-2)) # 往小了方向推移
# 上述函數中僅傳入推移大小,則移動資料
# 若還傳入頻率,則移動time index
print(ts.shift(1, freq=‘M‘)) # 資料不動,時間序列全部推移到本月最後一天
print(ts.shift(1, freq=‘3D‘)) # 資料不動,時間序列全部推移3天
# shift最大的功用在於可以計算資料的百分比變化
print(‘‘)
print(ts / ts.shift(1) - 1)
# 時間位移量還可以直接作用於timestamp和datetime object
# 先匯入時間位移量
from pandas.tseries.offsets import Day, BMonthBegin

print(tnow + 3 * Day()) # 向時間前進方向位移3天
print(tnow + BMonthBegin()) # 位移至下個月的第一個工作日
print(‘------------------------------------↑, section 3\n\n‘)

# 4、時區處理
# UTC,國際標準時間間。時區以UTC的位移量表示
# 4.1、本地化與轉換
# 先按照之前的方法建立一個time series
ts = pd.Series(range(2))
ts.index = pd.date_range(tnow, periods=2, normalize=True)
print(ts)
# 上述建立過程中未指定時區,則預設時區未None
print(‘\ntime zone is %s‘ % ts.index.tz)
# 給時間序列加上時區資料,稱為本地化。有兩種方法。
# (1)tz_localize方法
ts_china = ts.tz_localize(‘Asia/Shanghai‘)
print(ts_china) # time index 變成 本地時間+UTC位移 的格式
print(ts_china.index.tz) # 該時間序列的時區已經附上上海
# (2)在建立序列時直接顯示指定時區
ts_china = pd.Series(range(2), index=pd.date_range(tnow, periods=2, normalize=True, tz=‘Asia/Shanghai‘))
print(ts_china)
print(ts_china.index.tz)
# 時區可以通過tz_convert方法進行轉換
print(ts_china.tz_convert(‘UTC‘)) # 北京時間轉換成UTC時間
print(‘\n‘)
# datetime、timestamp、DatetimeIndex這些objects均可以使用tz_localize、tz_convert這些方法
# 4.2、操作時區意識型TimeStamp Object
stamp = pd.Timestamp(str(tnow))
stamp_china = stamp.tz_localize(‘Asia/Shanghai‘)
print(stamp_china)
stamp_utc = stamp_china.tz_convert(‘utc‘)
print(stamp_utc)
# timestamp object 中有一個屬性儲存了utc時間戳記值,即目前時間相對於UNIX紀元(1970年1月1日)的時間位移,以ns為單位
print(stamp_utc.value)
print(stamp_china.value) # 這兩個時間為stamp在不同時區的顯示時間,故絕對位移相等,均為stamp相對UNIX紀元的時間位移
print(‘\n‘)
# 4.3、不同時區之間的運算
# 不同時區的時間序列的運算結果均以UTC標準顯示
ts = pd.Series(range(2), index=pd.date_range(tnow, periods=2))
ts1 = ts.tz_localize(‘US/Eastern‘)
ts2 = ts.tz_localize(‘Asia/Shanghai‘)
print(ts1.index)
print(ts2.index)
print((ts1 + ts2).index)
print(‘----------------------------------↑, section4\n\n‘)

# 5、時期及其算術運算
# 一種新的object,時期(period)
# timestamp object表示某一時刻,相對的,period object是用來表示某一段周期的
# 建立一個period object
p = pd.Period(2000, freq=‘A-DEC‘) # 以12月結尾的周期年
print(p)
# period object支援加減整數實現位移
print(p - 2) # 2000年往前推2年
print(p + 1) # 2000年的後面一年
# 同頻率的period還支援加減
print(pd.Period(2005, freq=‘M‘) - pd.Period(2000, freq=‘M‘)) # 5年共60個月
# period_range方法可以建立一組時期範圍
plist = pd.period_range(‘2000Q1‘, ‘2002Q1‘, freq=‘Q‘) # 從2000年1季度到2002年1季度,季度間隔
print(plist)
print(‘‘)
# 以datetime object為內容的index稱為datetime index,同理,也有period index
ts = pd.Series(np.random.randn(len(plist)), index=plist)
print(ts)
print(‘‘)
# period index的構造還可以通過字串轉換完成
string = [‘2000Q1‘, ‘2000Q2‘, ‘2000Q3‘] # 2000年的前3季度
p = pd.PeriodIndex(string, freq=‘Q-DEC‘)
print(p)
print(‘\n‘)
# 5.1、時期的頻率轉換
# period 和 period index 均可通過asfreq進行頻率轉換
p = pd.Period(2000, freq=‘Q-DEC‘)
print(p) # 2000年第一季度
print(p.asfreq(‘M‘)) # 季轉月,預設最後一個月
print(p.asfreq(‘M‘, how=‘start‘)) # 用how關鍵字顯式指定第一個月
print(p.asfreq(‘A‘)) # 季轉年
print(‘‘)
# 同理,period index或者包含period index的time series也可以這麼操作
print(ts.asfreq(‘B‘, how=‘start‘)) # ts時間序列中的period index(以季度為時期)轉成以每季裡面第一個工作日為時期的period index
print(‘‘)
# 5.2、按季度計算的時期頻率
# Q-MAY 中的 MAY 表示該年末為五月,即6-8一個季度,9-11一個季度,12-2一個季度,3-5一個季度。 其餘表示以此類推
# 單個 period object 和 一組period(如period index)均可通過運算來表示某個時刻,並通過to_timestamp方法變成timestamp object
a = pd.Period(‘2000Q1‘, freq=‘Q‘)
print(a)
# 通過period運算將2000年第一季度轉換為2000年第一季度的倒數第三個工作日的上午9點30分的時間戳記
tstamp = ((a.asfreq(‘B‘, ‘e‘) - 2).asfreq(‘H‘, ‘s‘) + 9).asfreq(‘T‘, ‘s‘) + 30
print(tstamp) # 還是一個period object,不過是分鐘級的period
print(tstamp.to_timestamp()) # period 2 timestamp
print(‘‘)
# 5.3、將timestamp轉換為period(及其反向過程)
# (1)to_period方法可以將timestamp轉換為period
stamp = pd.date_range(‘2000/12/1‘, periods=2, freq=‘D‘)
print(stamp.to_period(‘M‘)) # 指定period的頻率為月
# (2)to_timestamp方法可以將period轉換為timestamp
print(stamp.to_period(‘M‘).to_timestamp())
print(‘‘)
# 5.4、通過數組建立period index
# 很多時候時間資料是分成幾個子部分(如年、月、日)存放在一張表格的某幾列中的,PeriodIndex方法可以合并這樣的列匯成一個period index
df = pd.DataFrame()
df[‘year‘] = [2000] * 4 + [2001] * 4
df[‘month‘] = range(1, 9)
df[‘day‘] = range(22, 30)
print(df)
tindex = pd.PeriodIndex(year=df[‘year‘], month=df[‘month‘], day=df[‘day‘], freq=‘D‘)
print(tindex)
print(‘---------------------------------------↑, section 5 \n\n‘)

# 6、重採樣與頻率轉換
# 重採樣是指從一個頻率變換到另一種頻率的過程,這裡的頻率並不是傳統意義上的頻率,而是指時間序列相關函數中freq關鍵字
# 重採樣分3種:
# 1、升採樣,頻率變大,或者說period周期變小,如Q採樣成D(季度->天)
# 2、降採樣,頻率變小,或者說period周期變大,如Q採樣成A(季度->年)
# 3、同period周期的採樣,如W-MON採樣成W-WED(每周一->每周三)
# 重採樣通過resample方法實現
# 6.1、降採樣
# 先產生一個分鐘序列資料
ts = pd.Series(range(15), index=pd.date_range(‘2010/10/1‘, periods=15, freq=‘T‘))
print(ts)
print(‘‘)
# resample方法需要指定區間哪一邊為閉區間(相應另一邊為開區間),需要指定區間以左右哪個邊界進行命名
# 在0.22.0版本(更高版本估計也類似)的pandas種,預設情況比較複雜,視freq不同而不同,所以保險一點還是用關鍵字顯示指定。
# 可通過closed關鍵字修改哪一邊為閉區間,可通過label關鍵字指定以區間的哪個邊界進行區間命名
print(ts.resample(‘10min‘, closed=‘left‘, label=‘left‘).count())
print(‘‘)
print(ts.resample(‘10min‘, closed=‘right‘, label=‘right‘).count())
# 為更清楚地顯示區間,可使用loffset關鍵字進行位移。其實也可以通過對整個time series進行shift實現相同的功能。
print(‘‘)
print(ts.resample(‘10min‘, closed=‘right‘, label=‘right‘, loffset=‘-1s‘).count())
# 特別地,降採樣中有一種採樣稱為OHLC採樣,特定用於金融資料,計算出每個區間的開盤價(open),收盤價(close),最高價(high),最低價(low)
print(‘‘)
print(ts.resample(‘5min‘, closed=‘right‘, label=‘right‘).ohlc())
# 另一種實現降採樣的方法是通過groupby方法實現,和resample各有適用情景
# 按照周幾進行分組
ts = pd.Series(range(100), index=pd.date_range(‘2000/10/10‘, periods=100, freq=‘D‘))
print(ts)
print(‘‘)
print(ts.groupby(lambda t: t.weekday).count())
# 6.2、升採樣和插值
# 從大尺度採樣到小尺度,自然而然會引入數值缺失的問題,故升採樣需要插值處理
# 先建立一個周頻率的時間序列
print(‘‘)
ts = pd.Series([1, 2], index=pd.date_range(‘2000/1/21‘, periods=2, freq=‘W-FRI‘)) # 從2000/1/21開始的兩個周五
print(ts)
# 將ts升採樣到日頻率
# 若不插值則會出現數值缺失,可通過前向插值(ffill)和後向插值(bfill)進行插值
print(‘‘)
print(ts.resample(‘D‘).ffill())
print(ts.resample(‘D‘).bfill())
# resample還可以實現既非升採樣也非降採樣
# 如將上述每周5的資料重採樣到每周1
print(‘‘)
print(ts.resample(‘W-MON‘).ffill())
print(ts.resample(‘W-MON‘).bfill())
# 6.3、通過時期進行重採樣
# 上述重採樣都是對timestamp index的series進行的,重採樣也可以對period index的series進行
# (1)period的降採樣和timestamp一樣,直接對其應用resample方法即可
# 先構造一個period index的時間序列
print(‘‘)
ts = pd.Series(range(10), index=pd.period_range(‘2010/1‘, periods=10, freq=‘M‘))
print(ts)
# 然後在resample中傳入一個更大周期的freq即可完成降採樣
ts = ts.resample(‘Q‘).sum()
print(ts)
print(‘‘)
# (2)period的升採樣,需要指定新區間中哪一端存放原來的值
# 比如,年到季度的升採樣,原來的值放到第一個季度還是最後一個季度需要指定
# 通過關鍵字convention進行指定,‘end‘則放到最後一個季度,‘start‘則放到第一個季度。預設為‘start‘
print(ts.resample(‘M‘, convention=‘end‘).ffill())
print(‘‘)
print(ts.resample(‘M‘).ffill())
print(‘--------------------------↑, section 6\n\n‘)

# 7、時間序列繪圖
# pandas的時間序列資料可直接用plot()方法進行繪圖,基於matplotlib包進行過處理的
# 匯入資料,1990年至2010年的幾隻美股資料
stk = pd.read_csv(‘./data_set/stock_px.csv‘, parse_dates=True, index_col=0)
stk = stk[[‘AAPL‘, ‘MSFT‘, ‘SPX‘]] # 從中取出3隻股票
stk = stk.resample(‘B‘).ffill() # 按工作日頻率進行重採樣,實現規則頻率
print(stk.describe())
# 通過切片直接應用plot方法可完成時間序列的作圖
fig, axes = plt.subplots(2, 2) # figure1
stk[‘AAPL‘].plot(ax=axes[0, 0]) # 品種切片
stk.ix[‘2005‘].plot(ax=axes[0, 1]) # 時間切片
stk[‘AAPL‘].ix[‘06/2006‘:‘08/2008‘].plot(ax=axes[1, 0]) # 雙重切片
# 還可以對原資料重採樣成季度資料,再作圖
stk[‘AAPL‘].resample(‘Q-DEC‘).ffill().plot(ax=axes[1, 1]) # 預設為每個季度最後一個工作日,若資料缺失則用前一天的資料填充
# plt.show() # 將這一行取消注釋,使能作圖功能
print(‘-------------------------------------↑, section 7\n\n‘)

# 8、移動視窗函數
# 移動視窗函數,用於在一個長序列中切割出一個子視窗進行相關量的統計
# 在金融資料中移動視窗應用較多,典型地,N日均線
# 對APPLE股價作250日移動平均
fig2, axes2 = plt.subplots(2, 2) # figure2
stk[‘AAPL‘].plot(ax=axes2[0, 0])
pd.Series.rolling(stk[‘AAPL‘], 250).mean().plot(ax=axes2[0, 0]) # 高版本推薦寫法,不同於書上常式
# 當rolling接受的資料很少時,將不返回移動平均值,通過min_periods關鍵字可以指定這個閾值
stk[‘AAPL‘].plot(ax=axes2[0, 1])
pd.Series.rolling(stk[‘AAPL‘], 250, min_periods=10).mean().plot(ax=axes2[0, 1]) # 最少有10個非NA值就返回移動平均
# 圖(0,0)和圖(0,1)的區別體現在圖(0,1)更快出現250日均線
# 通過rolling還可以延申成 擴充視窗平均, 即視窗長度可變,相當於時間序列長度
expanding_mean = lambda ts: pd.Series.rolling(ts, len(ts), min_periods=1).mean()
expanding_mean(stk[‘AAPL‘]).plot(ax=axes2[1, 0]) # 全長度均線
stk[‘AAPL‘].plot(ax=axes2[1, 0])
# 8.1、指數加權函數
# 移動視窗常搭配一個衰減因子使用,用於使近期的觀測值有更大的權重,從而更快體現原資料的變化
# 通過ewm方法實現
ma = pd.Series.rolling(stk[‘AAPL‘], 250, min_periods=50).mean() # 均權的年均線
ewma = pd.Series.ewm(stk[‘AAPL‘], span=250).mean()
ma.plot(ax=axes2[1, 1], style=‘--‘)
ewma.plot(ax=axes2[1, 1], style=‘:‘) # 通過圖可以看到帶有衰減因子的均線更快出現拐點(反應更快)
# 8.2、二元移動視窗函數
# 某些統計變數用到兩個資料,比如相關係數
spx = stk[‘SPX‘]
aapl = stk[‘AAPL‘]
# 兩種方法計算股價的百分數變化
spx_pctc = spx / spx.shift(1) - 1
aapl_pctc = stk[‘AAPL‘].pct_change()
fig3, axes3 = plt.subplots(2, 2) # figure3
# 計算兩者的移動視窗裡的相關係數
corr = pd.Series.rolling(spx_pctc, window=125, min_periods=100).corr(aapl_pctc) # 6個月視窗期,移動相關係數
corr.plot(ax=axes3[0,0])
# 很多時候會以唯一資料作為標準,計算其餘資料與標準資料的相關係數
# 此時,傳入Series(標準資料)與DataFrame(其餘資料)即可
corr = pd.DataFrame.rolling(stk[[‘AAPL‘, ‘MSFT‘]].pct_change(), window=125, min_periods=60).corr(spx_pctc)
corr.plot(ax=axes3[0,1])
# 8.3、使用者定義的移動視窗函數
ten_mean = lambda ts: np.mean(sorted(ts, reverse=True)[:10]) # 計算最大的前10個值得平均值
res = pd.DataFrame.rolling(stk[[‘AAPL‘, ‘MSFT‘]], window=125, min_periods=50).apply(ten_mean)
print(res)
res.plot(ax=axes3[1,0])
plt.show()
# 其實,在高版本的pandas中,rolling的用法和groupby是比較接近的。
print(‘----------------------total time is %.5f s‘ % (time.time() - start))

《python for data analysis》第十章,時間序列

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.