11.處理時(shí)間序列

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í)間單元的 2^{64}
例如边苹,如果你想要一納秒的時(shí)間精度陵且,那么只能編碼2^{64}納秒的信息,即少于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();
03.11-Working-with-Time-Series_110_0.png

重新采樣和轉(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');
03.11-Working-with-Time-Series_113_0.png

注意區(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"]);
03.11-Working-with-Time-Series_118_0.png

上面板是默認(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');
03.11-Working-with-Time-Series_123_0.png

我們看到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');
03.11-Working-with-Time-Series_126_0.png

這幫我們看清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)
03.11-Working-with-Time-Series_131_0.png

同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');
03.11-Working-with-Time-Series_151_0.png

約25,000小時(shí)的采樣太密集了沐扳。我們可以通過粗粒度重新采樣數(shù)據(jù)來看得更清楚.讓我們按周統(tǒng)計(jì):

weekly = data.resample('W').sum()
weekly.plot(style=[':', '--', '-'])
plt.ylabel('Weekly bicycle count');
03.11-Working-with-Time-Series_154_0.png

這里顯示出一些有趣的季節(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');
03.11-Working-with-Time-Series_157_0.png

結(jié)果的鋸齒狀是由于窗口的硬切斷和敬。我們可以使用窗口函數(shù)(如Gaussian窗口)來得到一個(gè)平滑rolling平均版本。下面的代碼既指定了窗口寬度(50天)也指定了窗口內(nèi)的高斯寬度(10天)

daily.rolling(50, center=True,
              win_type='gaussian').sum(std=10).plot(style=[':', '--', '-']);
03.11-Working-with-Time-Series_160_0.png

挖掘數(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=[':', '--', '-']);
03.11-Working-with-Time-Series_166_0.png

這顯示了工作日與周末的巨大區(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=[':', '--', '-']);
03.11-Working-with-Time-Series_172_0.png

結(jié)果非常有趣:我們看到在工作日是一個(gè)雙峰通勤模型萨咳,在周末是一個(gè)單峰游憩模式。發(fā)掘數(shù)據(jù)中更多細(xì)節(jié)會(huì)很有趣疫稿,并且檢查天氣培他,溫度,一年中時(shí)間遗座,及其它因素對(duì)人們出行模式的影響舀凛;對(duì)于進(jìn)一步討論,參見博客 "Is Seattle Really Seeing an Uptick In Cycling?"

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末员萍,一起剝皮案震驚了整個(gè)濱河市腾降,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碎绎,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抗果,死亡現(xiàn)場(chǎng)離奇詭異筋帖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)冤馏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門日麸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事代箭《栈” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵嗡综,是天一觀的道長(zhǎng)乙帮。 經(jīng)常有香客問我,道長(zhǎng)极景,這世上最難降的妖魔是什么察净? 我笑而不...
    開封第一講書人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮盼樟,結(jié)果婚禮上氢卡,老公的妹妹穿的比我還像新娘。我一直安慰自己晨缴,他們只是感情好译秦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著击碗,像睡著了一般筑悴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上延都,一...
    開封第一講書人閱讀 49,798評(píng)論 1 290
  • 那天雷猪,我揣著相機(jī)與錄音,去河邊找鬼晰房。 笑死求摇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殊者。 我是一名探鬼主播与境,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼猖吴!你這毒婦竟也來了摔刁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤海蔽,失蹤者是張志新(化名)和其女友劉穎共屈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體党窜,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拗引,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幌衣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矾削。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哼凯,到底是詐尸還是另有隱情欲间,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布断部,位于F島的核電站猎贴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏家坎。R本人自食惡果不足惜嘱能,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虱疏。 院中可真熱鬧惹骂,春花似錦、人聲如沸做瞪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)装蓬。三九已至著拭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牍帚,已是汗流浹背儡遮。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暗赶,地道東北人鄙币。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蹂随,于是被迫代替她去往敵國(guó)和親十嘿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容