呆鳥云:“數(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ù)與索引的對齊方式外妄痪,DataFrame
與 Series
對象還提供了 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
向前與向后填充
與 asfreq
與 reindex
相關(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 文檔糠赦。
基礎(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
返回對象裸燎,包括 sum
、mean
泼疑、std
德绿、sem
、max
退渗、min
移稳、mid
、median
会油、first
个粱、last
、ohlc
:
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è)置為left
或 right
都许,用于指定關(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
label
、loffset
等參數(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
睛低、Q
、BM
蹬敲、BA
暇昂、BQ
莺戒、W
的默認(rèn)值是right
外伴嗡,其它頻率偏移量的label
與closed
默認(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ù)的值為 0
或 1
伸辟,并可指定 DataFrame
重采樣的軸麻惶。
kind
參數(shù)可以是 timestamp
或 period
,轉(zhuǎn)換為時間戳或時間段形式的索引信夫。resample
默認(rèn)保留輸入的日期時間形式窃蹋。
重采樣 period
數(shù)據(jù)時(詳情見下文)卡啰,convention
可以設(shè)置為 start
或 end
。指定低頻時間段如何轉(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_method
是 None
時,中間值將填充為 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
聚合
類似于聚合 API,Groupby 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()
简肴。