Periods
(周期)表示时间跨度(timespans
),比如天,月,季,年。Period
类表示的就是这种数据类型,构建的时候需要用字符串或整数,以及一个频度(关于频度的代码可以看11.4中的表格):
import numpy as np
import pandas as pd
p = pd.Period(2007, freq='A-DEC')
p
Period('2007', 'A-DEC')
在这个例子里,Period
对象代表了整个2007
年一年的时间跨度,从1月1日到12月31日。在Period
对象上进行加减,会有和对频度进行位移(shifting
)一样的效果:
p + 5
Period('2012', 'A-DEC')
p - 2
Period('2005', 'A-DEC')
如果两个周期有相同的频度,二者的区别就是它们之间有多少个单元(units
):
pd.Period('2014', freq='A-DEC') - p
7
固定范围的周期(Regular ranges of periods
)可以通过period_range
函数创建:
rng = pd.period_range('2000-01-01', '2000-06-03', freq='M')
rng
PeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='int64', freq='M')
PeriodIndex
类能存储周期组成的序列,而且可以作为任何pandas
数据结构中的轴索引(axis index
):
pd.Series(np.random.randn(6), index=rng)
2000-01 0.439035
2000-02 -0.231125
2000-03 -1.085106
2000-04 -1.909902
2000-05 1.478810
2000-06 0.656713
Freq: M, dtype: float64
如果我们有字符串组成的数组,可以使用PeriodIndex
类:
values = ['2001Q3', '2002Q2', '2003Q1']
index = pd.PeriodIndex(values, freq='Q-DEC')
index
PeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='int64', freq='Q-DEC')
通过使用asfreq
方法,Periods
和PeriodIndex
对象能被转换为其他频度。例如,假设我们有一个年度期间(annual period
),并且想要转换为月度期间(monthly period
),做法非常直观:
p = pd.Period('2007', freq='A-DEC')
p
Period('2007', 'A-DEC')
p.asfreq('M', how='start')
Period('2007-01', 'M')
p.asfreq('M', how='end')
Period('2007-12', 'M')
我们可以认为Period('2007', freq='A-DEC')
是某种指向时间跨度的光标,而这个时间跨度被细分为月度期间。可以看下面的图示:
如果一个财政年度(fiscal year
)是在1月结束,而不是12月,那么对应的月度期间会不一样:
p = pd.Period('2007', freq='A-JUN')
p
Period('2007', 'A-JUN')
p.asfreq('M', 'start')
Period('2006-07', 'M')
p.asfreq('M', 'end')
Period('2007-06', 'M')
当我们转换高频度为低频度时,pandas
会根据 subperiod
(次周期;子周期)的归属来决定superperiod
(超周期;母周期)。例如,在A-JUN
频度中,月份Aug-2007
其实是个2008
周期的一部分:
p = pd.Period('Aug-2007', 'M')
p.asfreq('A-JUN')
Period('2008', 'A-JUN')
整个PeriodIndex
对象或时间序列可以被转换为一样的语义(semantics
):
rng = pd.period_range('2006', '2009', freq='A-DEC')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
2006 0.391629
2007 0.497413
2008 -1.685639
2009 0.939885
Freq: A-DEC, dtype: float64
ts.asfreq('M', how='start')
2006-01 0.391629
2007-01 0.497413
2008-01 -1.685639
2009-01 0.939885
Freq: M, dtype: float64
这里,年度周期可以用月度周期替换,对应的第一个月也会包含在每个年度周期里。如果我们想要每年的最后一个工作日的话,可以使用'B'
频度,并指明我们想要周期的结尾:
ts.asfreq('B', how='end')
2006-12-29 0.391629
2007-12-31 0.497413
2008-12-31 -1.685639
2009-12-31 0.939885
Freq: B, dtype: float64
季度数据经常出现在会计,经济等领域。大部分季度数据都与财政年度结束日(fiscal year end
)相关,比如12
月最后一个工作日。因此,根据财政年度结束的不同,周期2012Q4
也有不同的意义。pandas
支持所有12
个周期频度,从Q-JAN
到Q-DEC
:
p = pd.Period('2012Q4', freq='Q-JAN')
p
Period('2012Q4', 'Q-JAN')
如果是财政年度结束日在一月份,那么2012Q4
代表从11
月到1
月,可以用日频度查看。
p.asfreq('D', 'start')
Period('2011-11-01', 'D')
p.asfreq('D', 'end')
Period('2012-01-31', 'D')
因此,做些简单的周期运算也是可能的,例如,获得每个季度的,第二个到最后一个工作日的,下午4点的时间戳:
p4pm = (p.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
p4pm
Period('2012-01-30 16:00', 'T')
p4pm.to_timestamp()
Timestamp('2012-01-30 16:00:00')
还可以用period_range
产生季度范围数据。运算方法也一样:
rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN')
ts = pd.Series(np.arange(len(rng)), index=rng)
ts
2011Q3 0
2011Q4 1
2012Q1 2
2012Q2 3
2012Q3 4
2012Q4 5
Freq: Q-JAN, dtype: int64
new_rng = (rng.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
ts.index = new_rng.to_timestamp()
ts
2010-10-28 16:00:00 0
2011-01-28 16:00:00 1
2011-04-28 16:00:00 2
2011-07-28 16:00:00 3
2011-10-28 16:00:00 4
2012-01-30 16:00:00 5
dtype: int64
用时间戳作为索引的Series
和DataFrame
对象,可以用to_period
方法转变为周期:
rng = pd.date_range('2000-01-01', periods=3, freq='M')
ts = pd.Series(np.random.randn(3), index=rng)
ts
2000-01-31 1.556049
2000-02-29 -0.708661
2000-03-31 -0.154767
Freq: M, dtype: float64
pts = ts.to_period()
pts
2000-01 1.556049
2000-02 -0.708661
2000-03 -0.154767
Freq: M, dtype: float64
因为周期是不重复的时间跨度(non-overlapping timespans
),一个时间戳只能属于一个有指定频度的单独周期。尽管默认情况下新的PeriodIndex
的频度会从时间戳中来推测,但我们也可以自己设定想要的频度。结果中有重复的周期也没有关系:
rng = pd.date_range('1/29/2000', periods=6, freq='D')
rng
DatetimeIndex(['2000-01-29', '2000-01-30', '2000-01-31', '2000-02-01',
'2000-02-02', '2000-02-03'],
dtype='datetime64[ns]', freq='D')
ts2 = pd.Series(np.random.randn(6), index=rng)
ts2
2000-01-29 1.115254
2000-01-30 -1.813124
2000-01-31 0.970670
2000-02-01 1.306337
2000-02-02 0.673274
2000-02-03 -0.105436
Freq: D, dtype: float64
ts2.to_period('M')
2000-01 1.115254
2000-01 -1.813124
2000-01 0.970670
2000-02 1.306337
2000-02 0.673274
2000-02 -0.105436
Freq: M, dtype: float64
想转换回时间戳的话,使用to_timestamp
:
pts = ts2.to_period()
pts
2000-01-29 1.115254
2000-01-30 -1.813124
2000-01-31 0.970670
2000-02-01 1.306337
2000-02-02 0.673274
2000-02-03 -0.105436
Freq: D, dtype: float64
pts.to_timestamp(how='end')
2000-01-29 1.115254
2000-01-30 -1.813124
2000-01-31 0.970670
2000-02-01 1.306337
2000-02-02 0.673274
2000-02-03 -0.105436
Freq: D, dtype: float64
有固定频度的数据集,有时会在很多列上存储时间跨度信息。例如,在下面的宏观经济数据及上,年度和季度在不同的列:
data = pd.read_csv('../examples/macrodata.csv')
data.head()
year | quarter | realgdp | realcons | realinv | realgovt | realdpi | cpi | m1 | tbilrate | unemp | pop | infl | realint | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1959.0 | 1.0 | 2710.349 | 1707.4 | 286.898 | 470.045 | 1886.9 | 28.98 | 139.7 | 2.82 | 5.8 | 177.146 | 0.00 | 0.00 |
1 | 1959.0 | 2.0 | 2778.801 | 1733.7 | 310.859 | 481.301 | 1919.7 | 29.15 | 141.7 | 3.08 | 5.1 | 177.830 | 2.34 | 0.74 |
2 | 1959.0 | 3.0 | 2775.488 | 1751.8 | 289.226 | 491.260 | 1916.4 | 29.35 | 140.5 | 3.82 | 5.3 | 178.657 | 2.74 | 1.09 |
3 | 1959.0 | 4.0 | 2785.204 | 1753.7 | 299.356 | 484.052 | 1931.3 | 29.37 | 140.0 | 4.33 | 5.6 | 179.386 | 0.27 | 4.06 |
4 | 1960.0 | 1.0 | 2847.699 | 1770.5 | 331.722 | 462.199 | 1955.5 | 29.54 | 139.6 | 3.50 | 5.2 | 180.007 | 2.31 | 1.19 |
data.year[:5]
1959Q1 1959.0
1959Q2 1959.0
1959Q3 1959.0
1959Q4 1959.0
1960Q1 1960.0
Freq: Q-DEC, Name: year, dtype: float64
data.quarter[:5]
1959Q1 1.0
1959Q2 2.0
1959Q3 3.0
1959Q4 4.0
1960Q1 1.0
Freq: Q-DEC, Name: quarter, dtype: float64
通过把这些数组传递给PeriodIndex
,并指定频度,我们可以把这些合并得到一个新的DataFrame
:
index = pd.PeriodIndex(year=data.year, quarter=data.quarter,
freq='Q-DEC')
index
PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
'1960Q3', '1960Q4', '1961Q1', '1961Q2',
...
'2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
'2008Q4', '2009Q1', '2009Q2', '2009Q3'],
dtype='int64', length=203, freq='Q-DEC')
data.index = index
data.head()
year | quarter | realgdp | realcons | realinv | realgovt | realdpi | cpi | m1 | tbilrate | unemp | pop | infl | realint | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1959Q1 | 1959.0 | 1.0 | 2710.349 | 1707.4 | 286.898 | 470.045 | 1886.9 | 28.98 | 139.7 | 2.82 | 5.8 | 177.146 | 0.00 | 0.00 |
1959Q2 | 1959.0 | 2.0 | 2778.801 | 1733.7 | 310.859 | 481.301 | 1919.7 | 29.15 | 141.7 | 3.08 | 5.1 | 177.830 | 2.34 | 0.74 |
1959Q3 | 1959.0 | 3.0 | 2775.488 | 1751.8 | 289.226 | 491.260 | 1916.4 | 29.35 | 140.5 | 3.82 | 5.3 | 178.657 | 2.74 | 1.09 |
1959Q4 | 1959.0 | 4.0 | 2785.204 | 1753.7 | 299.356 | 484.052 | 1931.3 | 29.37 | 140.0 | 4.33 | 5.6 | 179.386 | 0.27 | 4.06 |
1960Q1 | 1960.0 | 1.0 | 2847.699 | 1770.5 | 331.722 | 462.199 | 1955.5 | 29.54 | 139.6 | 3.50 | 5.2 | 180.007 | 2.31 | 1.19 |
data.infl[:5]
1959Q1 0.00
1959Q2 2.34
1959Q3 2.74
1959Q4 0.27
1960Q1 2.31
Freq: Q-DEC, Name: infl, dtype: float64