深入浅出Pandas读书笔记
C14 Pandas时序数据Time Series
14.1 固定时间
14.1.1 时间的表示
时间戳Timestamp
Pandas是在Python的datetime的基础上建立时间对象的
14.1.2 创建时间点
pd.Timestamp()
是Pandas定义时间的主要函数, 代替Python中的datetime.datetime对象
# Timestamp支持的准换语法
pd.Timestamp(datetime.datetime(2020, 6 ,8))
pd.Timestamp('2020-6-8')
pd.Timestamp(2020, 6, 8)
# 支持直接转换timestamp
pd.Timestamp(2000000000, unit='s')
# 支持today, now
pd.Timestamp('now') # 等同 pd.Timestamp.now()
pd.Timestamp('today') # 等同 pd.Timestamp.today()
# 昨天
pd.Timestamp.today() - pd.Timedelta(days=1)
# 替换day为1, 本月的1号
pd.Timestamp.today().replace(day=1)
14.1.3 时间的属性
一个固定的时间包含丰富的属性, 包括时间所在的年份, 月份, 周几, 是否月初, 在哪个季度等
time = pd.Timestamp.now()
time.dayofweek # 周几, 周一为0
time.days_in_month # 本月有多少天 同time.daysinmonth
time.is_leap_year # 是否闰年
time.is_month_end # 是否月末
time.is_month_start # 是否月初
time.is_quarter_end # 当季度最后一天
time.is_quarter_start
time.is_year_end
time.is_year_start
time.quarter
time.week # 当前第几周, 同time.weekofyear
time.day # month, year, second, minute, hour
14.1.4 时间的方法
# 返回星期名
time.day_name # 'Saturday'
time.month_name # 'November'
time.normalize() # 将时间标准化, 去掉时分秒
# 将时间元素替换datetime.replace
time.replace(year=2019)
14.1.5 时间缺失值
pd.Timestamp(pd.NaT)
14.2 时长数据
14.2.1 创建时间差
pd.Timedelta()
表示时间差, 也就是时长
pd.Timestamp('2022-12-1') - pd.Timestamp('2022-11-1')
# Timedelta('30 days 00:00:00')
pd.Timedelta(1, unit='d') # Timedelta('1 days 00:00:00')
pd.Timedelta('1 day 2 hour') # Timedelta('1 days 02:00:00')
# 用关键字参数指定时间
pd.Timedelta(days=5, seconds=10)
pd.Timedelta(minutes=3000) # Timedelta('2 days 02:00:00')
# 使用带周期量的偏移量别名
pd.Timedelta('1D') # Timedelta('1 days 00:00:00')
pd.Timedelta('2W') # Timedelta('14 days 00:00:00')
# 带单位的整型数字
pd.Timedelta(1, unit='D')
pd.Timedelta(4, unit='W') # Timedelta('28 days 00:00:00')
# 使用datetime.timedelta
pd.Timedelta(datetime.timedelta(days=1))
# 负值
pd.Timedelta(-1, unit='H') # Timedelta('-1 days +23:00:00')
# 使用时间偏移对象DateOffsets创建
pd.Timedelta(pd.offsets.Minute(2)) # Timedelta('0 days 00:02:00')
pd.Timedelta(pd.offsets.Day(2))
# pd.to_timedelta
pd.to_timedelta(pd.offsets.Day(1)) # Timedelta('1 days 00:00:00')
14.2.2 时长的加减
# 时长相加减
pd.Timedelta(days=1) + pd.Timedelta(pd.offsets.Hour(5)) # Timedelta('1 days 05:00:00')
pd.Timedelta(days=1) - pd.Timedelta(pd.offsets.Hour(5)) # Timedelta('0 days 19:00:00')
# 固定时间与时长相加减
pd.Timestamp('2020-10-1') + pd.Timedelta(days=10)
pd.Timestamp('2020-10-1') + pd.Timedelta(weeks=3)
14.2.3 时长的属性
tdt = pd.Timedelta(days=10, minutes=9)
tdt.days
tdt.seconds
14.2.4 时长索引
TimedeltaIndex
14.3 时间序列
将众多的固定时间组织起来就形成了时间序列, 即所谓的时序数据. 固定时间在时序数据中可以按照一定的频率组织
14.3.1 时序索引
DatetimeIndex是时间索引对象, 一般由to_datetime()
或date_range()
来创建
pd.to_datetime(['2020-10-1', '2020-11-1'])
# DatetimeIndex(['2020-10-01', '2020-11-01'], dtype='datetime64[ns]', freq=None)
pd.date_range('2020-10-1', '2020-10-30')
pd.date_range('2020-10-1', periods=30) # 同上
#
pd.bdate_range('2020-10-1', periods=10) # 跳过周六周日的时间索引序列
14.3.2 创建时序数据
idx = pd.date_range('2020-11-1', periods=10)
s = pd.Series(range(len(idx)), index=idx)
df = pd.DataFrame({'A': range(len(idx)), 'B': range(len(idx))[::-1]}, index=idx)
'''
A B
2020-11-01 0 9
2020-11-02 1 8
2020-11-03 2 7
2020-11-04 3 6
2020-11-05 4 5
2020-11-06 5 4
2020-11-07 6 3
2020-11-08 7 2
2020-11-09 8 1
2020-11-10 9 0
'''
14.3.3 数据访问
# 创建2020&2021, 以小时为频率的数据
idx = pd.date_range('2020-1-1', '2021-12-31', freq='H')
ts = pd.Series(np.random.randn(len(idx)), index=idx)
# 指定区间的
ts[5:10]
# 只筛选2020年的
ts['2020'] # ts[2020]是第2020个元素
# 指定天
ts['2020-10-1']
# 如果使用pd.Timestamp 有坑
ts[pd.Timestamp('2020-10-1')] # 这里会查询出Timestamp('2020-10-01 00:00:00')的值
# 查询2021-6月
ts['2020-6']
# 2021 6-10月
ts['2021-6':'2021-10']
# 时间粒度
ts.index.resolution # hour
# df.truncate() 返回一个新的对象, 避免直接赋值修改原数据
ts.truncate(before='2020-11-10', after='2020-12-1')
14.3.4 类型转换
s = pd.Series(['2020-10-1', '2020-12-1'])
s.astype('datetime64')
# pd.to_datetime转换
pd.to_datetime(s)
# pd.to_datetime()还可以将多列组合成一个时间进行转换
df = pd.DataFrame({'year': [2020, 2021, 2022],
'month': [10, 11, 12],
'day': [10, 11, 12]
})
pd.to_datetime(df.loc[:, :])
'''
0 2020-10-10
1 2021-11-11
2 2022-12-12
dtype: datetime64[ns]
'''
14.3.5 按格式转换
如果时间格式不规范, 指定转换格式
pd.to_datetime('2020-12-11', format='%Y-%d-%m', errors='ignore')
pd.to_datetime(200000000, unit='s') # 转换时间戳
# 将列表转换为时间
pd.to_datetime([10, 11, 12, 13], unit='D', origin='2020-10-1')
# DatetimeIndex(['2020-10-11', '2020-10-12', '2020-10-13', '2020-10-14'], dtype='datetime64[ns]', freq=None)
14.3.6 时间访问器.dt
.dt.<method>
, 用它可以使用time.dt.xxx的形式来访问时间序列数据的属性和调用他们的方法
s = pd.Series(pd.date_range('2020-1-1', periods=5))
s.dt.day_name() # 各天是星期几
s.dt.time
s.dt.year # month, day, hour, minute, second, microsecond, nanosecond
# 与周, 月, 年相关的属性
s.dt.week # 等同于, 将被Series.dt.isocalendar().week替代
s.dt.dayofweek # s.dt.weekday, 一周中的周几
s.dt.quarter
s.dt.is_month_start
s.dt.is_month_end
s.dt.is_quarter_start
s.dt.is_quarter_end
s.dt.is_year_start
s.dt.is_year_end
s.dt.is_leap_year # 闰年
s.dt.daysinmonth # 本月有多少天
s.dt.normalize() # 去掉时分秒
s.dt.month_name() # January
s.dt.day_name() # Wednesday
s.dt.strftime('%F')
14.3.7 时长数据访问器
ts = pd.Series(pd.to_timedelta(np.arange(5), unit='H'))
ts.dt.seconds
14.3.8 时序数据移动 shift()
14.3.9 频率转换 asfreq()
将时间序列由一个频率单位更换为另一个频率单位, 实现时间颗粒度的变化.
rng = pd.date_range('2020-11-1', '2020-12-1')
ts = pd.Series(range(len(rng)), index=rng)
# 将它的频率变为更细的粒度, 会产生缺失值, 用0填充缺失值
ts.asfreq(pd.offsets.Hour(12), fill_value=0)
# 用指定方法填充
ts.asfreq(pd.offsets.Hour(12), method='ffill')
14.4 时间偏移 DataOffset
DateOffset 与 Timedelta的区别
DateOffset使用日历中日期的规则, 而不是进行事件性质的算数计算
Timedelata是一个时长间隔
14.4.1 DateOffset对象
t = pd.Timestamp('2020-10-1')
t + pd.Timedelta(days=1)
t + pd.DateOffset(days=1) # 结果同上
# 加2个工作日
d = pd.Timestamp('2020-10-30')
d.day_name() # 'Friday'
new_day = d + pd.offsets.BDay(2)
new_day # Timestamp('2020-11-03 00:00:00')
new_day.day_name() # 'Tuesday'
与时长Timedelta不同, 时间偏移DateOffset不是数学意义上的增加或减少, 而是根据实际生活的日历对现有时间进行偏移
14.4.2 偏移别名
14.4.3 移动偏移
14.4.4 应用偏移
14.4.5 偏移参数
import datetime
d = datetime.datetime(2020, 6, 1, 9, 0)
d_new = d + pd.offsets.Waek(1, weekday=4, normalize=True) # 可以指定偏移一周, 偏移到周中的周四
# 当年年末
d + pd.offsets.YearEnd(normalize=True)
# 当年某月月末
d + pd.offsets.YearEnd(month=6, normalize=True)
14.4.6 相关查询
当使用日期作为索引的DataFrame时, 此函数可以基于日期偏移量选择
i = pd.date_range('2020-10-1', periods=4, freq='2D')
ts = pd.DataFrame({'A': [1, 2, 3, 4]}, index=i)
# 取时间序列上最后的3天
ts.last('3D') # 前3天 ts.first('3D')
'''
A
2020-10-05 3
2020-10-07 4
'''
# 取最后3行
ts.tail(3)
'''
A
2020-10-03 2
2020-10-05 3
2020-10-07 4
'''
14.4.7 与时序的计算
可以对Series或DatetimeIndex时间索引列应用时间偏移
rng = pd.date_range('2020-1-1', '2020-1-3')
s = pd.Series(rng)
s + pd.DateOffset(months=2)
s - pd.DateOffset(months=2)
s - pd.DateOffset(days=2)
14.4.8 锚定偏移
14.4.9 自定义工作时间
14.5 时间段
14.5.1 Period对象
我们用pd.Period()
创建时间段对象
14.5.2 属性方法
14.5.3 时间段计算
14.5.4 时间段索引
14.5.5 数据查询
14.5.6 相关类型转换
14.6 时间操作
14.6.1 市区转换
14.6.2 时间的格式化
# 解析时间格式
pd.to_datetime('2020-11-13', format='%Y-%m-%d')
# Timestamp('2020-11-13 00:00:00')
pd.Timestamp.now().strftime('%F') # '2022-11-20'
14.6.3 时间重采样
Pandas可以对时序数据按不同的频率进行重采样操作, resample()可以按照5分钟, 15分钟, 半小时等频率进行分组, 然后完成聚合计算.
idx = pd.date_range('2020-1-1', periods=500, freq='min')
ts = pd.Series(range(len(idx)), index=idx)
# 按5分钟重采样
ts.resample('5MIN').sum()
ts.resample('5MIN').mean()
ts.resample('5MIN').max()
14.6.4 上采样 upsampling
上采样upsampling一般应用在图像学中, 目的是放大图像. 由于原数据有限, 放大图像后需要对缺失值进行内插值填充.