Pandas 時間序列 - 實例方法與重采樣

呆鳥云:“數(shù)據(jù)分析就像是夜里行軍,業(yè)務(wù)知識是燈塔拔疚,分析思維是地圖肥隆,沒燈塔你不知道方向,沒地圖你不知道該怎么走稚失。技術(shù)是你的交通工具栋艳,你用11路腿兒著,還是騎自行車句各,還是開跑車吸占,交通工具越好,你實現(xiàn)目標(biāo)的速度越快 凿宾》停”

移位與延遲

有時,需要整體向前或向后移動時間序列里的值初厚,這就是移位與延遲件蚕。實現(xiàn)這一操作的方法是 shift(),該方法適用于所有 Pandas 對象。

In [272]: ts = pd.Series(range(len(rng)), index=rng)

In [273]: ts = ts[:5]

In [274]: ts.shift(1)
Out[274]: 
2012-01-01    NaN
2012-01-02    0.0
2012-01-03    1.0
Freq: D, dtype: float64

shift 方法支持 freq 參數(shù)排作,可以把 DateOffset牵啦、timedelta 對象、偏移量別名 作為參數(shù)值:

In [275]: ts.shift(5, freq=pd.offsets.BDay())
Out[275]: 
2012-01-06    0
2012-01-09    1
2012-01-10    2
Freq: B, dtype: int64

In [276]: ts.shift(5, freq='BM')
Out[276]: 
2012-05-31    0
2012-05-31    1
2012-05-31    2
Freq: D, dtype: int64

除更改數(shù)據(jù)與索引的對齊方式外妄痪,DataFrameSeries 對象還提供了 tshift() 便捷方法蕾久,可以指定偏移量修改索引日期。

In [277]: ts.tshift(5, freq='D')
Out[277]: 
2012-01-06    0
2012-01-07    1
2012-01-08    2
Freq: D, dtype: int64

注意拌夏,使用 tshift() 時僧著,因為數(shù)據(jù)沒有重對齊,NaN 不會排在前面障簿。

頻率轉(zhuǎn)換

改變頻率的函數(shù)主要是 asfreq()盹愚。對于 DatetimeIndex,這就是一個調(diào)用 reindex()站故,并生成 date_range 的便捷打包器皆怕。

In [278]: dr = pd.date_range('1/1/2010', periods=3, freq=3 * pd.offsets.BDay())

In [279]: ts = pd.Series(np.random.randn(3), index=dr)

In [280]: ts
Out[280]: 
2010-01-01    1.494522
2010-01-06   -0.778425
2010-01-11   -0.253355
Freq: 3B, dtype: float64

In [281]: ts.asfreq(pd.offsets.BDay())
Out[281]: 
2010-01-01    1.494522
2010-01-04         NaN
2010-01-05         NaN
2010-01-06   -0.778425
2010-01-07         NaN
2010-01-08         NaN
2010-01-11   -0.253355
Freq: B, dtype: float64

asfreq 用起來很方便,可以為頻率轉(zhuǎn)化后出現(xiàn)的任意間隔指定插值方法西篓。

In [282]: ts.asfreq(pd.offsets.BDay(), method='pad')
Out[282]: 
2010-01-01    1.494522
2010-01-04    1.494522
2010-01-05    1.494522
2010-01-06   -0.778425
2010-01-07   -0.778425
2010-01-08   -0.778425
2010-01-11   -0.253355
Freq: B, dtype: float64

向前與向后填充

asfreqreindex 相關(guān)的是 fillna()愈腾,有關(guān)文檔請參閱缺失值

轉(zhuǎn)換 Python 日期與時間

to_datetime 方法可以把DatetimeIndex 轉(zhuǎn)換為 Python 原生 datetime.datetime 對象數(shù)組岂津。

重采樣

0.18.0 版修改了 .resample 接口虱黄,現(xiàn)在的 .resample 更靈活,更像 groupby吮成。參閱更新文檔 橱乱,對比新舊版本操作的區(qū)別。

Pandas 有一個雖然簡單粱甫,但卻強大泳叠、高效的功能,可在頻率轉(zhuǎn)換時執(zhí)行重采樣茶宵,如危纫,將秒數(shù)據(jù)轉(zhuǎn)換為 5 分鐘數(shù)據(jù),這種操作在金融等領(lǐng)域里的應(yīng)用非常廣泛乌庶。

resample() 是基于時間的分組操作种蝶,每個組都遵循歸納方法。參閱 Cookbook 示例了解高級應(yīng)用安拟。

從 0.18.0 版開始蛤吓,resample() 可以直接用于 DataFrameGroupBy 對象,參閱 groupby 文檔糠赦。

.resample() 類似于基于時間偏移量的 rolling() 操作会傲,請參閱這里的討論锅棕。

基礎(chǔ)知識

In [283]: rng = pd.date_range('1/1/2012', periods=100, freq='S')

In [284]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)

In [285]: ts.resample('5Min').sum()
Out[285]: 
2012-01-01    25103
Freq: 5T, dtype: int64

resample 函數(shù)非常靈活,可以指定多種頻率轉(zhuǎn)換與重采樣參數(shù)淌山。

任何支持派送(dispatch)的函數(shù)都可用于 resample 返回對象裸燎,包括 summean泼疑、std德绿、semmax退渗、min移稳、midmedian会油、first个粱、lastohlc

In [286]: ts.resample('5Min').mean()
Out[286]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

In [287]: ts.resample('5Min').ohlc()
Out[287]: 
            open  high  low  close
2012-01-01   308   460    9    205

In [288]: ts.resample('5Min').max()
Out[288]: 
2012-01-01    460
Freq: 5T, dtype: int64

對于下采樣翻翩,closed 可以設(shè)置為leftright都许,用于指定關(guān)閉哪一端間隔:

In [289]: ts.resample('5Min', closed='right').mean()
Out[289]: 
2011-12-31 23:55:00    308.000000
2012-01-01 00:00:00    250.454545
Freq: 5T, dtype: float64

In [290]: ts.resample('5Min', closed='left').mean()
Out[290]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

labelloffset 等參數(shù)用于生成標(biāo)簽嫂冻。label 指定生成的結(jié)果是否要為間隔標(biāo)注起始時間胶征。loffset 調(diào)整輸出標(biāo)簽的時間。

In [291]: ts.resample('5Min').mean()  # 默認(rèn)為 label='left'
Out[291]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

In [292]: ts.resample('5Min', label='left').mean()
Out[292]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

In [293]: ts.resample('5Min', label='left', loffset='1s').mean()
Out[293]: 
2012-01-01 00:00:01    251.03
dtype: float64

除了 M桨仿、A睛低、QBM蹬敲、BA暇昂、BQ莺戒、W 的默認(rèn)值是 right 外伴嗡,其它頻率偏移量的 labelclosed 默認(rèn)值都是 left

這種操作可能會導(dǎo)致時間回溯从铲,即后面的時間會被拉回到前面的時間瘪校,如下例的 BusinessDay 頻率所示。

In [294]: s = pd.date_range('2000-01-01', '2000-01-05').to_series()

In [295]: s.iloc[2] = pd.NaT

In [296]: s.dt.weekday_name
Out[296]: 
2000-01-01     Saturday
2000-01-02       Sunday
2000-01-03          NaN
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: D, dtype: object

# 默認(rèn)為:label='left', closed='left'
In [297]: s.resample('B').last().dt.weekday_name
Out[297]: 
1999-12-31       Sunday
2000-01-03          NaN
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: B, dtype: object

看到了嗎名段?星期日被拉回到了上一個星期五阱扬。要想把星期日移至星期一,改用以下代碼:

In [298]: s.resample('B', label='right', closed='right').last().dt.weekday_name
Out[298]: 
2000-01-03       Sunday
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: B, dtype: object

axis 參數(shù)的值為 01伸辟,并可指定 DataFrame 重采樣的軸麻惶。

kind 參數(shù)可以是 timestampperiod,轉(zhuǎn)換為時間戳或時間段形式的索引信夫。resample 默認(rèn)保留輸入的日期時間形式窃蹋。

重采樣 period 數(shù)據(jù)時(詳情見下文)卡啰,convention 可以設(shè)置為 startend。指定低頻時間段如何轉(zhuǎn)換為高頻時間段警没。

上采樣

上采樣可以指定上采樣的方式及插入時間間隔的 limit 參數(shù):

# 從秒到每 250 毫秒
In [299]: ts[:2].resample('250L').asfreq()
Out[299]: 
2012-01-01 00:00:00.000    308.0
2012-01-01 00:00:00.250      NaN
2012-01-01 00:00:00.500      NaN
2012-01-01 00:00:00.750      NaN
2012-01-01 00:00:01.000    204.0
Freq: 250L, dtype: float64

In [300]: ts[:2].resample('250L').ffill()
Out[300]: 
2012-01-01 00:00:00.000    308
2012-01-01 00:00:00.250    308
2012-01-01 00:00:00.500    308
2012-01-01 00:00:00.750    308
2012-01-01 00:00:01.000    204
Freq: 250L, dtype: int64

In [301]: ts[:2].resample('250L').ffill(limit=2)
Out[301]: 
2012-01-01 00:00:00.000    308.0
2012-01-01 00:00:00.250    308.0
2012-01-01 00:00:00.500    308.0
2012-01-01 00:00:00.750      NaN
2012-01-01 00:00:01.000    204.0
Freq: 250L, dtype: float64

稀疏重采樣

相對于時間點總量匈辱,稀疏時間序列重采樣的點要少很多。單純上采樣稀疏系列可能會生成很多中間值杀迹。未指定填充值亡脸,即 fill_methodNone 時,中間值將填充為 NaN树酪。

鑒于 resample 是基于時間的分組浅碾,下列這種方法可以有效重采樣,只是分組不是都為 NaN续语。

In [302]: rng = pd.date_range('2014-1-1', periods=100, freq='D') + pd.Timedelta('1s')

In [303]: ts = pd.Series(range(100), index=rng)

Series 全范圍重采樣及穗。

In [304]: ts.resample('3T').sum()
Out[304]: 
2014-01-01 00:00:00     0
2014-01-01 00:03:00     0
2014-01-01 00:06:00     0
2014-01-01 00:09:00     0
2014-01-01 00:12:00     0
                       ..
2014-04-09 23:48:00     0
2014-04-09 23:51:00     0
2014-04-09 23:54:00     0
2014-04-09 23:57:00     0
2014-04-10 00:00:00    99
Freq: 3T, Length: 47521, dtype: int64

對以下包含點的分組重采樣:

In [305]: from functools import partial

In [306]: from pandas.tseries.frequencies import to_offset

In [307]: def round(t, freq):
   .....:     freq = to_offset(freq)
   .....:     return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
   .....: 

In [308]: ts.groupby(partial(round, freq='3T')).sum()
Out[308]: 
2014-01-01     0
2014-01-02     1
2014-01-03     2
2014-01-04     3
2014-01-05     4
              ..
2014-04-06    95
2014-04-07    96
2014-04-08    97
2014-04-09    98
2014-04-10    99
Length: 100, dtype: int64

聚合

類似于聚合 APIGroupby API窗口函數(shù) API绵载,Resampler 可以有選擇地重采樣埂陆。

DataFrame 重采樣,默認(rèn)用相同函數(shù)操作所有列娃豹。

In [309]: df = pd.DataFrame(np.random.randn(1000, 3),
   .....:                   index=pd.date_range('1/1/2012', freq='S', periods=1000),
   .....:                   columns=['A', 'B', 'C'])
   .....: 

In [310]: r = df.resample('3T')

In [311]: r.mean()
Out[311]: 
                            A         B         C
2012-01-01 00:00:00 -0.033823 -0.121514 -0.081447
2012-01-01 00:03:00  0.056909  0.146731 -0.024320
2012-01-01 00:06:00 -0.058837  0.047046 -0.052021
2012-01-01 00:09:00  0.063123 -0.026158 -0.066533
2012-01-01 00:12:00  0.186340 -0.003144  0.074752
2012-01-01 00:15:00 -0.085954 -0.016287 -0.050046

標(biāo)準(zhǔn) getitem 操作可以指定的一列或多列焚虱。

In [312]: r['A'].mean()
Out[312]: 
2012-01-01 00:00:00   -0.033823
2012-01-01 00:03:00    0.056909
2012-01-01 00:06:00   -0.058837
2012-01-01 00:09:00    0.063123
2012-01-01 00:12:00    0.186340
2012-01-01 00:15:00   -0.085954
Freq: 3T, Name: A, dtype: float64

In [313]: r[['A', 'B']].mean()
Out[313]: 
                            A         B
2012-01-01 00:00:00 -0.033823 -0.121514
2012-01-01 00:03:00  0.056909  0.146731
2012-01-01 00:06:00 -0.058837  0.047046
2012-01-01 00:09:00  0.063123 -0.026158
2012-01-01 00:12:00  0.186340 -0.003144
2012-01-01 00:15:00 -0.085954 -0.016287

聚合還支持函數(shù)列表與字典,輸出的是 DataFrame懂版。

In [314]: r['A'].agg([np.sum, np.mean, np.std])
Out[314]: 
                           sum      mean       std
2012-01-01 00:00:00  -6.088060 -0.033823  1.043263
2012-01-01 00:03:00  10.243678  0.056909  1.058534
2012-01-01 00:06:00 -10.590584 -0.058837  0.949264
2012-01-01 00:09:00  11.362228  0.063123  1.028096
2012-01-01 00:12:00  33.541257  0.186340  0.884586
2012-01-01 00:15:00  -8.595393 -0.085954  1.035476

重采樣后的 DataFrame鹃栽,可以為每列指定函數(shù)列表,生成結(jié)構(gòu)化索引的聚合結(jié)果:

In [315]: r.agg([np.sum, np.mean])
Out[315]: 
                             A                    B                    C          
                           sum      mean        sum      mean        sum      mean
2012-01-01 00:00:00  -6.088060 -0.033823 -21.872530 -0.121514 -14.660515 -0.081447
2012-01-01 00:03:00  10.243678  0.056909  26.411633  0.146731  -4.377642 -0.024320
2012-01-01 00:06:00 -10.590584 -0.058837   8.468289  0.047046  -9.363825 -0.052021
2012-01-01 00:09:00  11.362228  0.063123  -4.708526 -0.026158 -11.975895 -0.066533
2012-01-01 00:12:00  33.541257  0.186340  -0.565895 -0.003144  13.455299  0.074752
2012-01-01 00:15:00  -8.595393 -0.085954  -1.628689 -0.016287  -5.004580 -0.050046

把字典傳遞給 aggregate躯畴,可以為 DataFrame 里不同的列應(yīng)用不同聚合函數(shù)民鼓。

In [316]: r.agg({'A': np.sum,
   .....:        'B': lambda x: np.std(x, ddof=1)})
   .....: 
Out[316]: 
                             A         B
2012-01-01 00:00:00  -6.088060  1.001294
2012-01-01 00:03:00  10.243678  1.074597
2012-01-01 00:06:00 -10.590584  0.987309
2012-01-01 00:09:00  11.362228  0.944953
2012-01-01 00:12:00  33.541257  1.095025
2012-01-01 00:15:00  -8.595393  1.035312

還可以用字符串代替函數(shù)名。為了讓字符串有效蓬抄,必須在重采樣對象上操作:

In [317]: r.agg({'A': 'sum', 'B': 'std'})
Out[317]: 
                             A         B
2012-01-01 00:00:00  -6.088060  1.001294
2012-01-01 00:03:00  10.243678  1.074597
2012-01-01 00:06:00 -10.590584  0.987309
2012-01-01 00:09:00  11.362228  0.944953
2012-01-01 00:12:00  33.541257  1.095025
2012-01-01 00:15:00  -8.595393  1.035312

甚至還可以為每列單獨多個聚合函數(shù)丰嘉。

In [318]: r.agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
Out[318]: 
                             A                   B          
                           sum       std      mean       std
2012-01-01 00:00:00  -6.088060  1.043263 -0.121514  1.001294
2012-01-01 00:03:00  10.243678  1.058534  0.146731  1.074597
2012-01-01 00:06:00 -10.590584  0.949264  0.047046  0.987309
2012-01-01 00:09:00  11.362228  1.028096 -0.026158  0.944953
2012-01-01 00:12:00  33.541257  0.884586 -0.003144  1.095025
2012-01-01 00:15:00  -8.595393  1.035476 -0.016287  1.035312

如果 DataFrame 用的不是 datetime 型索引,則可以基于 datetime 數(shù)據(jù)列重采樣嚷缭,用關(guān)鍵字 on 控制饮亏。

In [319]: df = pd.DataFrame({'date': pd.date_range('2015-01-01', freq='W', periods=5),
   .....:                    'a': np.arange(5)},
   .....:                   index=pd.MultiIndex.from_arrays([
   .....:                       [1, 2, 3, 4, 5],
   .....:                       pd.date_range('2015-01-01', freq='W', periods=5)],
   .....:                       names=['v', 'd']))
   .....: 

In [320]: df
Out[320]: 
                   date  a
v d                       
1 2015-01-04 2015-01-04  0
2 2015-01-11 2015-01-11  1
3 2015-01-18 2015-01-18  2
4 2015-01-25 2015-01-25  3
5 2015-02-01 2015-02-01  4

In [321]: df.resample('M', on='date').sum()
Out[321]: 
            a
date         
2015-01-31  6
2015-02-28  4

同樣,還可以對 datetime MultiIndex 重采樣阅爽,通過關(guān)鍵字 level 傳遞名字與位置路幸。

In [322]: df.resample('M', level='d').sum()
Out[322]: 
            a
d            
2015-01-31  6
2015-02-28  4

分組迭代

Resampler對象迭代分組數(shù)據(jù)的操作非常自然,類似于 itertools.groupby()

In [323]: small = pd.Series(
   .....:     range(6),
   .....:     index=pd.to_datetime(['2017-01-01T00:00:00',
   .....:                           '2017-01-01T00:30:00',
   .....:                           '2017-01-01T00:31:00',
   .....:                           '2017-01-01T01:00:00',
   .....:                           '2017-01-01T03:00:00',
   .....:                           '2017-01-01T03:05:00'])
   .....: )
   .....: 

In [324]: resampled = small.resample('H')

In [325]: for name, group in resampled:
   .....:     print("Group: ", name)
   .....:     print("-" * 27)
   .....:     print(group, end="\n\n")
   .....: 
Group:  2017-01-01 00:00:00
---------------------------
2017-01-01 00:00:00    0
2017-01-01 00:30:00    1
2017-01-01 00:31:00    2
dtype: int64

Group:  2017-01-01 01:00:00
---------------------------
2017-01-01 01:00:00    3
dtype: int64

Group:  2017-01-01 02:00:00
---------------------------
Series([], dtype: int64)

Group:  2017-01-01 03:00:00
---------------------------
2017-01-01 03:00:00    4
2017-01-01 03:05:00    5
dtype: int64

了解更多詳情付翁,請參閱分組迭代itertools.groupby()简肴。

Pandas 百問百答

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市百侧,隨后出現(xiàn)的幾起案子砰识,更是在濱河造成了極大的恐慌杂伟,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仍翰,死亡現(xiàn)場離奇詭異赫粥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)予借,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門越平,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人灵迫,你說我怎么就攤上這事秦叛。” “怎么了瀑粥?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵挣跋,是天一觀的道長。 經(jīng)常有香客問我狞换,道長避咆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任修噪,我火速辦了婚禮查库,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘黄琼。我一直安慰自己樊销,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布脏款。 她就那樣靜靜地躺著围苫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撤师。 梳的紋絲不亂的頭發(fā)上剂府,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音丈氓,去河邊找鬼周循。 笑死,一個胖子當(dāng)著我的面吹牛万俗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饮怯,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闰歪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蓖墅?” 一聲冷哼從身側(cè)響起库倘,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤临扮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后教翩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杆勇,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年饱亿,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚜退。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡彪笼,死狀恐怖钻注,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情配猫,我是刑警寧澤幅恋,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站泵肄,受9級特大地震影響捆交,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腐巢,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一零渐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧系忙,春花似錦诵盼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛹疯,卻和暖如春戒财,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捺弦。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工饮寞, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人列吼。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓幽崩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寞钥。 傳聞我的和親對象是個殘疾皇子慌申,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355