美文网首页呆鸟的Python数据分析呆鸟译Py
Pandas 时间序列 - 时间跨度表示

Pandas 时间序列 - 时间跨度表示

作者: 呆鸟的简书 | 来源:发表于2019-12-30 14:57 被阅读0次

    [TOC]

    规律时间间隔可以用 pandas 的 Peirod 对象表示,Period 对象序列叫做 PeriodIndex,用便捷函数 period_range 创建。

    Period

    Period 表示时间跨度,即时间段,如年、季、月、日等。关键字 freq 与频率别名可以指定时间段。freq 表示的是 Period 的时间跨度,不能为负,如,-3D

    In [326]: pd.Period('2012', freq='A-DEC')
    Out[326]: Period('2012', 'A-DEC')
    
    In [327]: pd.Period('2012-1-1', freq='D')
    Out[327]: Period('2012-01-01', 'D')
    
    In [328]: pd.Period('2012-1-1 19:00', freq='H')
    Out[328]: Period('2012-01-01 19:00', 'H')
    
    In [329]: pd.Period('2012-1-1 19:00', freq='5H')
    Out[329]: Period('2012-01-01 19:00', '5H')
    

    时间段加减法按自身频率位移。 不同频率的时间段不可进行算术运算。

    In [330]: p = pd.Period('2012', freq='A-DEC')
    
    In [331]: p + 1
    Out[331]: Period('2013', 'A-DEC')
    
    In [332]: p - 3
    Out[332]: Period('2009', 'A-DEC')
    
    In [333]: p = pd.Period('2012-01', freq='2M')
    
    In [334]: p + 2
    Out[334]: Period('2012-05', '2M')
    
    In [335]: p - 1
    Out[335]: Period('2011-11', '2M')
    
    In [336]: p == pd.Period('2012-01', freq='3M')
    ---------------------------------------------------------------------------
    IncompatibleFrequency                     Traceback (most recent call last)
    <ipython-input-336-4b67dc0b596c> in <module>
    ----> 1 p == pd.Period('2012-01', freq='3M')
    
    /pandas/pandas/_libs/tslibs/period.pyx in pandas._libs.tslibs.period._Period.__richcmp__()
    
    IncompatibleFrequency: Input has different freq=3M from Period(freq=2M)
    

    freq 的频率为日或更高频率时,如 DHTSLUNoffsetstimedelta 可以用相同频率实现加法。否则,会触发 ValueError

    In [337]: p = pd.Period('2014-07-01 09:00', freq='H')
    
    In [338]: p + pd.offsets.Hour(2)
    Out[338]: Period('2014-07-01 11:00', 'H')
    
    In [339]: p + datetime.timedelta(minutes=120)
    Out[339]: Period('2014-07-01 11:00', 'H')
    
    In [340]: p + np.timedelta64(7200, 's')
    Out[340]: Period('2014-07-01 11:00', 'H')
    In [1]: p + pd.offsets.Minute(5)
    Traceback
       ...
    ValueError: Input has different freq from Period(freq=H)
    

    如果 Period 为其它频率,只有相同频率的 offsets 可以相加。否则,会触发 ValueError

    In [341]: p = pd.Period('2014-07', freq='M')
    
    In [342]: p + pd.offsets.MonthEnd(3)
    Out[342]: Period('2014-10', 'M')
    In [1]: p + pd.offsets.MonthBegin(3)
    Traceback
       ...
    ValueError: Input has different freq from Period(freq=M)
    

    用相同频率计算不同时间段实例之间的区别,将返回这些实例之间的频率单元数量。

    In [343]: pd.Period('2012', freq='A-DEC') - pd.Period('2002', freq='A-DEC')
    Out[343]: <10 * YearEnds: month=12>
    

    PeriodIndex 与 period_range

    period_range 便捷函数可以创建有规律的 Period 对象序列,即 PeriodIndex

    In [344]: prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
    
    In [345]: prng
    Out[345]: 
    PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
                 '2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12',
                 '2012-01'],
                dtype='period[M]', freq='M')
    

    也可以直接用 PeriodIndex 创建:

    In [346]: pd.PeriodIndex(['2011-1', '2011-2', '2011-3'], freq='M')
    Out[346]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]', freq='M')
    

    频率为复数时,输出的 Period 序列为复数时间段。

    In [347]: pd.period_range(start='2014-01', freq='3M', periods=4)
    Out[347]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]', freq='3M')
    

    Period 对象的 startend 会被当作 PeriodIndex 的锚定终点,其频率与 PeriodIndex 的频率一样。

    In [348]: pd.period_range(start=pd.Period('2017Q1', freq='Q'),
       .....:                 end=pd.Period('2017Q2', freq='Q'), freq='M')
       .....: 
    Out[348]: PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]', freq='M')
    

    DatetimeIndex 一样,PeriodIndex 也可以作为 pandas 对象的索引。

    In [349]: ps = pd.Series(np.random.randn(len(prng)), prng)
    
    In [350]: ps
    Out[350]: 
    2011-01   -2.916901
    2011-02    0.514474
    2011-03    1.346470
    2011-04    0.816397
    2011-05    2.258648
    2011-06    0.494789
    2011-07    0.301239
    2011-08    0.464776
    2011-09   -1.393581
    2011-10    0.056780
    2011-11    0.197035
    2011-12    2.261385
    2012-01   -0.329583
    Freq: M, dtype: float64
    

    PeriodIndex 的加减法与 Period 一样。

    In [351]: idx = pd.period_range('2014-07-01 09:00', periods=5, freq='H')
    
    In [352]: idx
    Out[352]: 
    PeriodIndex(['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00',
                 '2014-07-01 12:00', '2014-07-01 13:00'],
                dtype='period[H]', freq='H')
    
    In [353]: idx + pd.offsets.Hour(2)
    Out[353]: 
    PeriodIndex(['2014-07-01 11:00', '2014-07-01 12:00', '2014-07-01 13:00',
                 '2014-07-01 14:00', '2014-07-01 15:00'],
                dtype='period[H]', freq='H')
    
    In [354]: idx = pd.period_range('2014-07', periods=5, freq='M')
    
    In [355]: idx
    Out[355]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]', freq='M')
    
    In [356]: idx + pd.offsets.MonthEnd(3)
    Out[356]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]', freq='M')
    

    PeriodIndex 有自己的数据类型,即 period,请参阅 Period 数据类型

    Period 数据类型

    0.19.0 版新增

    PeriodIndex 的自定义数据类型是 period,是 pandas 扩展数据类型,类似于带时区信息的数据类型datetime64[ns, tz])。

    Period 数据类型支持 freq 属性,还可以用 period[freq] 表示,如,period[D]period[M],这里用的是频率字符串

    In [357]: pi = pd.period_range('2016-01-01', periods=3, freq='M')
    
    In [358]: pi
    Out[358]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]', freq='M')
    
    In [359]: pi.dtype
    Out[359]: period[M]
    

    period 数据类型在 .astype(...) 里使用。允许改变 PeriodIndexfreq, 如 .asfreq(),并用 to_period()DatetimeIndex 转化为 PeriodIndex

    # 把月频改为日频
    In [360]: pi.astype('period[D]')
    Out[360]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]', freq='D')
    
    # 转换为 DatetimeIndex
    In [361]: pi.astype('datetime64[ns]')
    Out[361]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')
    
    # 转换为 PeriodIndex
    In [362]: dti = pd.date_range('2011-01-01', freq='M', periods=3)
    
    In [363]: dti
    Out[363]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='M')
    
    In [364]: dti.astype('period[M]')
    Out[364]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]', freq='M')
    

    PeriodIndex 局部字符串索引

    DatetimeIndex 一样,PeriodIndex 可以把日期与字符串传递给 SeriesDataFrame。详情请参阅 DatetimeIndex 局部字符串索引

    In [365]: ps['2011-01']
    Out[365]: -2.9169013294054507
    
    In [366]: ps[datetime.datetime(2011, 12, 25):]
    Out[366]: 
    2011-12    2.261385
    2012-01   -0.329583
    Freq: M, dtype: float64
    
    In [367]: ps['10/31/2011':'12/31/2011']
    Out[367]: 
    2011-10    0.056780
    2011-11    0.197035
    2011-12    2.261385
    Freq: M, dtype: float64
    

    传递比 PeriodIndex 更低频率的字符串会返回局部切片数据。

    In [368]: ps['2011']
    Out[368]: 
    2011-01   -2.916901
    2011-02    0.514474
    2011-03    1.346470
    2011-04    0.816397
    2011-05    2.258648
    2011-06    0.494789
    2011-07    0.301239
    2011-08    0.464776
    2011-09   -1.393581
    2011-10    0.056780
    2011-11    0.197035
    2011-12    2.261385
    Freq: M, dtype: float64
    
    In [369]: dfp = pd.DataFrame(np.random.randn(600, 1),
       .....:                    columns=['A'],
       .....:                    index=pd.period_range('2013-01-01 9:00',
       .....:                                          periods=600,
       .....:                                          freq='T'))
       .....: 
    
    In [370]: dfp
    Out[370]: 
                             A
    2013-01-01 09:00 -0.538468
    2013-01-01 09:01 -1.365819
    2013-01-01 09:02 -0.969051
    2013-01-01 09:03 -0.331152
    2013-01-01 09:04 -0.245334
    ...                    ...
    2013-01-01 18:55  0.522460
    2013-01-01 18:56  0.118710
    2013-01-01 18:57  0.167517
    2013-01-01 18:58  0.922883
    2013-01-01 18:59  1.721104
    
    [600 rows x 1 columns]
    
    In [371]: dfp['2013-01-01 10H']
    Out[371]: 
                             A
    2013-01-01 10:00 -0.308975
    2013-01-01 10:01  0.542520
    2013-01-01 10:02  1.061068
    2013-01-01 10:03  0.754005
    2013-01-01 10:04  0.352933
    ...                    ...
    2013-01-01 10:55 -0.865621
    2013-01-01 10:56 -1.167818
    2013-01-01 10:57 -2.081748
    2013-01-01 10:58 -0.527146
    2013-01-01 10:59  0.802298
    
    [60 rows x 1 columns]
    

    DatetimeIndex 一样,终点包含在结果范围之内。下例中的切片数据就是从 10:00 到 11:59。

    In [372]: dfp['2013-01-01 10H':'2013-01-01 11H']
    Out[372]: 
                             A
    2013-01-01 10:00 -0.308975
    2013-01-01 10:01  0.542520
    2013-01-01 10:02  1.061068
    2013-01-01 10:03  0.754005
    2013-01-01 10:04  0.352933
    ...                    ...
    2013-01-01 11:55 -0.590204
    2013-01-01 11:56  1.539990
    2013-01-01 11:57 -1.224826
    2013-01-01 11:58  0.578798
    2013-01-01 11:59 -0.685496
    
    [120 rows x 1 columns]
    

    频率转换与 PeriodIndex 重采样

    PeriodPeriodIndex 的频率可以用 asfreq 转换。下列代码开始于 2011 财年,结束时间为十二月:

    In [373]: p = pd.Period('2011', freq='A-DEC')
    
    In [374]: p
    Out[374]: Period('2011', 'A-DEC')
    

    可以把它转换为月频。使用 how 参数,指定是否返回开始或结束月份。

    In [375]: p.asfreq('M', how='start')
    Out[375]: Period('2011-01', 'M')
    
    In [376]: p.asfreq('M', how='end')
    Out[376]: Period('2011-12', 'M')
    

    简称 se 用起来更方便:

    In [377]: p.asfreq('M', 's')
    Out[377]: Period('2011-01', 'M')
    
    In [378]: p.asfreq('M', 'e')
    Out[378]: Period('2011-12', 'M')
    

    转换为“超级 period”,(如,年频就是季频的超级 period),自动返回包含输入时间段的超级 period:

    In [379]: p = pd.Period('2011-12', freq='M')
    
    In [380]: p.asfreq('A-NOV')
    Out[380]: Period('2012', 'A-NOV')
    

    注意,因为转换年频是在十一月结束的,2011 年 12 月的月时间段实际上是 2012 A-NOV period。

    用锚定频率转换时间段,对经济学、商业等领域里的各种季度数据特别有用。很多公司都依据其财年开始月与结束月定义季度。因此,2011 年第一个季度有可能 2010 年就开始了,也有可能 2011 年过了几个月才开始。通过锚定频率,pandas 可以处理所有从 Q-JANQ-DEC的季度频率。

    Q-DEC 定义的是常规日历季度:

    In [381]: p = pd.Period('2012Q1', freq='Q-DEC')
    
    In [382]: p.asfreq('D', 's')
    Out[382]: Period('2012-01-01', 'D')
    
    In [383]: p.asfreq('D', 'e')
    Out[383]: Period('2012-03-31', 'D')
    

    Q-MAR 定义的是财年结束于三月:

    In [384]: p = pd.Period('2011Q4', freq='Q-MAR')
    
    In [385]: p.asfreq('D', 's')
    Out[385]: Period('2011-01-01', 'D')
    
    In [386]: p.asfreq('D', 'e')
    Out[386]: Period('2011-03-31', 'D')
    

    不同表现形式之间的转换

    to_period 把时间戳转换为 PeriodIndexto_timestamp 则执行反向操作。

    In [387]: rng = pd.date_range('1/1/2012', periods=5, freq='M')
    
    In [388]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
    
    In [389]: ts
    Out[389]: 
    2012-01-31    1.931253
    2012-02-29   -0.184594
    2012-03-31    0.249656
    2012-04-30   -0.978151
    2012-05-31   -0.873389
    Freq: M, dtype: float64
    
    In [390]: ps = ts.to_period()
    
    In [391]: ps
    Out[391]: 
    2012-01    1.931253
    2012-02   -0.184594
    2012-03    0.249656
    2012-04   -0.978151
    2012-05   -0.873389
    Freq: M, dtype: float64
    
    In [392]: ps.to_timestamp()
    Out[392]: 
    2012-01-01    1.931253
    2012-02-01   -0.184594
    2012-03-01    0.249656
    2012-04-01   -0.978151
    2012-05-01   -0.873389
    Freq: MS, dtype: float64
    

    记住 se 返回 period 开始或结束的时间戳:

    In [393]: ps.to_timestamp('D', how='s')
    Out[393]: 
    2012-01-01    1.931253
    2012-02-01   -0.184594
    2012-03-01    0.249656
    2012-04-01   -0.978151
    2012-05-01   -0.873389
    Freq: MS, dtype: float64
    

    用便捷算数函数可以转换时间段与时间戳。下例中,把以 11 月年度结束的季频转换为以下一个季度月末上午 9 点:

    In [394]: prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')
    
    In [395]: ts = pd.Series(np.random.randn(len(prng)), prng)
    
    In [396]: ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
    
    In [397]: ts.head()
    Out[397]: 
    1990-03-01 09:00   -0.109291
    1990-06-01 09:00   -0.637235
    1990-09-01 09:00   -1.735925
    1990-12-01 09:00    2.096946
    1991-03-01 09:00   -1.039926
    Freq: H, dtype: float64
    

    界外跨度表示

    数据在 Timestamp 限定边界外时,参阅 Timestamp 限制,可以用 PeriodIndexPeriodsSeries 执行计算。

    In [398]: span = pd.period_range('1215-01-01', '1381-01-01', freq='D')
    
    In [399]: span
    Out[399]: 
    PeriodIndex(['1215-01-01', '1215-01-02', '1215-01-03', '1215-01-04',
                 '1215-01-05', '1215-01-06', '1215-01-07', '1215-01-08',
                 '1215-01-09', '1215-01-10',
                 ...
                 '1380-12-23', '1380-12-24', '1380-12-25', '1380-12-26',
                 '1380-12-27', '1380-12-28', '1380-12-29', '1380-12-30',
                 '1380-12-31', '1381-01-01'],
                dtype='period[D]', length=60632, freq='D')
    

    从基于 int64YYYYMMDD 表示形式转换。

    In [400]: s = pd.Series([20121231, 20141130, 99991231])
    
    In [401]: s
    Out[401]: 
    0    20121231
    1    20141130
    2    99991231
    dtype: int64
    
    In [402]: def conv(x):
       .....:     return pd.Period(year=x // 10000, month=x // 100 % 100,
       .....:                      day=x % 100, freq='D')
       .....: 
    
    In [403]: s.apply(conv)
    Out[403]: 
    0    2012-12-31
    1    2014-11-30
    2    9999-12-31
    dtype: period[D]
    
    In [404]: s.apply(conv)[2]
    Out[404]: Period('9999-12-31', 'D')
    

    轻轻松松就可以这些数据转换成 PeriodIndex

    In [405]: span = pd.PeriodIndex(s.apply(conv))
    
    In [406]: span
    Out[406]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]', freq='D')
    

    Pandas 时间序列 1 - 纵览与时间戳
    Pandas 时间序列 2 - 日期时间索引
    Pandas 时间序列 3 - DateOffset 对象
    Pandas 时间序列 4 - 实例方法与重采样
    Pandas 时间序列 5 - 时间跨度表示

    相关文章

      网友评论

        本文标题:Pandas 时间序列 - 时间跨度表示

        本文链接:https://www.haomeiwen.com/subject/wtxdgctx.html