Pandas是在金融建模背景下開發(fā)出來的,所以如你期待的,它包含相當(dāng)大的工具集來處理日期,時(shí)間匈勋,和時(shí)間索引數(shù)據(jù)礼旅。我們將在這里討論記錄時(shí)間和日期的幾種風(fēng)格:
- 時(shí)間戳 指特殊時(shí)刻(如膳叨,2015年7月4日上午7點(diǎn))
- 時(shí)間間隔和周期 指特定起始和結(jié)束點(diǎn)間的時(shí)間段。例如2015年痘系。周期是時(shí)間間隔的特殊情形菲嘴,在這里每個(gè)間隔有同樣長(zhǎng)時(shí)間,并且不會(huì)重疊(如汰翠,一天包含24小時(shí))
- 時(shí)間差或持續(xù)時(shí)間 指確切的時(shí)間長(zhǎng)度(如龄坪,持續(xù)22.56秒)
在本章,我們將介紹如何處理Pandas中的各種時(shí)間類型复唤。短短的一章是無法對(duì)Python或Pandas時(shí)間工具進(jìn)行完整介紹的健田,相反,本章旨在作為一個(gè)廣泛的概述佛纫,使你作為一個(gè)用戶應(yīng)該如何處理時(shí)間序列妓局。在對(duì)Pandas提供的特殊的工具討論之前,我們將對(duì)Python中處理日期和時(shí)間的工具作一個(gè)簡(jiǎn)要介紹呈宇。列出一些適合深入學(xué)習(xí)的資源以后好爬,我們回顧一些在Pandas中使用時(shí)間序列數(shù)據(jù)的簡(jiǎn)短例子。
Python中的日期和時(shí)間
Python世界中有許多種時(shí)間表達(dá)方式日期甥啄,時(shí)間存炮,時(shí)間差,時(shí)隙蜈漓。Pandas提供的實(shí)現(xiàn)序列工具對(duì)于數(shù)據(jù)科學(xué)應(yīng)用可能使最有用的了穆桂,看看這些工具和Python中使用的其它包的關(guān)系很有幫助。
Python原生日期和時(shí)間:datetime和dateutil
處理時(shí)間和日期的Python基本對(duì)象位于內(nèi)置的datetime模塊融虽。同第三方的dateutil模塊一起享完,你可以使用它們快速的執(zhí)行許多有用的時(shí)間日期功能。例如衣形,你可以使用datetime類型手動(dòng)構(gòu)建一個(gè)日期:
from datetime import datetime
datetime(year=2015, month=7, day=4)
datetime.datetime(2015, 7, 4, 0, 0)
或者驼侠,使用dateutil模塊姿鸿,你可以從不同的字符串模式中解析出日期:
from dateutil import parser
date = parser.parse("4th of July, 2015")
date
datetime.datetime(2015, 7, 4, 0, 0)
一旦你有了一個(gè)datetime對(duì)象,你可以做像打印周幾這樣的事:
date.strftime('%A')
'Saturday'
在最后一行倒源,我們使用一種標(biāo)準(zhǔn)的字符串格式代碼來打印日期("%A")苛预,參見Python 時(shí)間日期文檔. strftime章。其它有用的日期工具文檔可以在 dateutil的在線文檔中找到笋熬。
另一個(gè)需要注意的包是 pytz
, 它里面包括可以處理時(shí)間序列中最令人頭疼部分:時(shí)區(qū)热某,的工具
datetime和dateutil的強(qiáng)大之處在于它們靈活和簡(jiǎn)單的語法:你可以使用這些對(duì)象及其內(nèi)置方法來方便的執(zhí)行幾乎任何你感興趣的操作。當(dāng)你希望處理時(shí)間和日期的大數(shù)組時(shí)胳螟,這些工具不不好用了:就如Python數(shù)值變量列表比不過NumPy風(fēng)格的數(shù)值數(shù)組昔馋,Python的日期時(shí)間對(duì)象列表與日期編碼的類型數(shù)組相比也是次優(yōu)的。
時(shí)間類型數(shù)組:NumPy 的datetime64
Python datetime格式的缺點(diǎn)激勵(lì)NumPy團(tuán)隊(duì)添加一組原生時(shí)間序列數(shù)據(jù)類型到NumPy里糖耸。datetime64 dtype將日期編碼成64位的整型秘遏,因此允許日期數(shù)組非常緊湊的表達(dá)。datetime64需要特殊的輸入格式:
import numpy as np
date = np.array('2015-07-04', dtype=np.datetime64)
date
array(datetime.date(2015, 7, 4), dtype='datetime64[D]')
但是一旦我們有了這種日期格式嘉竟,我們可以快捷的做矢量化操作:
date + np.arange(12)
array(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
'2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
'2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'], dtype='datetime64[D]')
由于NumPy datetime64 數(shù)組內(nèi)部的統(tǒng)一類型邦危,相比直接工作在Python的datetime對(duì)象上,這種類型的操作在datetime64sh上要快很多舍扰,特別時(shí)當(dāng)數(shù)組很大的時(shí)候(我們介紹過這種向量類型在Computation on NumPy Arrays: Universal Functions).)
datetime64和timedelta64 對(duì)象的一個(gè)細(xì)節(jié)時(shí)基于基礎(chǔ)時(shí)間單元構(gòu)建倦蚪。因?yàn)閐atetime64對(duì)象受限于64位精度,時(shí)間編碼的范圍是基礎(chǔ)時(shí)間單元的 倍
例如边苹,如果你想要一納秒的時(shí)間精度陵且,那么只能編碼納秒的信息,即少于600年个束。NumPy將會(huì)從輸入中推斷出合適的單位慕购;例如,這里是基于天的的datetime:
np.datetime64('2015-07-04')
numpy.datetime64('2015-07-04')
這個(gè)是基于分鐘的datetime
np.datetime64('2015-07-04 12:00')
numpy.datetime64('2015-07-04T12:00')
注意時(shí)區(qū)被自動(dòng)設(shè)置成代碼執(zhí)行計(jì)算機(jī)的本地時(shí)間播急。你可以強(qiáng)制使用一種你想要的基礎(chǔ)時(shí)間單元脓钾;例如:我們強(qiáng)制使用基于納秒的時(shí)間:
np.datetime64('2015-07-04 12:59:59.50', 'ns')
numpy.datetime64('2015-07-04T12:59:59.500000000')
下表來自于NumPy datetime64 documentation,列出了可用格式代碼及其它們可以編碼的相對(duì)和絕對(duì)時(shí)間時(shí)間跨度
Code | Meaning | Time span (relative) | Time span (absolute) |
---|---|---|---|
Y |
Year | ± 9.2e18 years | [9.2e18 BC, 9.2e18 AD] |
M |
Month | ± 7.6e17 years | [7.6e17 BC, 7.6e17 AD] |
W |
Week | ± 1.7e17 years | [1.7e17 BC, 1.7e17 AD] |
D |
Day | ± 2.5e16 years | [2.5e16 BC, 2.5e16 AD] |
h |
Hour | ± 1.0e15 years | [1.0e15 BC, 1.0e15 AD] |
m |
Minute | ± 1.7e13 years | [1.7e13 BC, 1.7e13 AD] |
s |
Second | ± 2.9e12 years | [ 2.9e9 BC, 2.9e9 AD] |
ms |
Millisecond | ± 2.9e9 years | [ 2.9e6 BC, 2.9e6 AD] |
us |
Microsecond | ± 2.9e6 years | [290301 BC, 294241 AD] |
ns |
Nanosecond | ± 292 years | [ 1678 AD, 2262 AD] |
ps |
Picosecond | ± 106 days | [ 1969 AD, 1970 AD] |
fs |
Femtosecond | ± 2.6 hours | [ 1969 AD, 1970 AD] |
as |
Attosecond | ± 9.2 seconds | [ 1969 AD, 1970 AD] |
對(duì)于我們現(xiàn)實(shí)世界中的時(shí)間類型,有用的默認(rèn)值是datetime64[ns]桩警,因?yàn)樗梢允褂煤线m的精度編碼有用的現(xiàn)代日期可训。
最后,我們要注意盡管datetime64數(shù)據(jù)類型解決了一些Python datetime類型的不足之處捶枢,但它缺少datetime特別是dateutil提供的那些便利的方法握截。更多信息參見NumPy's datetime64 documentation.
pandas的日期和時(shí)間:兩全齊美
Pandas在前面討論過的工具之上提供了Timestamp對(duì)象,它結(jié)合了datetime和dateutil的易用性以及numpy.datetime64的存儲(chǔ)效率和矢量化接口烂叔。從一組Timestamp對(duì)象谨胞,Pandas能構(gòu)建出可以被用來索引Series或DataFrame數(shù)據(jù)的DatetimeIndex;后面我們會(huì)見到許多例子蒜鸡。
例如胯努,我們使用Pandas工具重新演示上面的例子牢裳。我們可以解析靈活的字符串日期格式,并且使用格式代碼輸出是周幾:
import pandas as pd
date = pd.to_datetime("4th of July, 2015")
date
Timestamp('2015-07-04 00:00:00')
date.strftime('%A')
'Saturday'
另外叶沛,我們可以直接在同樣對(duì)象上蒲讯,做NumPy風(fēng)格的矢量化操作:
date + pd.to_timedelta(np.arange(12), 'D')
DatetimeIndex(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
'2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
'2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'],
dtype='datetime64[ns]', freq=None)
下一部分,我們仔細(xì)看看使用Pandas工具操縱時(shí)間序列數(shù)據(jù)灰署。
Pandas 時(shí)間序列:按時(shí)間排序
Pandas時(shí)間序列非常有用的地方是當(dāng)你用時(shí)間戳進(jìn)行索引的時(shí)候判帮。例如,我們可以構(gòu)建使用時(shí)間索引的Series對(duì)象:
index = pd.DatetimeIndex(['2014-07-04', '2014-08-04',
'2015-07-04', '2015-08-04'])
data = pd.Series([0, 1, 2, 3], index=index)
data
2014-07-04 0
2014-08-04 1
2015-07-04 2
2015-08-04 3
dtype: int64
現(xiàn)在我們?cè)赟eries中有這樣數(shù)據(jù)溉箕,我們可以使用之前討論過任何Series索引模式晦墙,輸入值被強(qiáng)制成日期:
data['2014-07-04':'2015-07-04']
2014-07-04 0
2014-08-04 1
2015-07-04 2
dtype: int64
還有額外特殊的日期索引操作,例如傳遞一個(gè)年份來獲取那年的所以數(shù)據(jù):
data['2015']
2015-07-04 2
2015-08-04 3
dtype: int64
后面肴茄,我們會(huì)看到其它日期作為索引的便利性的例子晌畅。但首先,仔細(xì)看一下可以時(shí)間序列數(shù)據(jù)結(jié)構(gòu).
Pandas時(shí)間序列數(shù)據(jù)結(jié)構(gòu)
這部分將介紹用于處理時(shí)間序列數(shù)據(jù)的基礎(chǔ)Pandas數(shù)據(jù)結(jié)構(gòu):
- 對(duì)于時(shí)間戳独郎,Pandas提供了Timestamp類型踩麦。前面提到過枚赡,它基本可以替代Python原生的datetime氓癌,并且它時(shí)基于更高效的numpy.datetime64 數(shù)據(jù)類型。對(duì)應(yīng)的索引結(jié)構(gòu)是DatetimeIndex.
- 對(duì)于時(shí)間周期贫橙,Pandas提供了Period類型贪婉。它基于numpy.datetime64編碼固定頻率周期間隔。對(duì)應(yīng)的索引結(jié)構(gòu)是PeriodIndex.
- 對(duì)于時(shí)間差或持續(xù)時(shí)間卢肃, Pandas提供了Timedelta類型疲迂,它對(duì)應(yīng)的索引結(jié)構(gòu)是TimedeltaIndex.
這些日期/時(shí)間對(duì)象中最基本的是Timestamp和DatetimeIndex對(duì)象。盡管這些對(duì)象可以被直接調(diào)用莫湘,通常使用pd.to_datetime()函數(shù)尤蒿,它可以解析不同的格式。傳遞單日期給pd.to_datetime()產(chǎn)生一個(gè)Timestamp幅垮,傳遞一串日期腰池,默認(rèn)產(chǎn)生DatetimeIndex:
dates = pd.to_datetime([datetime(2015, 7, 3), '4th of July, 2015',
'2015-Jul-6', '07-07-2015', '20150708'])
dates
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
'2015-07-08'],
dtype='datetime64[ns]', freq=None)
任何DatetimeIndex都可以使用帶頻率代碼參數(shù)的to_period()函數(shù)轉(zhuǎn)換成PeriodIndex;使用'D'表示日頻率:
dates.to_period('D')
PeriodIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
'2015-07-08'],
dtype='int64', freq='D')
當(dāng)一個(gè)日期減去另一個(gè)日期時(shí)忙芒,就創(chuàng)建了 TimedeltaIndex:
dates - dates[0]
TimedeltaIndex(['0 days', '1 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)
規(guī)則序列:pd.date_range()
為了更方便的創(chuàng)建規(guī)則日期序列示弓,Pandas位這個(gè)目的提供了幾個(gè)函數(shù):pd.date_range()用于時(shí)間戳,pd.period_range()用于周期呵萨,pd.timedelta_range()用于時(shí)間差奏属。我們見過Python的range()和NumPy的np.arange()將起點(diǎn),終點(diǎn)和可選步長(zhǎng)轉(zhuǎn)變成序列潮峦。類似的囱皿,pd.date_range()接受起始日期勇婴,截止日期,和可選的周期代碼來創(chuàng)建日期規(guī)則序列嘱腥。默認(rèn)情況下咆耿,頻率時(shí)一天:
pd.date_range('2015-07-03', '2015-07-10')
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
'2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
dtype='datetime64[ns]', freq='D')
或者不必指定起始和終止日期,而時(shí)起始點(diǎn)和周期數(shù)量:
pd.date_range('2015-07-03', periods=8)
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
'2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
dtype='datetime64[ns]', freq='D')
間隔可以通過改變freq參數(shù)來修改爹橱,默認(rèn)間隔是D萨螺。例如,我們構(gòu)建一系列以小時(shí)為間隔的時(shí)間戳:
pd.date_range('2015-07-03', periods=8, freq='H')
DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 01:00:00',
'2015-07-03 02:00:00', '2015-07-03 03:00:00',
'2015-07-03 04:00:00', '2015-07-03 05:00:00',
'2015-07-03 06:00:00', '2015-07-03 07:00:00'],
dtype='datetime64[ns]', freq='H')
為了創(chuàng)建Period和Timedelta值愧驱,可以使用函數(shù)pd.period_range()和pd.timedelta_range()慰技。這里時(shí)一下月為周期的例子:
pd.period_range('2015-07', periods=8, freq='M')
PeriodIndex(['2015-07', '2015-08', '2015-09', '2015-10', '2015-11', '2015-12',
'2016-01', '2016-02'],
dtype='int64', freq='M')
按小時(shí)增加的持續(xù)時(shí)間序列:
pd.timedelta_range(0, periods=10, freq='H')
TimedeltaIndex(['00:00:00', '01:00:00', '02:00:00', '03:00:00', '04:00:00',
'05:00:00', '06:00:00', '07:00:00', '08:00:00', '09:00:00'],
dtype='timedelta64[ns]', freq='H')
所有這些需要理解Pandas的頻率代碼,下一部分有我們的總結(jié)组砚。
頻率和偏移
這些Pandas時(shí)間序列工具的基礎(chǔ)是頻率或日期偏移概念吻商。如同我們上面看到的D(天)和H(小時(shí))代碼,我們可以使用這類代碼來指定任何想要的周期間隔糟红。下表總結(jié)了可用的主要編碼:
Code | Description | Code | Description |
---|---|---|---|
D |
Calendar day | B |
Business day |
W |
Weekly | ||
M |
Month end | BM |
Business month end |
Q |
Quarter end | BQ |
Business quarter end |
A |
Year end | BA |
Business year end |
H |
Hours | BH |
Business hours |
T |
Minutes | ||
S |
Seconds | ||
L |
Milliseonds | ||
U |
Microseconds | ||
N |
nanoseconds |
月度艾帐,季度,年度頻率標(biāo)記在指定周期的后面盆偿。通過添加S后綴在周期后面柒爸,周期將從對(duì)應(yīng)的第一天開始計(jì)算:
| Code | Description || Code | Description |
|---------|------------------------||---------|------------------------|
| MS
| Month start ||BMS
| Business month start |
| QS
| Quarter start ||BQS
| Business quarter start |
| AS
| Year start ||BAS
| Business year start |
此外,可以通過添加三個(gè)字母的月份代碼為后綴來更改用于標(biāo)記任何季度或年度代碼的結(jié)束的最后月份:
-
Q-JAN
,BQ-FEB
,QS-MAR
,BQS-APR
, etc. -
A-JAN
,BA-FEB
,AS-MAR
,BAS-APR
, etc.
同樣的事扭,周周期的分割點(diǎn)可以同添加三字母的工作日代碼來改變:
-
W-SUN
,W-MON
,W-TUE
,W-WED
, etc.
在這之上捎稚,代碼可以和數(shù)值相結(jié)合來指定其它周期。例如對(duì)于2小時(shí)30分鐘求橄,我們可以相如下這樣結(jié)合小時(shí)(H)和分鐘(T):
pd.timedelta_range(0, periods=9, freq="2H30T")
TimedeltaIndex(['00:00:00', '02:30:00', '05:00:00', '07:30:00', '10:00:00',
'12:30:00', '15:00:00', '17:30:00', '20:00:00'],
dtype='timedelta64[ns]', freq='150T')
所有這些短碼都是指Pandas時(shí)間序列偏移的具體實(shí)例今野,它可以在pd.tseries.offsets模塊中發(fā)現(xiàn)。例如罐农,我們像如下這樣創(chuàng)建一個(gè)工作日偏移:
from pandas.tseries.offsets import BDay
pd.date_range('2015-07-01', periods=5, freq=BDay())
DatetimeIndex(['2015-07-01', '2015-07-02', '2015-07-03', '2015-07-06',
'2015-07-07'],
dtype='datetime64[ns]', freq='B')
更多過于頻率和偏移的用法討論条霜,參見Pandas文檔的"DateOffset"部分
重采樣、移位和窗口
使用日期和時(shí)間作為索引來直觀的組織和訪問數(shù)據(jù)的能力是Pandas時(shí)間管理工具重要功能涵亏。除了索引數(shù)據(jù)的一般用處(操作時(shí)自動(dòng)對(duì)齊宰睡,按直覺的數(shù)據(jù)切片和訪問),Pandas提供了幾種額外的時(shí)間序列相關(guān)操作溯乒。
使用股票價(jià)格數(shù)據(jù)作為用例夹厌,我們來看其中的幾個(gè)方法。因?yàn)镻andas大部分時(shí)在金融環(huán)境下開發(fā)的裆悄,它包含了一些特殊工具用于處理金融數(shù)據(jù)矛纹。例如,伴生的pandas-datareader包(通過conda install pandas-datareader來安裝)光稼,知道如果從不同數(shù)據(jù)源(包括或南,雅虎金融孩等,Google金融等)導(dǎo)入數(shù)據(jù)。這類我們導(dǎo)入的是谷歌收盤價(jià)記錄:
from pandas_datareader import data
goog = data.DataReader('GOOG', start='2004', end='2016',
data_source='google')
goog.head()
Open High Low Close Volume
Date
2004-08-19 49.96 51.98 47.93 50.12 NaN
2004-08-20 50.69 54.49 50.20 54.10 NaN
2004-08-23 55.32 56.68 54.47 54.65 NaN
2004-08-24 55.56 55.74 51.73 52.38 NaN
2004-08-25 52.43 53.95 51.89 52.95 NaN
為了簡(jiǎn)單起見采够,我們只使用收盤價(jià):
goog = goog['Close']
在正常的設(shè)置好Matplotlib樣板(參見Chapter 4)后肄方,我們就可以使用plot()進(jìn)行可視化了:
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn; seaborn.set()
goog.plot();
重新采樣和轉(zhuǎn)換頻率
一個(gè)對(duì)時(shí)間序列常見的需求是以更高或更低的頻率進(jìn)行采樣。這個(gè)需求可以使用resample()方法或更簡(jiǎn)單的asfreq()方法來實(shí)現(xiàn)蹬癌。兩者之間的主要區(qū)別是resample()本質(zhì)上是數(shù)據(jù)聚合权她,而asfreq()是數(shù)據(jù)選擇。
看一眼谷歌收盤價(jià)格逝薪,讓我們比較在數(shù)據(jù)下采樣時(shí)兩者的返回值隅要。這里我們將都商業(yè)年末數(shù)據(jù)重新采樣:
goog.plot(alpha=0.5, style='-')
goog.resample('BA').mean().plot(style=':')
goog.asfreq('BA').plot(style='--');
plt.legend(['input', 'resample', 'asfreq'],
loc='upper left');
注意區(qū)別:在每個(gè)點(diǎn),resample報(bào)告的是前一年數(shù)據(jù)的均值董济,而asfreq報(bào)告的是當(dāng)年的值步清。
對(duì)于上采樣,resample()和asfreq()大體相當(dāng)虏肾,盡管resample有更多的可用選項(xiàng)廓啊。在本例中,兩種方法默認(rèn)都是將上采樣點(diǎn)置為空封豪,置為NA值谴轮。如同之前pd。fillna()函數(shù)討論過的撑毛,asfred()接收一個(gè)方法參數(shù)來指定如何插值书聚。這里,我們將重新采樣工作日為日頻率(包括周末):
fig, ax = plt.subplots(2, sharex=True)
data = goog.iloc[:10]
data.asfreq('D').plot(ax=ax[0], marker='o')
data.asfreq('D', method='bfill').plot(ax=ax[1], style='-o')
data.asfreq('D', method='ffill').plot(ax=ax[1], style='--o')
ax[1].legend(["back-fill", "forward-fill"]);
上面板是默認(rèn)情況:非工作日設(shè)置為空藻雌,沒有在圖中顯示出來。下面板顯示兩個(gè)填充策略的不同:前向填充和后向填充斩个。
時(shí)間移動(dòng)
另一個(gè)時(shí)間序列特殊操作是按時(shí)間移動(dòng)數(shù)據(jù)胯杭。Pandas有兩個(gè)緊密聯(lián)系的方法用來計(jì)算移動(dòng):shift()和tshift(),兩者之間的區(qū)別是shift()移動(dòng)數(shù)據(jù)受啥,而tshift()移動(dòng)索引做个。兩種情況下,移位都是以頻率倍數(shù)來指定的滚局。
fig, ax = plt.subplots(3, sharey=True)
# apply a frequency to the data
goog = goog.asfreq('D', method='pad')
goog.plot(ax=ax[0])
goog.shift(900).plot(ax=ax[1])
goog.tshift(900).plot(ax=ax[2])
# legends and annotations
local_max = pd.to_datetime('2007-11-05')
offset = pd.Timedelta(900, 'D')
ax[0].legend(['input'], loc=2)
ax[0].get_xticklabels()[2].set(weight='heavy', color='red')
ax[0].axvline(local_max, alpha=0.3, color='red')
ax[1].legend(['shift(900)'], loc=2)
ax[1].get_xticklabels()[2].set(weight='heavy', color='red')
ax[1].axvline(local_max + offset, alpha=0.3, color='red')
ax[2].legend(['tshift(900)'], loc=2)
ax[2].get_xticklabels()[1].set(weight='heavy', color='red')
ax[2].axvline(local_max + offset, alpha=0.3, color='red');
我們看到shift(900)移動(dòng)了900天的數(shù)據(jù)居暖,把它的一些數(shù)據(jù)推到圖表的結(jié)尾(并且在另一端留下空值),而tshift(900)移動(dòng)了900天的索引數(shù)據(jù)藤肢。
這類移動(dòng)的一般用于計(jì)算基于時(shí)間的不同太闺。例如,我們使用移動(dòng)數(shù)據(jù)來計(jì)算按年的谷歌股票投資回報(bào):
ROI = 100 * (goog.tshift(-365) / goog - 1)
ROI.plot()
plt.ylabel('% Return on Investment');
這幫我們看清Google股票的整體趨勢(shì):截止目前為止嘁圈,投資Google最賺錢的時(shí)間是其IPO之后省骂,和2009衰退中期蟀淮。
移動(dòng)窗口
移動(dòng)統(tǒng)計(jì)是Pandas實(shí)現(xiàn)的第三類特殊時(shí)間序列操作。它可用公共Series和DataFrame對(duì)象屬性的rolling()函數(shù)來實(shí)現(xiàn)钞澳,該函數(shù)返回值同我們見到的groupby操作(見Aggregation and Grouping).很類似怠惶。
移動(dòng)視圖有幾個(gè)默認(rèn)的聚合操作。
例如轧粟,這是谷歌股票歷年價(jià)格窗口中心移動(dòng)均值和標(biāo)準(zhǔn)差策治。
rolling = goog.rolling(365, center=True)
data = pd.DataFrame({'input': goog,
'one-year rolling_mean': rolling.mean(),
'one-year rolling_std': rolling.std()})
ax = data.plot(style=['-', '--', ':'])
ax.lines[0].set_alpha(0.3)
同group-by操作一樣,aggregate()和apply()方法也可以被用來定制移動(dòng)窗口計(jì)算兰吟。
進(jìn)一步學(xué)習(xí)
這部分提供了對(duì)Pandas時(shí)間序列工具種最基本功能的簡(jiǎn)要總結(jié)览妖;想要更完整的討論,參見Pandas在線文檔"Time Series/Date" section
另一個(gè)優(yōu)秀的資源是Wes McKinney寫的Python for Data Analysis揽祥。雖然距現(xiàn)在已經(jīng)幾年了讽膏,這本書是使用Pandas及其重要的資源。特別的該書強(qiáng)調(diào)了時(shí)間序列工具在商業(yè)和金融環(huán)境中的使用拄丰,并且特別關(guān)注了商業(yè)日歷府树,時(shí)區(qū)等主題的細(xì)節(jié)。
一如往常料按,你可以使用Ipython的幫助功能來探索和嘗試這里面討論的功能和函數(shù)的可用選項(xiàng)奄侠。我發(fā)現(xiàn)這經(jīng)常是學(xué)習(xí)Python新工具最后的方式。
例子:可視化西雅圖自行車數(shù)量
作為一個(gè)更多處理時(shí)間序列數(shù)據(jù)的例子载矿,讓我們看看西雅圖 Fremont橋自行車通過數(shù)量垄潮。數(shù)據(jù)來自于自動(dòng)自行車計(jì)數(shù)器,它于2012年下半年安裝闷盔,在橋的東西人行道有感應(yīng)傳感器弯洗。小時(shí)自行車數(shù)量可用從http://data.seattle.gov/下載。
CSV文件可用通過如下方式下載:
# !curl -o FremontBridge.csv https://data.seattle.gov/api/views/65db-xm6k/rows.csv?accessType=DOWNLOAD
數(shù)據(jù)集下載完畢后逢勾,我們使用Pandas來讀取CSV文件生成一個(gè)DataFrame牡整。我們指定時(shí)間作為索引,并且希望日期被自動(dòng)解析:
data = pd.read_csv('FremontBridge.csv', index_col='Date', parse_dates=True)
data.head()
Fremont Bridge West Sidewalk Fremont Bridge East Sidewalk
Date
2012-10-03 00:00:00 4.0 9.0
2012-10-03 01:00:00 4.0 6.0
2012-10-03 02:00:00 1.0 1.0
2012-10-03 03:00:00 2.0 3.0
2012-10-03 04:00:00 6.0 1.0
為了方便溺拱,我們進(jìn)一步處理數(shù)據(jù)集逃贝,縮短列名稱,添加一個(gè)"Total"列:
data.columns = ['West', 'East']
data['Total'] = data.eval('West + East')
現(xiàn)在讓我們看看數(shù)據(jù)的總結(jié)統(tǒng)計(jì):
data.dropna().describe()
West East Total
count 35752.000000 35752.000000 35752.000000
mean 61.470267 54.410774 115.881042
std 82.588484 77.659796 145.392385
min 0.000000 0.000000 0.000000
25% 8.000000 7.000000 16.000000
50% 33.000000 28.000000 65.000000
75% 79.000000 67.000000 151.000000
max 825.000000 717.000000 1186.000000
可視化數(shù)據(jù)
通過可視化我們可以獲取對(duì)數(shù)據(jù)集更多的了解迫摔。讓我們由畫原始數(shù)據(jù)開始:
%matplotlib inline
import seaborn; seaborn.set()
data.plot()
plt.ylabel('Hourly Bicycle Count');
約25,000小時(shí)的采樣太密集了沐扳。我們可以通過粗粒度重新采樣數(shù)據(jù)來看得更清楚.讓我們按周統(tǒng)計(jì):
weekly = data.resample('W').sum()
weekly.plot(style=[':', '--', '-'])
plt.ylabel('Weekly bicycle count');
這里顯示出一些有趣的季節(jié)趨勢(shì):如你所想,夏天人們騎自行車比冬天多句占,并且即使在同樣季節(jié)沪摄,每周的使用量也不同。
另一種手動(dòng)聚合數(shù)據(jù)的方式是利用pd.rolling_mean()函數(shù)來做rolling平均。這里做一個(gè)30天的數(shù)據(jù)rolling平均卓起,確保中心窗口:
daily = data.resample('D').sum()
daily.rolling(30, center=True).sum().plot(style=[':', '--', '-'])
plt.ylabel('mean hourly count');
結(jié)果的鋸齒狀是由于窗口的硬切斷和敬。我們可以使用窗口函數(shù)(如Gaussian窗口)來得到一個(gè)平滑rolling平均版本。下面的代碼既指定了窗口寬度(50天)也指定了窗口內(nèi)的高斯寬度(10天)
daily.rolling(50, center=True,
win_type='gaussian').sum(std=10).plot(style=[':', '--', '-']);
挖掘數(shù)據(jù)
這些光滑的視圖對(duì)于獲得數(shù)據(jù)大體趨勢(shì)很有用戏阅,但它們隱藏了許多有趣的細(xì)節(jié)昼弟。例如,我想要看一下每天平均交通量奕筐。我們可以使用在Aggregation and Grouping討論的Groupby功能
by_time = data.groupby(data.index.time).mean()
hourly_ticks = 4 * 60 * 60 * np.arange(6)
by_time.plot(xticks=hourly_ticks, style=[':', '--', '-']);
小時(shí)交通量是典型的雙峰分布舱痘,峰值在早上8點(diǎn)和晚上5點(diǎn)。這是跨橋通勤證據(jù)的重要組成部分离赫。這也進(jìn)一步證明了西人行道和東人行道的不同:前者(通常是去市中心)峰值時(shí)在早晨芭逝,后者(通常時(shí)離開市中心),峰值在晚上渊胸。
我們好奇的是每周的情況是怎樣旬盯。再次,我們使用簡(jiǎn)單的groupby:
by_weekday = data.groupby(data.index.dayofweek).mean()
by_weekday.index = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']
by_weekday.plot(style=[':', '--', '-']);
這顯示了工作日與周末的巨大區(qū)別翎猛,周一到周五的騎行人數(shù)是周末的二倍胖翰。
記住這些,我們來做一個(gè)符合的GroupBy來看一下工作日對(duì)周末的小時(shí)趨勢(shì)切厘。我們使用一個(gè)標(biāo)簽來標(biāo)記分組周末和其它工作日:
weekend = np.where(data.index.weekday < 5, 'Weekday', 'Weekend')
by_time = data.groupby([weekend, data.index.time]).mean()
現(xiàn)在我們使用Matplotlib工具Multiple Subplots來并排的繪制兩個(gè)面板:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(14, 5))
by_time.ix['Weekday'].plot(ax=ax[0], title='Weekdays',
xticks=hourly_ticks, style=[':', '--', '-'])
by_time.ix['Weekend'].plot(ax=ax[1], title='Weekends',
xticks=hourly_ticks, style=[':', '--', '-']);
結(jié)果非常有趣:我們看到在工作日是一個(gè)雙峰通勤模型萨咳,在周末是一個(gè)單峰游憩模式。發(fā)掘數(shù)據(jù)中更多細(xì)節(jié)會(huì)很有趣疫稿,并且檢查天氣培他,溫度,一年中時(shí)間遗座,及其它因素對(duì)人們出行模式的影響舀凛;對(duì)于進(jìn)一步討論,參見博客 "Is Seattle Really Seeing an Uptick In Cycling?"