11.5 时期及其算术运算

    这里,这个Period对象表示的是从2007年1月1日到2007年12月31日之间的整段时间。只需对Period对象加上或减去一个整数即可达到根据其频率进行位移的效果:

    1. Out[151]: Period('2012', 'A-DEC')
    2. In [152]: p - 2
    3. Out[152]: Period('2005', 'A-DEC')

    如果两个Period对象拥有相同的频率,则它们的差就是它们之间的单位数量:

    1. In [153]: pd.Period('2014', freq='A-DEC') - p
    2. Out[153]: 7

    period_range函数可用于创建规则的时期范围:

    1. In [154]: rng = pd.period_range('2000-01-01', '2000-06-30', freq='M')
    2. In [155]: rng
    3. Out[155]: PeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '20
    4. 00-06'], dtype='period[M]', freq='M')

    PeriodIndex类保存了一组Period,它可以在任何pandas数据结构中被用作轴索引:

    1. In [156]: pd.Series(np.random.randn(6), index=rng)
    2. Out[156]:
    3. 2000-01 -0.514551
    4. 2000-02 -0.559782
    5. 2000-03 -0.783408
    6. 2000-04 -1.797685
    7. 2000-05 -0.172670
    8. 2000-06 0.680215
    9. Freq: M, dtype: float64

    如果你有一个字符串数组,你也可以使用PeriodIndex类:

    1. In [157]: values = ['2001Q3', '2002Q2', '2003Q1']
    2. In [158]: index = pd.PeriodIndex(values, freq='Q-DEC')
    3. In [159]: index
    4. Out[159]: PeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='period[Q-DEC]', freq
    5. ='Q-DEC')

    Period和PeriodIndex对象都可以通过其asfreq方法被转换成别的频率。假设我们有一个年度时期,希望将其转换为当年年初或年末的一个月度时期。该任务非常简单:

    1. In [164]: p = pd.Period('2007', freq='A-JUN')
    2. In [165]: p
    3. Out[165]: Period('2007', 'A-JUN')
    4. In [166]: p.asfreq('M', 'start')
    5. Out[166]: Period('2006-07', 'M')
    6. In [167]: p.asfreq('M', 'end')
    7. Out[167]: Period('2007-06', 'M')

    在将高频率转换为低频率时,超时期(superperiod)是由子时期(subperiod)所属的位置决定的。例如,在A-JUN频率中,月份“2007年8月”实际上是属于周期“2008年”的:

    1. In [168]: p = pd.Period('Aug-2007', 'M')
    2. In [169]: p.asfreq('A-JUN')
    3. Out[169]: Period('2008', 'A-JUN')

    完整的PeriodIndex或TimeSeries的频率转换方式也是如此:

    1. In [170]: rng = pd.period_range('2006', '2009', freq='A-DEC')
    2. In [171]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
    3. In [172]: ts
    4. Out[172]:
    5. 2006 1.607578
    6. 2007 0.200381
    7. 2008 -0.834068
    8. 2009 -0.302988
    9. Freq: A-DEC, dtype: float64
    10. In [173]: ts.asfreq('M', how='start')
    11. Out[173]:
    12. 2006-01 1.607578
    13. 2007-01 0.200381
    14. 2008-01 -0.834068
    15. 2009-01 -0.302988
    16. Freq: M, dtype: float64

    这里,根据年度时期的第一个月,每年的时期被取代为每月的时期。如果我们想要每年的最后一个工作日,我们可以使用“B”频率,并指明想要该时期的末尾:

    1. In [174]: ts.asfreq('B', how='end')
    2. Out[174]:
    3. 2006-12-29 1.607578
    4. 2007-12-31 0.200381
    5. 2009-12-31 -0.302988
    6. Freq: B, dtype: float64

    季度型数据在会计、金融等领域中很常见。许多季度型数据都会涉及“财年末”的概念,通常是一年12个月中某月的最后一个日历日或工作日。就这一点来说,时期”2012Q4”根据财年末的不同会有不同的含义。pandas支持12种可能的季度型频率,即Q-JAN到Q-DEC:

    1. In [176]: p
    2. Out[176]: Period('2012Q4', 'Q-JAN')

    在以1月结束的财年中,2012Q4是从11月到1月(将其转换为日型频率就明白了)。图11-2对此进行了说明:

    因此,Period之间的算术运算会非常简单。例如,要获取该季度倒数第二个工作日下午4点的时间戳,你可以这样:

    1. In [179]: p4pm = (p.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
    2. In [180]: p4pm
    3. Out[180]: Period('2012-01-30 16:00', 'T')
    4. In [181]: p4pm.to_timestamp()
    5. Out[181]: Timestamp('2012-01-30 16:00:00')

    period_range可用于生成季度型范围。季度型范围的算术运算也跟上面是一样的:

    1. In [182]: rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN')
    2. In [183]: ts = pd.Series(np.arange(len(rng)), index=rng)
    3. In [184]: ts
    4. Out[184]:
    5. 2011Q3 0
    6. 2011Q4 1
    7. 2012Q1 2
    8. 2012Q2 3
    9. 2012Q3 4
    10. 2012Q4 5
    11. Freq: Q-JAN, dtype: int64
    12. In [185]: new_rng = (rng.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
    13. In [186]: ts.index = new_rng.to_timestamp()
    14. In [187]: ts
    15. Out[187]:
    16. 2010-10-28 16:00:00 0
    17. 2011-01-28 16:00:00 1
    18. 2011-04-28 16:00:00 2
    19. 2011-07-28 16:00:00 3
    20. 2011-10-28 16:00:00 4
    21. 2012-01-30 16:00:00 5
    22. dtype: int64

    通过使用to_period方法,可以将由时间戳索引的Series和DataFrame对象转换为以时期索引:

    1. In [188]: rng = pd.date_range('2000-01-01', periods=3, freq='M')
    2. In [189]: ts = pd.Series(np.random.randn(3), index=rng)
    3. In [190]: ts
    4. Out[190]:
    5. 2000-01-31 1.663261
    6. 2000-02-29 -0.996206
    7. 2000-03-31 1.521760
    8. Freq: M, dtype: float64
    9. In [191]: pts = ts.to_period()
    10. In [192]: pts
    11. Out[192]:
    12. 2000-01 1.663261
    13. 2000-02 -0.996206
    14. 2000-03 1.521760
    15. Freq: M, dtype: float64

    由于时期指的是非重叠时间区间,因此对于给定的频率,一个时间戳只能属于一个时期。新PeriodIndex的频率默认是从时间戳推断而来的,你也可以指定任何别的频率。结果中允许存在重复时期:

    1. In [193]: rng = pd.date_range('1/29/2000', periods=6, freq='D')
    2. In [194]: ts2 = pd.Series(np.random.randn(6), index=rng)
    3. In [195]: ts2
    4. Out[195]:
    5. 2000-01-29 0.244175
    6. 2000-01-30 0.423331
    7. 2000-01-31 -0.654040
    8. 2000-02-01 2.089154
    9. 2000-02-02 -0.060220
    10. In [196]: ts2.to_period('M')
    11. Out[196]:
    12. 2000-01 0.244175
    13. 2000-01 0.423331
    14. 2000-01 -0.654040
    15. 2000-02 2.089154
    16. 2000-02 -0.060220
    17. 2000-02 -0.167933
    18. Freq: M, dtype: float64

    要转换回时间戳,使用to_timestamp即可:

    1. In [197]: pts = ts2.to_period()
    2. In [198]: pts
    3. Out[198]:
    4. 2000-01-29 0.244175
    5. 2000-01-30 0.423331
    6. 2000-01-31 -0.654040
    7. 2000-02-01 2.089154
    8. 2000-02-02 -0.060220
    9. 2000-02-03 -0.167933
    10. Freq: D, dtype: float64
    11. In [199]: pts.to_timestamp(how='end')
    12. Out[199]:
    13. 2000-01-29 0.244175
    14. 2000-01-30 0.423331
    15. 2000-01-31 -0.654040
    16. 2000-02-01 2.089154
    17. 2000-02-02 -0.060220
    18. 2000-02-03 -0.167933
    19. Freq: D, dtype: float64

    固定频率的数据集通常会将时间信息分开存放在多个列中。例如,在下面这个宏观经济数据集中,年度和季度就分别存放在不同的列中:

    1. In [204]: index = pd.PeriodIndex(year=data.year, quarter=data.quarter,
    2. .....: freq='Q-DEC')
    3. In [205]: index
    4. Out[205]:
    5. PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
    6. '1960Q3', '1960Q4', '1961Q1', '1961Q2',
    7. ...
    8. '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
    9. '2008Q4', '2009Q1', '2009Q2', '2009Q3'],
    10. dtype='period[Q-DEC]', length=203, freq='Q-DEC')
    11. In [206]: data.index = index
    12. In [207]: data.infl
    13. Out[207]:
    14. 1959Q1 0.00
    15. 1959Q2 2.34
    16. 1959Q3 2.74
    17. 1959Q4 0.27
    18. 1960Q1 2.31
    19. 1960Q2 0.14
    20. 1960Q3 2.70
    21. 1960Q4 1.21
    22. 1961Q1 -0.40
    23. 1961Q2 1.47
    24. ...
    25. 2007Q2 2.75
    26. 2007Q3 3.45
    27. 2007Q4 6.38
    28. 2008Q1 2.82
    29. 2008Q2 8.53
    30. 2008Q3 -3.16
    31. 2008Q4 -8.79
    32. 2009Q1 0.94
    33. 2009Q2 3.37
    34. 2009Q3 3.56