参考书目:《深入浅出Pandas:利用Python进行数据处理与分析》
pandas的时间对象如下,本次介绍 period和DateOffest类。
DateOffset时间偏移
#DateOffset 类似于时间差 Timedelta ,但它使用日历中时间日期的规则,而不是直接进行时间性质的算术计算,让时间更符合实际生活。
#比如,有些地区使用夏令时时,每日偏移时间有可能是 23 或 24 小时,甚至 25 个小时。
#DateOffset 示例,以下所示了夏令时的情况:
# 生成一个指定的时间,芬兰赫尔辛基时间执行夏令时
ts = pd.Timestamp('2016-10-30 00:00:00', tz='Europe/Helsinki')
ts
# Timestamp('2016-10-30 00:00:00+0300', tz='Europe/Helsinki')
# 增加一天
ts + pd.Timedelta(days=1)
# Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')
# 按日历时间
ts + pd.DateOffset(days=1)
# Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
#以下是增加工作日,出现跨周末的情况:
friday = pd.Timestamp('2022-01-05')
friday.day_name()
#'Friday'
# 增加两个工作日,从周五到周二
two_business_days = 2 * pd.offsets.BDay() #BDay()表示工作日
two_business_days.apply(friday)
# Timestamp('2022-01-09 00:00:00')
friday + two_business_days
# Timestamp('2022-01-09 00:00:00')
(friday + two_business_days).day_name()
# 'Tuesday'
#所有的日期偏移对象都在 pandas.tseries.offsets 下,其中 pandas.tseries.offsets.DateOffset 是标准的日期范围时间偏移类型,
#用于日期范围的标准日期增量类型。它默认是一个日历日。
from pandas.tseries.offsets import DateOffset
ts = pd.Timestamp('2017-01-01 09:10:11')
ts + DateOffset(months=3)
# Timestamp('2017-04-01 09:10:11')
ts + DateOffset(hours=2)
# Timestamp('2017-01-01 11:10:11')
ts + DateOffset()
# Timestamp('2017-01-02 09:10:11')
#移动偏移,
Offset 支持向前或向后偏移:
ts = pd.Timestamp('2020-06-06 00:00:00')
ts.day_name()
# 'Saturday'
# 定义一个工作小时偏移,默认是周一到周五 9-17 点,我们从 10点开始
offset = pd.offsets.BusinessHour(start='10:00')
# 向前偏移一个工作小时,是一个周一,跳过了周日
offset.rollforward(ts)
# Timestamp('2020-06-08 10:00:00')
# 向前偏移至最近的工作日,小时也会增加
ts + offset
# Timestamp('2020-06-08 11:00:00')
# 向后偏移,会在周五下班前的一个小时
offset.rollback(ts)
# Timestamp('2020-06-05 17:00:00')
ts - pd.offsets.Day(1) # 昨日
ts - pd.offsets.Day(2) # 前日
ts - pd.offsets.Week(weekday=0) - pd.offsets.Day(14) # 上周一
ts - pd.offsets.MonthEnd() - pd.offsets.MonthBegin() # 上月一日
#时间偏移操作会保留小时和分钟,有时候我们不在意具体的时间只开始从哪天开始,可以使用 normalize() 进行标准化到午夜 0 点:
offset.rollback(ts).normalize()
# Timestamp('2020-06-05 00:00:00')
应用偏移
#apply 可以使用偏移对象应用到一个时间上:
ts = pd.Timestamp('2020-06-01 09:00')
day = pd.offsets.Day() # 定义偏移对象
day.apply(ts) # 偏移对象应用到时间上
# Timestamp('2020-06-02 09:00:00')
day.apply(ts).normalize() # 标准化/归一化
# Timestamp('2020-06-02 00:00:00')
ts = pd.Timestamp('2020-06-01 22:00')
hour = pd.offsets.Hour()
hour.apply(ts)
# Timestamp('2020-06-01 23:00:00')
hour.apply(ts).normalize()
# Timestamp('2020-06-01 00:00:00')
hour.apply(pd.Timestamp("2014-01-01 23:30")).normalize()
# Timestamp('2014-01-02 00:00:00')
偏移参数
#上边我们偏移时只偏移了偏移对象的一个单位,可以传入参数支持多个单位和对象中的其他单位:
d = datetime.datetime(2020, 6, 1, 9, 0)
# datetime.datetime(2020, 6, 1, 9, 0)
d + pd.offsets.Week() # 偏移一周
# Timestamp('2020-06-08 09:00:00')
d + pd.offsets.Week(weekday=4) # 偏移4个周中的日期
# Timestamp('2020-06-05 09:00:00')
# 取一周第几天
(d + pd.offsets.Week(weekday=4)).weekday()
# 4
d - pd.offsets.Week() # 向后一周
# Timestamp('2020-05-25 09:00:00')
#参数也支持归一标准化 normalize:
d + pd.offsets.Week(normalize=True)
# Timestamp('2020-06-08 00:00:00')
d - pd.offsets.Week(normalize=True)
# Timestamp('2020-05-25 00:00:00')
#再比如,YearEnd 支持 month 指定月份:
d + pd.offsets.YearEnd()
# Timestamp('2020-12-31 09:00:00')
d + pd.offsets.YearEnd(month=6)
# Timestamp('2020-06-30 09:00:00')
序列与时间偏移操作
rng = pd.date_range('2012-01-01', '2012-01-03')
s = pd.Series(rng)
rng
# DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
s
'''
0 2012-01-01
1 2012-01-02
2 2012-01-03
dtype: datetime64[ns]
'''
rng + pd.DateOffset(months=2)
# DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq='D')
s + pd.DateOffset(months=2)
'''
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
'''
s - pd.DateOffset(months=2)
'''
0 2011-11-01
1 2011-11-02
2 2011-11-03
dtype: datetime64[ns]
'''
序列与时长的操作
#时长也支持与时间偏移进行操作,而且和时长与时长的操作一样:
s - pd.offsets.Day(2)
'''
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
'''
td = s - pd.Series(pd.date_range('2011-12-29', '2011-12-31'))
td
'''
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
'''
td + pd.offsets.Minute(15)
'''
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
'''
时间段
Period 对象
#创建时间段对象 年
pd.Period('2020')
#季度
pd.Period('2020Q3') #Q代表季度
Period 对象索引
#周期 dtype 拥有 freq 属性,并使用频率字符串以诸如 period[D]或period[M]之类的period[freq] 表示。
pi = pd.period_range('2016-01-01', periods=3, freq='M')
pi
# PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]', freq='M')
pi.dtype
# period[M]
#可以使用 period_range 可以构造 Period 序列:
prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
prng
pd.period_range('2022-11-01 10:00',periods=10,freq='H')
pd.period_range('2022-Q1','2022-Q4',freq='Q-NOV')#NOV为一年最后时间
pd.period_range('2022-Q1','2022-Q4',freq='Q')
Period 计算操作
#从周期中添加和减去整数会按其自身的频率移动周期。 具有不同频率(跨度)的Period 之间不允许进行算术运算。
# 一年一个周期
p = pd.Period('2012', freq='A-DEC')
p + 1 # 加一个周期,加一年
# Period('2013', 'A-DEC')
p - 3 # 减少一个周期,减去三年
# Period('2009', 'A-DEC')
# 两月一个周期
p = pd.Period('2012-01', freq='2M')
p + 2 # 加两个周期,到五月据的周期
# Period('2012-05', '2M')
p - 1 # 减去一个周期
# Period('2011-11', '2M')
# 周期频率不同不能计算,会报 IncompatibleFrequency 错误
p == pd.Period('2012-01', freq='3M')
相关类型转化
# 频率从月转为天
pi.astype('period[D]')
# PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]', freq='D')
# 转换为 DatetimeIndex
pi.astype('datetime64[ns]')
# DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')
# 转换为 PeriodIndex
dti = pd.date_range('2011-01-01', freq='M', periods=3)
dti
# DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='M')
dti.astype('period[M]')
# PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]', freq='M')
pd.date_range('2011-01-01','2022-01-06',freq='D').astype('period[M]')
pd.date_range('2011-01-01','2022-01-06',freq='D').astype('period[Q]')