呆鸟云:“书接上文《Pandas 时间序列 - 日期时间索引》,本篇讲的主要是日期偏移,就是 标题里的 DateOffset。这里呆鸟强调一下,文章光看不行,一定要敲代码,呆鸟就是左右分屏,一边放文章,一边放 Jupyter Notebook,一段一段代码敲出来,看每一段运行的结果,这样能更好理解代码的含义,才能学的更扎实。还有,官档精译系列是全面基础知识,百问百答系列是实战技巧,两个系列要配合着学才好。”
今天换个浅色的代码主题。
呆鸟又云:“感谢大家支持,上一篇《三分钟告诉你 1575119387982 是什么?》” 的在看突破了 50+。呆鸟辛苦翻译,写文,公号也没留言功能,看到在看,才知道有多少读者对呆鸟文章的认可,在看多, 就是读者对呆鸟的鼓励,就更有动力继续下去,所以继续求在看,希望这篇也能上50+。”
[TOC]
上例中,频率字符串(如,D
)用于定义指定的频率:
用
date_range()
按指定频率分隔DatetimeIndex
里的日期与时间Period
或PeriodIndex
的频率
频率字符串表示的是 DateOffset
对象及其子类。DateOffset
类似于时间差 Timedelta
,但遵循指定的日历日规则。例如,Timedelta
表示的每日时间差一直都是 24 小时,而 DateOffset
的每日偏移量则是与下一天相同的时间差,使用夏时制时,每日偏移时间有可能是 23 或 24 小时,甚至还有可能是 25 小时。不过,DateOffset
子类只能是等于或小于小时的时间单位(Hour
、Minute
、Second
、Milli
、Micro
、Nano
),操作类似于 Timedelta
及对应的绝对时间。
DateOffset
基础操作类似于 dateutil.relativedelta
(relativedelta 文档),可按指定的日历日时间段偏移日期时间。可用算数运算符(+)或 apply
方法执行日期偏移操作。
# 指定包含夏时制变迁的某天
In [141]: ts = pd.Timestamp('2016-10-30 00:00:00', tz='Europe/Helsinki')
# 对应的绝对时间
In [142]: ts + pd.Timedelta(days=1)
Out[142]: Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')
# 对应的日历时间
In [143]: ts + pd.DateOffset(days=1)
Out[143]: Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
In [144]: friday = pd.Timestamp('2018-01-05')
In [145]: friday.day_name()
Out[145]: 'Friday'
# 与两个工作日相加(星期五 --> 星期二)
In [146]: two_business_days = 2 * pd.offsets.BDay()
In [147]: two_business_days.apply(friday)
Out[147]: Timestamp('2018-01-09 00:00:00')
In [148]: friday + two_business_days
Out[148]: Timestamp('2018-01-09 00:00:00')
In [149]: (friday + two_business_days).day_name()
Out[149]: 'Tuesday'
大多数 DateOffset
都支持频率字符串或偏移别名,可用作 freq
关键字参数。有效的日期偏移及频率字符串如下:
日期偏移量 | 频率字符串 | 说明 |
---|---|---|
DateOffset |
无 | 通用偏移类,默认为一个日历日 |
BDay 或 BusinessDay
|
'B' |
工作日 |
CDay 或 CustomBusinessDay
|
'C' |
自定义工作日 |
Week |
'W' |
一周,可选周内固定某日 |
WeekOfMonth |
'WOM' |
每月第几周的第几天 |
LastWeekOfMonth |
'LWOM' |
每月最后一周的第几天 |
MonthEnd |
'M' |
日历日月末 |
MonthBegin |
'MS' |
日历日月初 |
BMonthEnd 或 BusinessMonthEnd
|
'BM' |
工作日月末 |
BMonthBegin 或 BusinessMonthBegin
|
'BMS' |
工作日月初 |
CBMonthEnd 或 CustomBusinessMonthEnd
|
'CBM' |
自定义工作日月末 |
CBMonthBegin 或 CustomBusinessMonthBegin
|
'CBMS' |
自定义工作日月初 |
SemiMonthEnd |
'SM' |
某月第 15 天(或其它半数日期)与日历日月末 |
SemiMonthBegin |
'SMS' |
日历日月初与第 15 天(或其它半数日期) |
QuarterEnd |
'Q' |
日历日季末 |
QuarterBegin |
'QS' |
日历日季初 |
BQuarterEnd |
'BQ |
工作日季末 |
BQuarterBegin |
'BQS' |
工作日季初 |
FY5253Quarter |
'REQ' |
零售季,又名 52-53 周 |
YearEnd |
'A' |
日历日年末 |
YearBegin |
'AS' 或 'BYS'
|
日历日年初 |
BYearEnd |
'BA' |
工作日年末 |
BYearBegin |
'BAS' |
工作日年初 |
FY5253 |
'RE' |
零售年(又名 52-53 周) |
Easter |
无 | 复活节假日 |
BusinessHour |
'BH' |
工作小时 |
CustomBusinessHour |
'CBH' |
自定义工作小时 |
Day |
'D' |
一天 |
Hour |
'H' |
一小时 |
Minute |
'T' 或 'min'
|
一分钟 |
Second |
'S' |
一秒 |
Milli |
'L' 或 'ms'
|
一毫秒 |
Micro |
'U' 或 'us'
|
一微秒 |
Nano |
'N' |
一纳秒 |
DateOffset
还支持 rollforward()
与 rollback()
方法,按偏移量把某一日期向前或向后移动至有效偏移日期。例如,工作日偏移滚动日期时会跳过周末(即,星期六与星期日),直接到星期一,因为工作日偏移针对的是工作日。
In [150]: ts = pd.Timestamp('2018-01-06 00:00:00')
In [151]: ts.day_name()
Out[151]: 'Saturday'
# 工作时间的有效偏移日期为星期一至星期五
In [152]: offset = pd.offsets.BusinessHour(start='09:00')
# 向前偏移到最近的工作日,即星期一
In [153]: offset.rollforward(ts)
Out[153]: Timestamp('2018-01-08 09:00:00')
# 向前偏移至最近的工作日,同时,小时也相应增加了
In [154]: ts + offset
Out[154]: Timestamp('2018-01-08 10:00:00')
这些操作默认保存时间(小时、分钟等)信息。normalize()
可以把时间重置为午夜零点,是否应用此操作,取决于是否需要保留时间信息。
In [155]: ts = pd.Timestamp('2014-01-01 09:00')
In [156]: day = pd.offsets.Day()
In [157]: day.apply(ts)
Out[157]: Timestamp('2014-01-02 09:00:00')
In [158]: day.apply(ts).normalize()
Out[158]: Timestamp('2014-01-02 00:00:00')
In [159]: ts = pd.Timestamp('2014-01-01 22:00')
In [160]: hour = pd.offsets.Hour()
In [161]: hour.apply(ts)
Out[161]: Timestamp('2014-01-01 23:00:00')
In [162]: hour.apply(ts).normalize()
Out[162]: Timestamp('2014-01-01 00:00:00')
In [163]: hour.apply(pd.Timestamp("2014-01-01 23:30")).normalize()
Out[163]: Timestamp('2014-01-02 00:00:00')
参数偏移
偏移量支持参数,可以让不同操作生成不同结果。例如,Week
偏移生成每周数据时支持 weekday
参数,生成日期始终位于一周中的指定日期。
In [164]: d = datetime.datetime(2008, 8, 18, 9, 0)
In [165]: d
Out[165]: datetime.datetime(2008, 8, 18, 9, 0)
In [166]: d + pd.offsets.Week()
Out[166]: Timestamp('2008-08-25 09:00:00')
In [167]: d + pd.offsets.Week(weekday=4)
Out[167]: Timestamp('2008-08-22 09:00:00')
In [168]: (d + pd.offsets.Week(weekday=4)).weekday()
Out[168]: 4
In [169]: d - pd.offsets.Week()
Out[169]: Timestamp('2008-08-11 09:00:00')
加减法也支持 normalize
选项。
In [170]: d + pd.offsets.Week(normalize=True)
Out[170]: Timestamp('2008-08-25 00:00:00')
In [171]: d - pd.offsets.Week(normalize=True)
Out[171]: Timestamp('2008-08-11 00:00:00')
YearEnd
也支持参数,如 month
参数,用于指定月份 。
In [172]: d + pd.offsets.YearEnd()
Out[172]: Timestamp('2008-12-31 09:00:00')
In [173]: d + pd.offsets.YearEnd(month=6)
Out[173]: Timestamp('2009-06-30 09:00:00')
Series
与 DatetimeIndex
偏移
可以为 Series
或 DatetimeIndex
里的每个元素应用偏移。
In [174]: rng = pd.date_range('2012-01-01', '2012-01-03')
In [175]: s = pd.Series(rng)
In [176]: rng
Out[176]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
In [177]: rng + pd.DateOffset(months=2)
Out[177]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq='D')
In [178]: s + pd.DateOffset(months=2)
Out[178]:
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
In [179]: s - pd.DateOffset(months=2)
Out[179]:
0 2011-11-01
1 2011-11-02
2 2011-11-03
dtype: datetime64[ns]
如果偏移直接映射 Timedelta
(Day
、Hour
、Minute
、Second
、Micro
、Milli
、Nano
),则该偏移与 Timedelta
的使用方式完全一样。参阅时间差 - Timedelta,查看更多示例。
In [180]: s - pd.offsets.Day(2)
Out[180]:
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
In [181]: td = s - pd.Series(pd.date_range('2011-12-29', '2011-12-31'))
In [182]: td
Out[182]:
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
In [183]: td + pd.offsets.Minute(15)
Out[183]:
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
注意,某些偏移量(如 BQuarterEnd
)不支持矢量操作,即使可以执行运算,速度也非常慢,并可能显示 PerformanceWaring
(性能警告)。
In [184]: rng + pd.offsets.BQuarterEnd()
Out[184]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq='D')
自定义工作日
Cday
或 CustomBusinessDay
类可以参数化 BusinessDay
类,用于创建支持本地周末与传统节假日的自定义工作日历。
下面这个例子就很有意思,知道吗?埃及的周末是星期五与星期六。
In [185]: weekmask_egypt = 'Sun Mon Tue Wed Thu'
# 下面是 2012 - 2014 年的五一劳动节
In [186]: holidays = ['2012-05-01',
.....: datetime.datetime(2013, 5, 1),
.....: np.datetime64('2014-05-01')]
.....:
In [187]: bday_egypt = pd.offsets.CustomBusinessDay(holidays=holidays,
.....: weekmask=weekmask_egypt)
.....:
In [188]: dt = datetime.datetime(2013, 4, 30)
In [189]: dt + 2 * bday_egypt
Out[189]: Timestamp('2013-05-05 00:00:00')
下列代码实现了日期与工作日之间的映射关系。
In [190]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)
In [191]: pd.Series(dts.weekday, dts).map(
.....: pd.Series('Mon Tue Wed Thu Fri Sat Sun'.split()))
.....:
Out[191]:
2013-04-30 Tue
2013-05-02 Thu
2013-05-05 Sun
2013-05-06 Mon
2013-05-07 Tue
Freq: C, dtype: object
节日日历支持节假日列表。更多信息,请参阅节日日历文档。
In [192]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [193]: bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
# 马丁路德金纪念日前的星期五
In [194]: dt = datetime.datetime(2014, 1, 17)
# 马丁路德金纪念日后的星期二,因为星期一放假,所以跳过了
In [195]: dt + bday_us
Out[195]: Timestamp('2014-01-21 00:00:00')
遵循节日日历规则的月偏移可以用正常方式定义。
In [196]: bmth_us = pd.offsets.CustomBusinessMonthBegin(
.....: calendar=USFederalHolidayCalendar())
.....:
# 跳过新年
In [197]: dt = datetime.datetime(2013, 12, 17)
In [198]: dt + bmth_us
Out[198]: Timestamp('2014-01-02 00:00:00')
# 定义带自定义偏移的日期索引
In [199]: pd.date_range(start='20100101', end='20120101', freq=bmth_us)
Out[199]:
DatetimeIndex(['2010-01-04', '2010-02-01', '2010-03-01', '2010-04-01',
'2010-05-03', '2010-06-01', '2010-07-01', '2010-08-02',
'2010-09-01', '2010-10-01', '2010-11-01', '2010-12-01',
'2011-01-03', '2011-02-01', '2011-03-01', '2011-04-01',
'2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
'2011-09-01', '2011-10-03', '2011-11-01', '2011-12-01'],
dtype='datetime64[ns]', freq='CBMS')
注意:频率字符串 'C' 验证
CustomBusinessDay
日期偏移 调用,注意,CustomBusinessDay
可实现参数化,CustomBusinessDay
实例会各不相同,且频率字符串 'C' 无法识别这个问题。用户应确保应用里调用的频率字符串 'C' 的一致性 。
工作时间
BusinessHour
表示 BusinessDay
基础上的工作时间,用于指定开始与结束工作时间。
BusinessHour
默认的工作时间是 9:00 - 17:00。BusinessHour
加法以小时频率增加 Timestamp
。如果目标 Timestamp
超出了一小时,则要先移动到下一个工作小时,再行增加。如果超过了当日工作时间的范围,剩下的时间则添加到下一个工作日。
In [200]: bh = pd.offsets.BusinessHour()
In [201]: bh
Out[201]: <BusinessHour: BH=09:00-17:00>
# 2014 年 8 月 1 日是星期五
In [202]: pd.Timestamp('2014-08-01 10:00').weekday()
Out[202]: 4
In [203]: pd.Timestamp('2014-08-01 10:00') + bh
Out[203]: Timestamp('2014-08-01 11:00:00')
# 下例等同于: pd.Timestamp('2014-08-01 09:00') + bh
In [204]: pd.Timestamp('2014-08-01 08:00') + bh
Out[204]: Timestamp('2014-08-01 10:00:00')
# 如果计算结果为当日下班时间,则转移到下一个工作日的上班时间
In [205]: pd.Timestamp('2014-08-01 16:00') + bh
Out[205]: Timestamp('2014-08-04 09:00:00')
# 剩下的时间也会添加到下一天
In [206]: pd.Timestamp('2014-08-01 16:30') + bh
Out[206]: Timestamp('2014-08-04 09:30:00')
# 添加 2 个工作小时
In [207]: pd.Timestamp('2014-08-01 10:00') + pd.offsets.BusinessHour(2)
Out[207]: Timestamp('2014-08-01 12:00:00')
# 减掉 3 个工作小时
In [208]: pd.Timestamp('2014-08-01 10:00') + pd.offsets.BusinessHour(-3)
Out[208]: Timestamp('2014-07-31 15:00:00')
还可以用关键字指定 start
与 end
时间。参数必须是hour:minute
格式的字符串或 datetime.time
实例。把秒、微秒、纳秒设置为工作时间会导致 ValueError
。
In [209]: bh = pd.offsets.BusinessHour(start='11:00', end=datetime.time(20, 0))
In [210]: bh
Out[210]: <BusinessHour: BH=11:00-20:00>
In [211]: pd.Timestamp('2014-08-01 13:00') + bh
Out[211]: Timestamp('2014-08-01 14:00:00')
In [212]: pd.Timestamp('2014-08-01 09:00') + bh
Out[212]: Timestamp('2014-08-01 12:00:00')
In [213]: pd.Timestamp('2014-08-01 18:00') + bh
Out[213]: Timestamp('2014-08-01 19:00:00')
start
时间晚于 end
时间表示夜班工作时间。此时,工作时间将从午夜延至第二天。工作时间是否有效取决于该时间是否开始于有效的 BusinessDay
。
In [214]: bh = pd.offsets.BusinessHour(start='17:00', end='09:00')
In [215]: bh
Out[215]: <BusinessHour: BH=17:00-09:00>
In [216]: pd.Timestamp('2014-08-01 17:00') + bh
Out[216]: Timestamp('2014-08-01 18:00:00')
In [217]: pd.Timestamp('2014-08-01 23:00') + bh
Out[217]: Timestamp('2014-08-02 00:00:00')
# 虽然 2014 年 8 月 2 日是星期六,
# 但因为工作时间开始于星期五,因此,也是有效的
In [218]: pd.Timestamp('2014-08-02 04:00') + bh
Out[218]: Timestamp('2014-08-02 05:00:00')
# 虽然 2014 年 8 月 4 日是星期一,
# 但开始时间是星期日,因此,超出了工作时间
In [219]: pd.Timestamp('2014-08-04 04:00') + bh
Out[219]: Timestamp('2014-08-04 18:00:00')
BusinessHour.rollforward
与 rollback
操作将前滚至下一天的上班时间,或回滚至前一天的下班时间。与其它偏移量不同,BusinessHour.rollforward
输出与 apply
定义不同的结果。
这是因为一天工作时间的结束等同于第二天工作时间的开始。默认情况下,工作时间为 9:00 - 17:00,Pandas 认为 2014-08-01 17:00
与 2014-08-04 09:00
之间的时间间隔为 0 分钟。
# 把时间戳回滚到前一天的下班时间
In [220]: pd.offsets.BusinessHour().rollback(pd.Timestamp('2014-08-02 15:00'))
Out[220]: Timestamp('2014-08-01 17:00:00')
# 把时间戳前滚到下一个工作日的上班时间
In [221]: pd.offsets.BusinessHour().rollforward(pd.Timestamp('2014-08-02 15:00'))
Out[221]: Timestamp('2014-08-04 09:00:00')
# 等同于:BusinessHour().apply(pd.Timestamp('2014-08-01 17:00'))
# 与 BusinessHour().apply(pd.Timestamp('2014-08-04 09:00'))
In [222]: pd.offsets.BusinessHour().apply(pd.Timestamp('2014-08-02 15:00'))
Out[222]: Timestamp('2014-08-04 10:00:00')
# 工作日的结果(仅供参考)
In [223]: pd.offsets.BusinessHour().rollforward(pd.Timestamp('2014-08-02'))
Out[223]: Timestamp('2014-08-04 09:00:00')
# 等同于 BusinessDay().apply(pd.Timestamp('2014-08-01'))
# 等同于 rollforward 因为工作日不会重叠
In [224]: pd.offsets.BusinessHour().apply(pd.Timestamp('2014-08-02'))
Out[224]: Timestamp('2014-08-04 10:00:00')
BusinessHour
把星期六与星期日当成假日。CustomBusinessHour
可以把节假日设为工作时间,详见下文。
自定义工作时间
0.18.1 版新增。
CustomBusinessHour
是 BusinessHour
和 CustomBusinessDay
的混合体,可以指定任意节假日。除了跳过自定义节假日之外,CustomBusinessHour
的运作方式与 BusinessHour
一样。
In [225]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [226]: bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar())
# 马丁路德金纪念日之前的星期五
In [227]: dt = datetime.datetime(2014, 1, 17, 15)
In [228]: dt + bhour_us
Out[228]: Timestamp('2014-01-17 16:00:00')
# 跳至马丁路德金纪念日之后的星期二,星期一过节,所以跳过了
In [229]: dt + bhour_us * 2
Out[229]: Timestamp('2014-01-21 09:00:00')
BusinessHour
支持与 CustomBusinessDay
一样的关键字参数。
In [230]: bhour_mon = pd.offsets.CustomBusinessHour(start='10:00',
.....: weekmask='Tue Wed Thu Fri')
.....:
# 跳过了星期一,因为星期一过节,工作时间从 10 点开始
In [231]: dt + bhour_mon * 2
Out[231]: Timestamp('2014-01-21 10:00:00')
偏移量别名
时间序列频率的字符串别名在这里叫偏移量别名。
别名 | 说明 |
---|---|
B | 工作日频率 |
C | 自定义工作日频率 |
D | 日历日频率 |
W | 周频率 |
M | 月末频率 |
SM | 半月末频率(15 号与月末) |
BM | 工作日月末频率 |
CBM | 自定义工作日月末频率 |
MS | 月初频率 |
SMS | 半月初频率(1 号与 15 号) |
BMS | 工作日月初频率 |
CBMS | 自定义工作日月初频率 |
Q | 季末频率 |
BQ | 工作日季末频率 |
QS | 季初频率 |
BQS | 工作日季初频率 |
A, Y | 年末频率 |
BA, BY | 工作日年末频率 |
AS, YS | 年初频率 |
BAS, BYS | 工作日年初频率 |
BH | 工作时间频率 |
H | 小时频率 |
T, min | 分钟频率 |
S | 秒频率 |
L, ms | 毫秒 |
U, us | 微秒 |
N | 纳秒 |
别名组合
如前说述,别名与偏移量实例在绝大多数函数里可以互换:
In [232]: pd.date_range(start, periods=5, freq='B')
Out[232]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
In [233]: pd.date_range(start, periods=5, freq=pd.offsets.BDay())
Out[233]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
可以组合日与当日偏移量。
In [234]: pd.date_range(start, periods=10, freq='2h20min')
Out[234]:
DatetimeIndex(['2011-01-01 00:00:00', '2011-01-01 02:20:00',
'2011-01-01 04:40:00', '2011-01-01 07:00:00',
'2011-01-01 09:20:00', '2011-01-01 11:40:00',
'2011-01-01 14:00:00', '2011-01-01 16:20:00',
'2011-01-01 18:40:00', '2011-01-01 21:00:00'],
dtype='datetime64[ns]', freq='140T')
In [235]: pd.date_range(start, periods=10, freq='1D10U')
Out[235]:
DatetimeIndex([ '2011-01-01 00:00:00', '2011-01-02 00:00:00.000010',
'2011-01-03 00:00:00.000020', '2011-01-04 00:00:00.000030',
'2011-01-05 00:00:00.000040', '2011-01-06 00:00:00.000050',
'2011-01-07 00:00:00.000060', '2011-01-08 00:00:00.000070',
'2011-01-09 00:00:00.000080', '2011-01-10 00:00:00.000090'],
dtype='datetime64[ns]', freq='86400000010U')
锚定偏移量
可以指定某些频率的锚定后缀:
别名 | 说明 |
---|---|
W-SUN | 周频率(星期日),与 “W” 相同 |
W-MON | 周频率(星期一) |
W-TUE | 周频率(星期二) |
W-WED | 周频率(星期三) |
W-THU | 周频率(星期四) |
W-FRI | 周频率(星期五) |
W-SAT | 周频率(星期六) |
(B)Q(S)-DEC | 季频率,该年结束于十二月,与 “Q” 相同 |
(B)Q(S)-JAN | 季频率,该年结束于一月 |
(B)Q(S)-FEB | 季频率,该年结束于二月 |
(B)Q(S)-MAR | 季频率,该年结束于三月 |
(B)Q(S)-APR | 季频率,该年结束于四月 |
(B)Q(S)-MAY | 季频率,该年结束于五月 |
(B)Q(S)-JUN | 季频率,该年结束于六月 |
(B)Q(S)-JUL | 季频率,该年结束于七月 |
(B)Q(S)-AUG | 季频率,该年结束于八月 |
(B)Q(S)-SEP | 季频率,该年结束于九月 |
(B)Q(S)-OCT | 季频率,该年结束于十月 |
(B)Q(S)-NOV | 季频率,该年结束于十一月 |
(B)A(S)-DEC | 年频率,锚定结束于十二月,与 “A” 相同 |
(B)A(S)-JAN | 年频率,锚定结束于一月 |
(B)A(S)-FEB | 年频率,锚定结束于二月 |
(B)A(S)-MAR | 年频率,锚定结束于三月 |
(B)A(S)-APR | 年频率,锚定结束于四月 |
(B)A(S)-MAY | 年频率,锚定结束于五月 |
(B)A(S)-JUN | 年频率,锚定结束于六月 |
(B)A(S)-JUL | 年频率,锚定结束于七月 |
(B)A(S)-AUG | 年频率,锚定结束于八月 |
(B)A(S)-SEP | 年频率,锚定结束于九月 |
(B)A(S)-OCT | 年频率,锚定结束于十月 |
(B)A(S)-NOV | 年频率,锚定结束于十一月 |
这些别名可以用作 date_range
、bdate_range
、DatetimeIndex
及其它时间序列函数的参数。
锚定偏移量的含义
对于偏移量锚定于开始或结束指定频率(MonthEnd
、MonthBegin
、WeekEnd
等)下列规则应用于前滚与后滚。
n
不为 0 时,如果给定日期不是锚定日期,将寻找下一个或上一个锚点,并向前或向后移动 |n|-1
步。
In [236]: pd.Timestamp('2014-01-02') + pd.offsets.MonthBegin(n=1)
Out[236]: Timestamp('2014-02-01 00:00:00')
In [237]: pd.Timestamp('2014-01-02') + pd.offsets.MonthEnd(n=1)
Out[237]: Timestamp('2014-01-31 00:00:00')
In [238]: pd.Timestamp('2014-01-02') - pd.offsets.MonthBegin(n=1)
Out[238]: Timestamp('2014-01-01 00:00:00')
In [239]: pd.Timestamp('2014-01-02') - pd.offsets.MonthEnd(n=1)
Out[239]: Timestamp('2013-12-31 00:00:00')
In [240]: pd.Timestamp('2014-01-02') + pd.offsets.MonthBegin(n=4)
Out[240]: Timestamp('2014-05-01 00:00:00')
In [241]: pd.Timestamp('2014-01-02') - pd.offsets.MonthBegin(n=4)
Out[241]: Timestamp('2013-10-01 00:00:00')
如果给定日期是锚定日期,则向前(或向后)移动 |n|
个点。
In [242]: pd.Timestamp('2014-01-01') + pd.offsets.MonthBegin(n=1)
Out[242]: Timestamp('2014-02-01 00:00:00')
In [243]: pd.Timestamp('2014-01-31') + pd.offsets.MonthEnd(n=1)
Out[243]: Timestamp('2014-02-28 00:00:00')
In [244]: pd.Timestamp('2014-01-01') - pd.offsets.MonthBegin(n=1)
Out[244]: Timestamp('2013-12-01 00:00:00')
In [245]: pd.Timestamp('2014-01-31') - pd.offsets.MonthEnd(n=1)
Out[245]: Timestamp('2013-12-31 00:00:00')
In [246]: pd.Timestamp('2014-01-01') + pd.offsets.MonthBegin(n=4)
Out[246]: Timestamp('2014-05-01 00:00:00')
In [247]: pd.Timestamp('2014-01-31') - pd.offsets.MonthBegin(n=4)
Out[247]: Timestamp('2013-10-01 00:00:00')
n=0
时,如果日期在锚点,则不移动,否则将前滚至下一个锚点。
In [248]: pd.Timestamp('2014-01-02') + pd.offsets.MonthBegin(n=0)
Out[248]: Timestamp('2014-02-01 00:00:00')
In [249]: pd.Timestamp('2014-01-02') + pd.offsets.MonthEnd(n=0)
Out[249]: Timestamp('2014-01-31 00:00:00')
In [250]: pd.Timestamp('2014-01-01') + pd.offsets.MonthBegin(n=0)
Out[250]: Timestamp('2014-01-01 00:00:00')
In [251]: pd.Timestamp('2014-01-31') + pd.offsets.MonthEnd(n=0)
Out[251]: Timestamp('2014-01-31 00:00:00')
假日与节日日历
用假日与日历可以轻松定义 CustomBusinessDay
假日规则,或其它分析所需的预设假日。AbstractHolidayCalendar
类支持所有返回假日列表的方法,并且仅需在指定假日日历类里定义 rules
。start_date
与 end_date
类属性决定了假日的范围。该操作会覆盖 AbstractHolidayCalendar
类,适用于所有日历子类。USFederalHolidayCalendar
是仅有的假日日历,主要用作开发其它日历的示例。
固定日期的假日,如美国阵亡将士纪念日或美国国庆日(7 月 4 日),取决于该假日是否是在周末,可以使用以下规则:
规则 | 说明 |
---|---|
nearest_workday | 把星期六移至星期五,星期日移至星期一 |
sunday_to_monday | 星期六紧接着星期一 |
next_monday_or_tuesday | 把星期六移至星期一,并把星期日/星期一移至星期二 |
previous_friday | 把星期六与星期日移至上一个星期五 |
next_monday | 把星期六与星期日移至下一个星期一 |
下例展示如何定义假日与假日日历:
In [252]: from pandas.tseries.holiday import Holiday, USMemorialDay,\
.....: AbstractHolidayCalendar, nearest_workday, MO
.....:
In [253]: class ExampleCalendar(AbstractHolidayCalendar):
.....: rules = [
.....: USMemorialDay,
.....: Holiday('July 4th', month=7, day=4, observance=nearest_workday),
.....: Holiday('Columbus Day', month=10, day=1,
.....: offset=pd.DateOffset(weekday=MO(2)))]
.....:
In [254]: cal = ExampleCalendar()
In [255]: cal.holidays(datetime.datetime(2012, 1, 1), datetime.datetime(2012, 12, 31))
Out[255]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
注意:
weekday=MO(2)
与2 * Week(weekday=2)
相同。
用这个日历创建索引,或计算偏移量,将跳过周末与假日(如,纪念日与国庆节)。下列代码用 ExampleCalendar
设定自定义工作日偏移量。至于其它偏移量,可以用于创建 DatetimeIndex
或添加到 datetime
与 Timestamp
对象。
In [256]: pd.date_range(start='7/1/2012', end='7/10/2012',
.....: freq=pd.offsets.CDay(calendar=cal)).to_pydatetime()
.....:
Out[256]:
array([datetime.datetime(2012, 7, 2, 0, 0),
datetime.datetime(2012, 7, 3, 0, 0),
datetime.datetime(2012, 7, 5, 0, 0),
datetime.datetime(2012, 7, 6, 0, 0),
datetime.datetime(2012, 7, 9, 0, 0),
datetime.datetime(2012, 7, 10, 0, 0)], dtype=object)
In [257]: offset = pd.offsets.CustomBusinessDay(calendar=cal)
In [258]: datetime.datetime(2012, 5, 25) + offset
Out[258]: Timestamp('2012-05-29 00:00:00')
In [259]: datetime.datetime(2012, 7, 3) + offset
Out[259]: Timestamp('2012-07-05 00:00:00')
In [260]: datetime.datetime(2012, 7, 3) + 2 * offset
Out[260]: Timestamp('2012-07-06 00:00:00')
In [261]: datetime.datetime(2012, 7, 6) + offset
Out[261]: Timestamp('2012-07-09 00:00:00')
AbstractHolidayCalendar
的类属性 start_date
与 end_date
定义日期范围。默认值如下:
In [262]: AbstractHolidayCalendar.start_date
Out[262]: Timestamp('1970-01-01 00:00:00')
In [263]: AbstractHolidayCalendar.end_date
Out[263]: Timestamp('2030-12-31 00:00:00')
这两个日期可以用 datetime
、Timestamp
、字符串
修改。
In [264]: AbstractHolidayCalendar.start_date = datetime.datetime(2012, 1, 1)
In [265]: AbstractHolidayCalendar.end_date = datetime.datetime(2012, 12, 31)
In [266]: cal.holidays()
Out[266]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
get_calender
函数通过日历名称访问日历,返回的是日历实例。任意导入的日历都自动适用于此函数。同时,HolidayCalendarFactory
还提供了一个创建日历组合或含附加规则日历的简易接口。
In [267]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory,\
.....: USLaborDay
.....:
In [268]: cal = get_calendar('ExampleCalendar')
In [269]: cal.rules
Out[269]:
[Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f2460862c20>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]
In [270]: new_cal = HolidayCalendarFactory('NewExampleCalendar', cal, USLaborDay)
In [271]: new_cal.rules
Out[271]:
[Holiday: Labor Day (month=9, day=1, offset=<DateOffset: weekday=MO(+1)>),
Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f2460862c20>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]