↑↑↑關注後"星標"Datawhale
每日乾貨 & 每月組隊學習,不錯過
作者:耿遠昊,Datawhale成員,華東師範大學
時序數據是指時間序列數據。時間序列數據是同一統一指標按時間順序記錄的數據列。在同一數據列中的各個數據必須是同口徑的,要求具有可比性。時序數據可以是時期數,也可以時點數。時間序列分析的目的是通過找出樣本內時間序列的統計特性和發展規律性,構建時間序列模型,進行樣本外預測。本文目錄
1. 時序的創建
1.1. 四類時間變量
1.2. 時間點的創建
1.3. DataOffset對象
2. 時序的索引及屬性
2.1. 索引切片
2.2. 子集索引
2.3. 時間點的屬性
3. 重採樣
3.1. resample對象的基本操作
3.2. 採樣聚合
3.3. 採樣組的迭代
4. 窗口函數
4.1. Rolling
4.2. Expanding
5. 問題及練習
5.1. 問題
import pandas as pdimport numpy as np一、時序的創建1.1. 四類時間變量現在理解可能關於③和④有些困惑,後面會作出一些說明1.2. 時間點的創建(a)to_datetime方法Pandas在時間點建立的輸入格式規定上給了很大的自由度,下面的語句都能正確建立同一時間點pd.to_datetime('2020.1.1')pd.to_datetime('2020 1.1')pd.to_datetime('2020 1 1')pd.to_datetime('2020 1-1')pd.to_datetime('2020-1 1')pd.to_datetime('2020-1-1')pd.to_datetime('2020/1/1')pd.to_datetime('1.1.2020')pd.to_datetime('1.1 2020')pd.to_datetime('1 1 2020')pd.to_datetime('1 1-2020')pd.to_datetime('1-1 2020')pd.to_datetime('1-1-2020')pd.to_datetime('1/1/2020')pd.to_datetime('20200101')pd.to_datetime('2020.0101')Timestamp('2020-01-01 00:00:00')
下面的語句都會報錯#pd.to_datetime('2020\\1\\1')#pd.to_datetime('2020`1`1')#pd.to_datetime('2020.1 1')#pd.to_datetime('1 1.2020')此時可利用format參數強制匹配pd.to_datetime('2020\\1\\1',format='%Y\\%m\\%d')pd.to_datetime('2020`1`1',format='%Y`%m`%d')pd.to_datetime('2020.1 1',format='%Y.%m %d')pd.to_datetime('1 1.2020',format='%d %m.%Y')Timestamp('2020-01-01 00:00:00')
同時,使用列表可以將其轉為時間點索引pd.Series(range(2),index=pd.to_datetime(['2020/1/1','2020/1/2']))type(pd.to_datetime(['2020/1/1','2020/1/2']))pandas.core.indexes.datetimes.DatetimeIndex
對於DataFrame而言,如果列已經按照時間順序排好,則利用to_datetime可自動轉換df = pd.DataFrame({'year': [2020, 2020],'month': [1, 1], 'day': [1, 2]})pd.to_datetime(df)(b)時間精度與範圍限制事實上,Timestamp的精度遠遠不止day,可以最小到納秒nspd.to_datetime('2020/1/1 00:00:00.123456789')Timestamp('2020-01-01 00:00:00.123456789')
同時,它帶來範圍的代價就是只有大約584年的時間點是可用的Timestamp('1677-09-21 00:12:43.145225')
Timestamp('2262-04-11 23:47:16.854775807')
(c)date_range方法一般來說,start/end/periods(時間點個數)/freq(間隔方法)是該方法最重要的參數,給定了其中的3個,剩下的一個就會被確定pd.date_range(start='2020/1/1',end='2020/1/10',periods=3)pd.date_range(start='2020/1/1',end='2020/1/10',freq='D')pd.date_range(start='2020/1/1',periods=3,freq='D')pd.date_range(end='2020/1/3',periods=3,freq='D')其中freq參數有許多選項,下面將常用部分羅列如下,更多選項可看這裡pd.date_range(start='2020/1/1',periods=3,freq='T')pd.date_range(start='2020/1/1',periods=3,freq='M')pd.date_range(start='2020/1/1',periods=3,freq='BYS')bdate_range是一個類似與date_range的方法,特點在於可以在自帶的工作日間隔設置上,再選擇weekmask參數和holidays參數它的freq中有一個特殊的'C'/'CBM'/'CBMS'選項,表示定製,需要聯合weekmask參數和holidays參數使用例如現在需要將工作日中的周一、周二、周五3天保留,並將部分holidays剔除weekmask = 'Mon Tue Fri'holidays = [pd.Timestamp('2020/1/%s'%i) for i in range(7,13)]pd.bdate_range(start='2020-1-1',end='2020-1-15',freq='C',weekmask=weekmask,holidays=holidays)1.3. DateOffset對象
(a)DataOffset與Timedelta的區別Timedelta絕對時間差的特點指無論是冬令時還是夏令時,增減1day都只計算24小時DataOffset相對時間差指,無論一天是23\24\25小時,增減1day都與當天相同的時間保持一致例如,英國當地時間 2020年03月29日,01:00:00 時鐘向前調整 1 小時 變為 2020年03月29日,02:00:00,開始夏令時ts = pd.Timestamp('2020-3-29 01:00:00', tz='Europe/Helsinki')ts + pd.Timedelta(days=1)Timestamp('2020-03-30 02:00:00+0300', tz='Europe/Helsinki')
ts + pd.DateOffset(days=1)Timestamp('2020-03-30 01:00:00+0300', tz='Europe/Helsinki')
這似乎有些令人頭大,但只要把tz(time zone)去除就可以不用管它了,兩者保持一致,除非要使用到時區變換ts = pd.Timestamp('2020-3-29 01:00:00')ts + pd.Timedelta(days=1)Timestamp('2020-03-30 01:00:00')
ts + pd.DateOffset(days=1)Timestamp('2020-03-30 01:00:00')
(b)增減一段時間DateOffset的可選參數包括years/months/weeks/days/hours/minutes/secondspd.Timestamp('2020-01-01') + pd.DateOffset(minutes=20) - pd.DateOffset(weeks=2)Timestamp('2019-12-18 00:20:00')
(c)各類常用offset對象pd.Timestamp('2020-01-01') + pd.offsets.Week(2)Timestamp('2020-01-15 00:00:00')
pd.Timestamp('2020-01-01') + pd.offsets.BQuarterBegin(1)Timestamp('2020-03-02 00:00:00')
(d)序列的offset操作利用apply函數pd.Series(pd.offsets.BYearBegin(3).apply(i) for i in pd.date_range('20200101',periods=3,freq='Y'))直接使用對象加減pd.date_range('20200101',periods=3,freq='Y') + pd.offsets.BYearBegin(3)定製offset,可以指定weekmask和holidays參數(思考為什麼三個都是一個值)pd.Series(pd.offsets.CDay(3,weekmask='Wed Fri',holidays='2020010').apply(i) for i in pd.date_range('20200105',periods=3,freq='D'))二、時序的索引及屬性2.1. 索引切片這一部分幾乎與第二章的規則完全一致rng = pd.date_range('2020','2021', freq='W')ts = pd.Series(np.random.randn(len(rng)), index=rng)ts.head()-0.47982974619679947
合法字符自動轉換為時間點ts['2020-01-26':'20200726'].head()2.2. 子集索引支持混合形態索引ts['2011-1':'20200726'].head()2.3. 時間點的屬性採用dt對象可以輕鬆獲得關於時間的信息pd.Series(ts.index).dt.week.head()pd.Series(ts.index).dt.day.head()利用strftime可重新修改時間格式pd.Series(ts.index).dt.strftime('%Y-間隔1-%m-間隔2-%d').head()對於datetime對象可以直接通過屬性獲取信息pd.date_range('2020','2021', freq='W').monthpd.date_range('2020','2021', freq='W').weekday三、重採樣所謂重採樣,就是指resample函數,它可以看做時序版本的groupby函數3.1. resample對象的基本操作採樣頻率一般設置為上面提到的offset字符df_r = pd.DataFrame(np.random.randn(1000, 3),index=pd.date_range('1/1/2020', freq='S', periods=1000), columns=['A', 'B', 'C'])r = df_r.resample('3min')rdf_r2 = pd.DataFrame(np.random.randn(200, 3),index=pd.date_range('1/1/2020', freq='D', periods=200), columns=['A', 'B', 'C'])r = df_r2.resample('CBMS')r.sum()3.2. 採樣聚合
r['A'].agg([np.sum, np.mean, np.std])類似地,可以使用函數lambda表達式
r.agg({'A': np.sum,'B': lambda x: max(x)-min(x)})
3.3. 採樣組的迭代
採樣組的迭代和groupby迭代完全類似,對於每一個組都可以分別做相應操作small = pd.Series(range(6),index=pd.to_datetime(['2020-01-01 00:00:00', '2020-01-01 00:30:00' , '2020-01-01 00:31:00','2020-01-01 01:00:00' ,'2020-01-01 03:00:00','2020-01-01 03:05:00']))resampled = small.resample('H')for name, group in resampled: print("Group: ", name) print("-" * 27) print(group, end="\n\n")四、窗口函數下面主要介紹pandas中兩類主要的窗口(window)函數:rolling/expandings = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2020', periods=1000))s.head()4.1. Rolling(a)常用聚合所謂rolling方法,就是規定一個窗口,它和groupby對象一樣,本身不會進行操作,需要配合聚合函數才能計算結果Rolling [window=50,center=False,axis=0]
s.rolling(window=50).mean()min_periods參數是指需要的非缺失數據點數量閥值s.rolling(window=50,min_periods=3).mean().head()count/sum/mean/median/min/max/std/var/skew/kurt/quantile/cov/corr都是常用的聚合函數。(b)rolling的apply聚合使用apply聚合時,只需記住傳入的是window大小的Series,輸出的必須是標量即可,比如如下計算變異係數s.rolling(window=50,min_periods=3).apply(lambda x:x.std()/x.mean()).head()(c)基於時間的rollings.rolling('15D').mean().head()可選closed='right'(默認)\'left'\'both'\'neither'參數,決定端點的包含情況s.rolling('15D', closed='right').sum().head()4.2. Expanding(a)expanding函數普通的expanding函數等價與rolling(window=len(s),min_periods=1),是對序列的累計計算s.rolling(window=len(s),min_periods=1).sum().head()s.expanding().sum().head()apply方法也是同樣可用的s.expanding().apply(lambda x:sum(x)).head()(b)幾個特別的Expanding類型函數cumsum/cumprod/cummax/cummin都是特殊expanding累計計算方法shift/diff/pct_change都是涉及到了元素關係① shift是指序列索引不變,但值向後移動② diff是指前後元素的差,period參數表示間隔,默認為1,並且可以為負③ pct_change是值前後元素的變化百分比,period參數與diff類似五、問題與練習5.1. 問題
【問題一】 如何對date_range進行批量加幀操作或對某一時間段加大時間戳密度?【問題二】 如何批量增加TimeStamp的精度?【問題三】 對於超出處理時間的時間點,是否真的完全沒有處理方法?【問題四】 給定一組非連續的日期,怎麼快速找出位於其最大日期和最小日期之間,且沒有出現在該組日期中的日期?5.2. 練習
【練習一】 現有一份關於某超市牛奶銷售額的時間序列數據,請完成下列問題:(a)銷售額出現最大值的是星期幾?(提示:利用dayofweek函數)(b)計算除去春節、國慶、五一節假日的月度銷售總額(c)按季度計算周末(周六和周日)的銷量總額
(d)從最後一天開始算起,跳過周六和周一,以5天為一個時間單位向前計算銷售總和(e)假設現在發現數據有誤,所有同一周裡的周一與周五的銷售額記錄顛倒了,請計算2018年中每月第一個周一的銷售額(如果該周沒有周一或周五的記錄就保持不動)【練習二】 繼續使用上一題的數據,請完成下列問題:
(a)以50天為窗口計算滑窗均值和滑窗最大值(min_periods設為1)(b)現在有如下規則:若當天銷售額超過向前5天的均值,則記為1,否則記為0,請給出2018年相應的計算結果(c)將(c)中的「向前5天」改為「向前非周末5天」,請再次計算結果本文電子版 後臺回復 時序數據 獲取
「竟然學習完了,給自己點個贊↓