介紹
Pandas 是非常著名的開源數據處理庫,我們可以通過它完成對數據集進行快速讀取、轉換、過濾、分析等一系列操作。同樣,Pandas 已經被證明為是非常強大的用於處理時間序列數據的工具。本節將介紹所有 Pandas 在時間序列數據上的處理方法。
知識點
- 創建時間對象
- 時間索引對象
- 時間算術方法
創建時間對象
在 Pandas 中關於時間序列的常見對象有 6 種,分別是 Timestamp(時間戳)、DatetimeIndex(時間戳索引)、Period(時間段)、PeriodIndex(時間段索引)、以時間為元素的 Series 和以及以時間索引的 DataFrame。本小節學習如何創建以上對象。
創建時間戳
Timestamp 時間戳表示時間軸上的某一點,以下不同代碼都可以生成相同時間戳。
創建時間為 2018 年 10 月 1 日的時間戳。
import pandas as pd
pd.Timestamp(2018, 10, 1)
也可以使創建的時間精確到時分秒。
pd.Timestamp("2018-10-1 10:00:1")
from datetime import datetime
pd.Timestamp(datetime(2018, 10, 1))
創建時間段
Period 時間段表示時間軸上的某一區間,以下代碼都可以生成相同時間段。
pd.Period('2018-10')
Period()
函數后面通常有兩個參數,第二個 freq
參數決定時間段的分割長度。
創建頻率為日的時間段。
pd.Period('2018-10', freq='D')
創建時間元素的 Series
Pandas 中常用 to_datetime()
函數可以創建以時間為元素的 Series。
創建一個 Series,以三個時間的字符串作為元素。
df = ['2018-08-01', '2018-09-01', '2018-10-01']
pd.to_datetime(df)
可以使用多種方法創建時間元素的 Series。
df = pd.Series(['Sep 30, 2018', '2018-10-1', None])
pd.to_datetime(df)
df = pd.DataFrame({'year': [2017, 2018],
'month': [9, 10],
'day': [30, 1],
'hour': [23, 0]})
pd.to_datetime(df)
創建時間索引
要生成帶有時間戳的索引,可以使用 DatetimeIndex()
構造函數,並傳入列表或 Series 對象:
dates = ['2018-08-01', '2018-09-01', '2018-10-01']
index = pd.DatetimeIndex(dates)
index
實際運用中我們經常需要大量的的時間戳的索引。可以使用 date_range()
和 bdate_range()
來批量創建相同時間間隔的時間戳索引。
創建以 2018 年 9 月 30 日為開始的 250 條時間索引,相鄰索引間隔時間長度為一個月。
index = pd.date_range('2018-9-30', periods=250, freq='M')
index
創建以 2018 年 10 月 1 日為開始的 111 條時間索引,相鄰索引間隔時間長度為一個工作日。
index = pd.bdate_range('2018-10-1', periods=111)
index
在 date_range()
和 bdate_range()
中可以巧妙使用 start,end, periods,freq 等參數的各種組合輕松批量創建時間索引。
在 2017 年 10 月 1 日到 2018 年 10 月 1 日間,每隔一周創建一條索引。
start = datetime(2017, 10, 1)
end = datetime(2018, 10, 1)
rng = pd.date_range(start, end, freq='W')
rng
從 2018 年 10 月 1 日向前每隔一個工作日創建一條索引,共 250 條。
pd.bdate_range(end=end, periods=250)
同理,時間段也能作為索引使用,需要用到 period_range()
。
從 2018 年 9 月 30 日向后創建 666 條索引,相鄰索引間隔時間長度為一天。
pi = pd.period_range('2018-9-30', periods=666)
pi
創建以時間為索引的 Series 對象
以時間為索引的 Series 對象指的是在該 Series 中,元素的索引不再是 1、2、3、4、5……這樣的序號,而是有序的日期和時間。
import numpy as np
dates = [pd.Timestamp('2018-08-01'), pd.Timestamp('2018-09-01'),
pd.Timestamp('2018-10-01')] # 創建三個時間元素。
ts = pd.Series(np.random.randn(3), dates) # 創建索引值為隨機數的 Series 對象。
ts
同樣,時間段也能作為索引。
periods = [pd.Period('2018-08'), pd.Period('2018-09'), pd.Period('2018-10')]
ts = pd.Series(np.random.randn(3), periods)
ts
我們可以批量創建索引后再創建以時間為索引的 Series 對象。創建索引值為隨機數的 Series 對象,長度與 rng 長度相同。
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
時間段也能作為索引創建 DataFrame 對象。在 2017 年第一季度和 2018 年第四季度之間每隔一個季度創建一條索引。
prng = pd.period_range('2017Q1', '2018Q4', freq='Q-NOV')
# 行索引為時間段索引,列索引為 A。
ps = pd.DataFrame(np.random.rand(len(prng)), columns=[
'A'], index=prng)
ps
時間索引對象處理
以時間戳為索引的 Series、DataFrame 對象具有與普通列表近乎相同的操作,且更具智能化。
查找
簡單查找。
ts
查找前 10 條索引記錄。
ts[:10]
每隔 1 條記錄查找 1 條索引記錄。
ts[::2]
查找第 0、2、6 條索引記錄。
ts[[0, 2, 6]]
基於時間索引的精確查找。查找索引為 2018 年 9 月 30 日的值。
ts["09/30/2018"]
ts[datetime(2018, 9, 30)]
基於索引的范圍查找。查找索引時間在 2017 年內的所有記錄。
ts["2017"]
查找索引時間在 2018 年 9 月內的所有記錄。
ts["2018-9"]
以時間段為索引的 DataFrame 對象的查找規則與以時間戳的相同。
ps
2018 年的第一個季度規定為 2017 年的 12 月初到 2018 年的 2 月末。
查找 2017 年內的所有季度的記錄。
ps["2017"]
查找 2017 年 12 月 31 日前的所有季度的記錄。
ps[:datetime(2017, 12, 31)]
查找 2018 年 6 月內的所有季度的記錄。
ps["2018-06"]
切片
使用 truncate()
切下 2017 年 11 月 26 日與 2018 年 4 月 29 日間的記錄。
ts.truncate(before='11/26/2017', after='4/29/2018')
移動
將時間索引 Series 中的值向后和向前移動。其方法是 shift()
。
ts = ts[:5] # 取前 5 條數據方便觀察。
ts
將元素列向下移動一條。
ts.shift(1)
除了元素可以被移動,索引本身也能被移動,需要加上 freq
參數。將索引列向上移動一條:
ts.shift(1, freq='W')
重采樣
重采樣可以通俗得理解為改變時間索引的個數,通過增大或減小相鄰索引的時間間隔以達到減小或增加索引數量的效果,在 Pandas 中使用 resample()
函數。
下采樣:增大時間間隔,減少記錄的數量。創建從 2018 年 10 月 1 日開始的日間隔索引的 Series 。
rng = pd.date_range('10/1/2018', periods=10, freq='D')
ts = pd.Series(np.random.randint(0, 50, len(rng)), index=rng)
ts
原先索引的日間隔被擴大為周間隔,並以周末為索引采樣點,采樣點的索引值為所有未被索引值的和。
ts.resample('W').sum()
同樣也能使采樣點的索引值為所有未被索引值的平均值。
ts.resample('W').mean()
使用 ohlc()
函數對所用未被采樣值進行統計。
ts.resample('W').ohlc()
上采樣:減小時間間隔頻率,增加記錄的數量。
原來間隔為日的索引列,間隔被縮小成 12 小時,增加采樣點的值為空值。
ts.resample('12H').asfreq()
ffill()
函數可以將新增的索引值以相鄰的前一條索引值進行填充。
ts.resample('12H').ffill()
時間的算術方法
常用時間的算術規則
下表是 Pandas 內建的一些時間類,常用於時間索引的位移。
首先要導入 pandas.tseries.offsets
模塊,Pandas 所有常用時間類都在該模塊中。
d = pd.Timestamp(2018, 10, 1, 10, 1, 1)
d
使用 DateOffset()
實現時間戳位移。
向后移動一個月零兩天。
from pandas.tseries.offsets import DateOffset
d + DateOffset(months=1, days=2)
也可以用時間戳加減常用時間類以實現時間戳位移。向前移動 10 個工作日。
from pandas.tseries.offsets import BDay
d - 10 * BDay()
向后移動一個月末。
from pandas.tseries.offsets import BMonthEnd
d + BMonthEnd()
個性化定制日期。雖然日歷規定年末是 12 月,加入參數后相當於人為規定 2 月是年末。
向后移動到上兩個年末。
from pandas.tseries.offsets import YearEnd
d + YearEnd(month=2)
向前移動到上一個周四。
from pandas.tseries.offsets import Week
d - Week(weekday=4)
可以使用 rollforward()
將指定時間向前或向后移動到一個制定常用時間類的時間戳上。將時間移動到下一個月末:
offset = BMonthEnd()
offset.rollforward(d)
將時間移動到上一個月末。
offset.rollback(d)
偏移也同樣適用於時間索引
rng
所有的時間索引向后移動兩日。
rng + DateOffset(days=2)
所有的時間索引向后移動兩個工作日。
rng + 2*BDay()
所有的時間索引向后移動 15 分鍾。
from pandas.tseries.offsets import Minute
rng + Minute(15)
下列是常用時間系列頻率參數,上面小節經常出現,現在以一個表格作詳細說明。
參數名 | 說明 |
---|---|
B | 工作日頻率 |
C | 定制工作日頻率 |
D | 日歷日頻率 |
W | 每周頻率 |
M | 月結束頻率 |
SM | 半月結束頻率(15 個月和月末) |
BM | 業務月末頻率 |
CBM | 定制業務月末頻率 |
MS | 月起始頻率 |
sMs | 半月起始頻率(第 1 和 15) |
BMS | 業務月開始頻率 |
CBMS | 定制商業月份開始頻率 |
Q | 四分頻結束頻率 |
BQ | 業務四分之一頻率 |
QS | 四分頻啟動頻率 |
BQS | 業務季開始頻率 |
A | 年結束頻率 |
BA | 業務年結束頻率 |
AS | 年起始頻率 |
BAS | 業務年開始頻率 |
BH | 工作時間頻率 |
H | 每小時頻率 |
T, min | 分鍾頻率 |
S | 次頻 |
L, ms | 毫秒 |
U, uS | 微秒 |
N | 納秒 |
使用常用頻率參數組合創建時間索引。
創建 10 條以 2018 年 10 月 1 日為開始,間隔為 1 天 1 小時 1 分鍾 10 微秒的時間索引。
pd.date_range("2018-10-1", periods=10, freq='1D1H1min10U')
以下頻率參數可以指定后綴以達到改變默認間隔點的效果。

創建 10 條以 2018 年 10 月 1 日為開始,間隔為每周三的時間索引。
pd.date_range("2018-10-1", periods=10, freq='W-WED')
在使用特定頻率(MonthEnd,MonthBegin,WeekEnd 等)的參數時,如果起始時間是剛好在頻率點上,使用 n
參數可以決定是否讓該點參與計算。
n=1
時參與計算。
from pandas.tseries.offsets import MonthBegin
pd.Timestamp('2018-10-1') + MonthBegin(n=1)
n=0
時不參與計算。
pd.Timestamp('2018-10-1') + MonthBegin(n=0)
下采樣聚合
下采樣中的聚合是指下采樣后,對未被采樣到的點進行的一系列計算。
創建 100 個日歷日為時間索引的 DataFrame,將其以月頻率下采樣。
df = pd.DataFrame(np.random.rand(100, 3),
index=pd.date_range('10/1/2018', freq='D', periods=100),
columns=['A', 'B', 'C'])
r = df.resample('M')
r
對未采樣點求和,結果保存在采樣點的值中。
r.sum()
在下采樣后也能進行查找操作。選擇 A、C 列后取均值計算。
r[['A', 'C']].mean()
使用 agg()
同時進行不同的計算。對采樣結果進行取和與取均值計算。
r.agg([np.sum, np.mean])
選擇 A 列,同時進行取和,取均值,取標准差計算。
r['A'].agg([np.sum, np.mean, np.std]
對 A 列求和與標准差,對 B 列求均值與標准差。
r.agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
總結
本章節介紹了 Pandas 對時間序列數據的基本處理操作。重點演示了時間的創建、時間索引對象的處理、時間的相關計算。當然,文中對這些方法的介紹依然還不夠詳細。如果你需要在實際工作中進行更復雜的時間數據處理,還需要深刻理解文中的基本演示,改編或組合出更高級的功能,這樣才能發揮出 Pandas 的強大作用。