Pandas時間序列重取樣(resample)方法中closed、label的作用詳解
Pandas提供了便捷的方式對時間序列進行重取樣,根據時間粒度的變大或者變小分為降取樣和升取樣:
- 降取樣:時間粒度變大。例如,原來是按天統計的資料,現在變成按周統計。降取樣會涉及到資料的聚合,比如天資料變成周資料,那麼就得對一週的7天資料聚合,聚合的方式可以是求和,求均值等等。
- 升取樣:時間粒度變小。例如,原來是按周統計的資料,現在變成按天統計。升取樣會涉及到資料的填充,根據填充的方法不同填充的資料也就不同。
下面涉及的例子,都需要匯入numpy和pandas(如下),並且對於降取樣資料的聚合做簡單的求和處理。
import numpy as np import pandas as pd
Pandas重取樣方法resample
在Pandas裡,通過resample來處理重取樣,根據頻率的不同(freq)會處理成降取樣或者升取樣。我們先來看看Resample的定義和關鍵引數註釋:
resample(self,rule,how=None,axis=0,fill_method=None,closed=None,label=None,convention='start',kind=None,loffset=None,limit=None,base=0,on=None,level=None) Convenience method for frequency conversion and resampling of time series. Object must have a datetime-like index (DatetimeIndex,PeriodIndex,or TimedeltaIndex),or pass datetime-like values to the on or level keyword. Parameters ---------- closed : {'right','left'} Which side of bin interval is closed. The default is ‘left' for all frequency offsets except for ‘M',‘A',‘Q',‘BM',‘BA',‘BQ',and ‘W' which all have a default of ‘right'. label : {'right','left'} Which bin edge label to label bucket with. The default is ‘left' for all frequency offsets except for ‘M',and ‘W' which all have a default of ‘right'.
第一眼看closed和label這兩個引數,會感覺雲裡霧裡,即使看了例子也可能會覺得莫名奇妙。下面我們通過具體的降取樣和升取樣例子,來解讀一下這個兩個引數內含的玄機。
降取樣
首先先來建立一個時間序列,起始日期是2018/01/01,一共12天,每天對應的數值分別是1到12:
rng = pd.date_range('20180101',periods=12) ts = pd.Series(np.arange(1,13),index=rng) print(ts) #### Outputs #### 2018-01-01 1 2018-01-02 2 2018-01-03 3 2018-01-04 4 2018-01-05 5 2018-01-06 6 2018-01-07 7 2018-01-08 8 2018-01-09 9 2018-01-10 10 2018-01-11 11 2018-01-12 12 Freq: D,dtype: int32
下面使用resample方法來做降取樣處理,頻率是5天,上面提到的兩個引數,都使用預設值:
ts_5d = ts.resample('5D').sum() print(ts_5d) #### Outputs #### 2018-01-01 15 2018-01-06 40 2018-01-11 23 Freq: 5D,dtype: int32
到這裡,我相信不論是程式碼還是程式碼的結果都很好理解:無非就是每5天來個求和。在第一部分中,我們列出了closed引數的註釋,從註釋可知,closed預設的值是'left'。那如果把closed的值改為'right',結果有是怎麼樣的?
ts_5d_rightclosed = ts.resample('5D',closed='right').sum() print(ts_5d_rightclosed) #### Outputs #### 2017-12-27 1 2018-01-01 20 2018-01-06 45 2018-01-11 12 Freq: 5D,dtype: int32
怎麼會這樣?為什麼變成了四個區間?closed=right到底做了什麼?
彆著急,我們來一步一步看看,這其中發生了什麼事情。原始的時間序列是從18年1月1號到1月12號,一共12天。以5天為單位降取樣處理後,變成了三個5天,分別是:
- 第一個5天:1-2-3-4-5-6
- 第二個5天:6-7-8-9-10-11
- 第三個5天:12-13-14-15-16
實際上,這三個5天就是三個區間了。和數學裡區間的概念一樣,區間有開和閉的概念。在resample中,區間的開和閉,就是通過closed這個引數來控制。用數學符號表示的話:
closed = 'left' 左閉右開
上面的三個5天可以由以下的三個左閉右開的區間構成:
- 區間1:[1,6)
- 區間2: [6,11)
- 區間3:[11,16) 例子中,時間只到12號為止,但是這裡會往後補足5天
現在,在這三個區間上做資料聚合也就很好理解了。對於區間1進行求和,也就是12、13、14、15、16這5天的值求和即可。區間2和區間3也是同理。所以下面的程式碼就很好理解了:
ts_5d_leftclosed = ts.resample('5D',closed='right').sum()
print(ts_5d_leftclosed)
#### Outputs #### 2018-01-01 15 2018-01-06 40 2018-01-11 23 Freq: 5D,dtype: int32
closed = 'right' 左開右閉
上面的三個5天可以由以下的四個左開右閉的區間構成。注意,由於第一個5天是從1號到6號,但由於是左開區間,1號就落不到1到6號的那個區間,所以要往前補足:
- 區間1:(27,1]
- 區間2:(1,6]
- 區間3: (6,11]
- 區間4:(11,16]
現在,在這四個區間上做資料聚合也是一樣的道理了:對於區間1,是對28,29,30,31,1這五天的值求和(這裡只有1號是有值的),其餘的區間也是同理,但需要注意是左開右閉。所以到這裡,上面“莫名其妙”的程式碼和結果就好理解了。複製程式碼和結果如下:
ts_5d_rightclosed = ts.resample('5D',dtype: int32
理解了clsoed的意義以後,再來理解label就so easy了。由註釋可知,label的預設值是left。下面在closed='right'的基礎上,將label設定為right:
ts_5d_rightclosed_rightlable = ts.resample('5D',closed='right',label='right').sum() print(ts_5d_rightclosed_rightlable) #### Outputs #### 2018-01-01 1 2018-01-06 20 2018-01-11 45 2018-01-16 12 Freq: 5D,dtype: int32
於label為left相比,二者結果的異同點如下:
- 相同點:一樣是四個區間,每個區間的聚合的值是一樣的
- 不同點:每個區間的索引不同
不難發現,label為left的時候,就以區間左邊的那個日期作為索引;label,就以區間的右邊那個日期作為索引。
綜上,我們可以總結一下closed和label的用法和意義了:
- closed:劃分區間的依據,left會劃成左閉右開區間;right會劃分成左開右閉的區間。一般來說,closed為right的時候,區間會比為left的時候多一個。區間劃分完畢,聚合運算就在這個區間內執行。
- label:劃分區間完畢,根據label的不同,區間的索引就不同。如果label為left,則區間左邊的日期作為索引;如果label為right,則區間右邊的日期作為索引。
升取樣
建立一個時間序列,起始日期是2018/01/01,一共2天,每天對應的數值分別是1到2:
rng = pd.date_range('20180101',periods=2) ts = pd.Series(np.arange(1,2),index=rng) print(ts) #### Outputs #### 2018-01-01 1 2018-01-02 2 Freq: D,dtype: int32
升取樣就不涉及到closed和label的值,也就是會忽略(筒子們可以驗證一下),所以我們在使用的時候無需設定這兩個值。對於升取樣,前面也提到,主要是涉及到值的填充。有下面的四種填充方法(實際是三種):
- 不填充。那麼對應無值的地方,用NaN代替。對應的方法是asfreq。
- 用前值填充。用前面的值填充無值的地方。對應的方法是ffill或者pad。這裡方便記憶,ffill的第一個f是代表forward,向前的意思
- 用後值填充。對應的方法是bfill,b代表back。
下面是一個例子:
ts_6h_asfreq = ts.resample('6H').asfreq() print(ts_6h_asfreq) ts_6h_pad = ts.resample('6H').pad() print(ts_6h_pad) ts_6h_ffill = ts.resample('6H').ffill() print(ts_6h_ffill) ts_6h_bfill = ts.resample('6H').bfill() print(ts_6h_bfill) #### Outputs #### 2018-01-01 00:00:00 1.0 2018-01-01 06:00:00 NaN 2018-01-01 12:00:00 NaN 2018-01-01 18:00:00 NaN 2018-01-02 00:00:00 2.0 Freq: 6H,dtype: float64 2018-01-01 00:00:00 1 2018-01-01 06:00:00 1 2018-01-01 12:00:00 1 2018-01-01 18:00:00 1 2018-01-02 00:00:00 2 Freq: 6H,dtype: int32 2018-01-01 00:00:00 1 2018-01-01 06:00:00 1 2018-01-01 12:00:00 1 2018-01-01 18:00:00 1 2018-01-02 00:00:00 2 Freq: 6H,dtype: int32 2018-01-01 00:00:00 1 2018-01-01 06:00:00 2 2018-01-01 12:00:00 2 2018-01-01 18:00:00 2 2018-01-02 00:00:00 2 Freq: 6H,dtype: int32
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。