Python 之 Pandas 生成时间戳范围Pandas 的时期函数 Period() 和时间序列 - 重采样 resample
Posted 虚心求知的熊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 之 Pandas 生成时间戳范围Pandas 的时期函数 Period() 和时间序列 - 重采样 resample相关的知识,希望对你有一定的参考价值。
文章目录
- 在开始之前,我们先导入 numpy 和 pandas 库,同时导入 python 内置的模块。
import pandas as pd
import numpy as np
import time
import datetime
一、生成时间戳范围
- 有时候,我们可能想要生成某个范围内的时间戳。例如,我想要生成 “2018-6-26” 这一天之后的 8 天时间戳,我们可以使用
date_range
和bdate_range
来完成时间戳范围的生成。 - 我们可以通过 date_range() 返回固定频率的 DatetimeIndex。
- 其语法模板如下:
date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False, name=None, closed=None, **kwargs)
- 他返回等距时间点的范围(其中任意两个相邻点之间的差值由给定频率指定),以便它们都满足 start <[= ]x <[=] end,其中第一个和最后一个分别为。该范围内的第一个和最后一个时间点位于 freq 的边界(如果以频率字符串的形式给出)或对 freq 有效。
- 其参数含义如下:
- start 表示生成日期的左边界。
- end 表示生成日期的左边界。
- periods 表示要生成的周期数。
- freq 表示频率, default ‘D’ ,频率字符串可以有倍数,例如 ‘5H’。
- tz 表示时区用于返回本地化日期时间索引的时区名称,例如 “Asia/Hong_Kong”。默认情况下,生成的 DatetimeIndex 是时区初始索引。
- normalize: 默认 False, 在生成日期范围之前,将开始/结束日期标准化。
- name:默认 None 设置返回 DatetimeIndex name。
1. 指定值
- 默认是包含开始和结束时间,默认频率使用的 D(天)。
- 示例 1:我们生成从 20210101 到 20210108 之间以天为单位长度的时间戳。
pd.date_range(start='1/1/2021', end='1/08/2021')
#3DatetimeIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
# '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
# dtype='datetime64[ns]', freq='D')
- 示例 2:我们生成 2010 年到 2011 年之间以天为单位长度的时间戳。
pd.date_range(start='2010', end='2011')
#DatetimeIndex(['2010-01-01', '2010-01-02', '2010-01-03', '2010-01-04',
# '2010-01-05', '2010-01-06', '2010-01-07', '2010-01-08',
# '2010-01-09', '2010-01-10',
# ...
# '2010-12-23', '2010-12-24', '2010-12-25', '2010-12-26',
# '2010-12-27', '2010-12-28', '2010-12-29', '2010-12-30',
# '2010-12-31', '2011-01-01'],
# dtype='datetime64[ns]', length=366, freq='D')
2. 指定开始日期并设置期间数
- 我们也可以指定开始时间和中间经过几天。
pd.date_range(start='1/1/2018', periods=8)
#DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
# '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
# dtype='datetime64[ns]', freq='D')
- 我们也可以指定开始、结束和期间,频率就会自动生成(线性间隔)。
- 例如,我们将开始定为 20180424,结束定为 20180427,期间定为 3,那么他就会自动以一天半为间隔进行时间戳的生成。
pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)
#DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00',
# '2018-04-27 00:00:00'],
# dtype='datetime64[ns]', freq=None)
- 如果我们不定义开始值,只定义结束值和期间,那么他会向前推导时间戳。
pd.date_range(end='2018-04-24', periods=4)
#DatetimeIndex(['2018-04-21', '2018-04-22', '2018-04-23', '2018-04-24'], #dtype='datetime64[ns]', freq='D')
3. 频率 freq
freq | 描述 |
---|---|
Y | 年 |
M | 月 |
D | 日(默认) |
T(MIN) | 分钟 |
S | 秒 |
L | 毫秒 |
U | 微妙 |
A-DEC | 每年指定月份的最后一个日历日 |
W-MON | 指定每月的哪个星期开始 |
WOM_2MON | 指定每个月的第几个星期的星期几开始(这里是第二个星期的星期一) |
Q-DEC(Q-月) | 指定月为季度末,每个季度末最后一月的最后一个日历日 |
B,(M,Q,A),S | 分别代表了工作日(以月为频率,以季度为频率,以年为频率),最接近月 |
B | 工作日 |
- 其中,月和星期的缩写基本是英语的前三个字母大写为准。
- 其中,Q-月只有三种情况:1-4-7-10,2-5-8-11,3-6-9-12。
- 例如,我们可以通过 W-MON,从指定星期一开始算起。
pd.date_range('2022/1/1','2022/2/1', freq = 'W-MON')
#DatetimeIndex(['2022-01-03', '2022-01-10', '2022-01-17', '2022-01-24',
# '2022-01-31'],
# dtype='datetime64[ns]', freq='W-MON')
- 例如,我们可以通过 WOM-2MON,从每月的第二个星期一开始。
pd.date_range('2022/1/1','2022/5/1', freq = 'WOM-2MON')
#DatetimeIndex(['2022-01-10', '2022-02-14', '2022-03-14', '2022-04-11'], #dtype='datetime64[ns]', freq='WOM-2MON')
- 我们可以只返回时间范围内的工作日。
pd.date_range('2022/1/1','2022/1/5', freq = 'B')
#DatetimeIndex(['2022-01-03', '2022-01-04', '2022-01-05'], dtype='datetime64[ns]', freq='B')
- 我们可以以小时为单位长度返回时间戳。
pd.date_range('2022/1/1','2022/1/2', freq = 'H')
- 我们可以以分钟为单位长度返回时间戳。
pd.date_range('2022/1/1 12:00','2022/1/1 12:10', freq = 'T')
- 我们可以以秒、毫秒和微秒为单位长度返回时间戳。
pd.date_range('2022/1/1 12:00:00','2022/1/1 12:00:10', freq = 'S')
pd.date_range('2022/1/1 12:00:00','2022/1/1 12:00:10', freq = 'L')
pd.date_range('2022/1/1 12:00:00','2022/1/1 12:00:10', freq = 'U')
- 我们可以通过 M 返回每月的最后一个日历日。
pd.date_range('2017','2018', freq = 'M')
#DatetimeIndex(['2017-01-31', '2017-02-28', '2017-03-31', '2017-04-30',
# '2017-05-31', '2017-06-30', '2017-07-31', '2017-08-31',
# '2017-09-30', '2017-10-31', '2017-11-30', '2017-12-31'],
# dtype='datetime64[ns]', freq='M')
- 我们可以通过 Q-月,返回每个季度末最后一月的最后一个日历日。
print(pd.date_range('2017','2020', freq = 'Q-DEC'))
#DatetimeIndex(['2017-03-31', '2017-06-30', '2017-09-30', '2017-12-31',
# '2018-03-31', '2018-06-30', '2018-09-30', '2018-12-31',
# '2019-03-31', '2019-06-30', '2019-09-30', '2019-12-31'],
# dtype='datetime64[ns]', freq='Q-DEC')
- 我们可以通过 A-月,返回每年指定月份的最后一个日历日。
print(pd.date_range('2017','2020', freq = 'A-DEC'))
#DatetimeIndex(['2017-12-31', '2018-12-31', '2019-12-31'], dtype='datetime64[ns]', freq='A-#DEC')
- 我们可以通过 BM 返回每的最后一个工作日,BQ-月返回每个季度末最后一月的最后一个工作日,BA-月返回每年指定月份的最后一个工作日。
pd.date_range('2017','2018', freq = 'BM')
pd.date_range('2017','2020', freq = 'BQ-DEC')
pd.date_range('2017','2020', freq = 'BA-DEC')
- 我们可以通过 pd.date_range() 指定时间频率(下面以 7 天,2 小时 30 分钟,2 月为例)。
print(pd.date_range('2017/1/1','2017/2/1', freq = '7D'))
print(pd.date_range('2017/1/1','2017/1/2', freq = '2h30min'))
print(pd.date_range('2017','2018', freq = '2M'))
4. closed
- closed 是觉得我们是否包含 start 值和 end 值,默认情况下是两个值都包含。
- 如果 closed 设置为 left,则表示不包含 end 值。
- 如果 closed 设置为 right,则表示不包含 start 值。
- 我们先生成初始数据,便于后续的观察。
pd.date_range(start='1/1/2021', end='1/08/2021')
#DatetimeIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
# '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
# dtype='datetime64[ns]', freq='D')
- 我们将 closed 设置为 left。
pd.date_range(start='1/1/2021', end='1/08/2021',closed='left')
#DatetimeIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
# '2021-01-05', '2021-01-06', '2021-01-07'],
# dtype='datetime64[ns]', freq='D')
- 我们将 closed 设置为 right。
pd.date_range(start='1/1/2021', end='1/08/2021',closed='right')
#DatetimeIndex(['2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
# '2021-01-06', '2021-01-07', '2021-01-08'],
# dtype='datetime64[ns]', freq='D')
- 我们可以通过
bdate_range(start=None, end=None, periods=None, freq='B')
语法返回固定频率的 DatetimeIndex,默认频率为 B(工作日)。
pd.bdate_range(start='2022-01-01', end='2022-02-01')
#DatetimeIndex(['2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06',
# '2022-01-07', '2022-01-10', '2022-01-11', '2022-01-12',
# '2022-01-13', '2022-01-14', '2022-01-17', '2022-01-18',
# '2022-01-19', '2022-01-20', '2022-01-21', '2022-01-24',
# '2022-01-25', '2022-01-26', '2022-01-27', '2022-01-28',
# '2022-01-31', '2022-02-01'],
# dtype='datetime64[ns]', freq='B')
二、Pandas 的时期函数 Period()
- 我们可以直接使用 period() 输出时期,他默认是 A-DEC(每年指定月份的最后一个日历日)。
p = pd.Period('2017')
p
Period('2017', 'A-DEC')
- 也可以对他的频率进行设置。
p = pd.Period('2017-1', freq = 'M')
print(p, type(p))
#2017-01 <class 'pandas._libs.tslibs.period.Period'>
- (1) 可以通过加减整数可以实现对 Period 的移动(默认是以月为单位进行加减)。
print(p + 1)
print(p - 2)
#2017-02
#2016-11
- (2) 如果两个 Period 对象拥有相同频率,则它们的差就是它们之间的单位数量。
p = pd.Period('2017-1', freq = 'M')
print(p, type(p))
pd.Period('2018', freq='M') - p
#2017-01 <class 'pandas._libs.tslibs.period.Period'>
#<12 * MonthEnds>
- (3) period_range 函数可用于创建规则的时期范围。
rng = pd.period_range('2021-1-1', '2021-6-1')
rng
#PeriodIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
# '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08',
# '2021-01-09', '2021-01-10',
# ...
# '2021-05-23', '2021-05-24', '2021-05-25', '2021-05-26',
# '2021-05-27', '2021-05-28', '2021-05-29', '2021-05-30',
# '2021-05-31', '2021-06-01'],
# dtype='period[D]', length=152, freq='D')
- (4) PeriodIndex 类的构造函数允许直接使用一组字符串表示一段时期。
- 这里需要注意的是,我们必须指定 freq。
values = ['200103', '200104', '200105']
index = pd.PeriodIndex(values, freq='M')
index
#PeriodIndex(['2001-03', '2001-04', '2001-05'], dtype='period[M]', freq='M')
- (5) 时期的频率转换 asfreq。
- freq 当中的 A-月表示每年指定月份的最后一个日历日。
p = pd.Period('2021', freq='A-DEC')
p
#Period('2021', 'A-DEC')
- 我们可以使用 asfreq 将时期的频率进行转换 M(月),通过设置 how 的参数为 start 或者 end 决定是起始还是结束。
p.asfreq('M')
#Period('2021-12', 'M')
- 我们将 how 设置为 start,也可写 how = ‘s’。
p.asfreq('M', how="start")
#Period('2021-01', 'M')
- 我们将 how 设置为 end,也可写 how = ‘e’。
p.asfreq('M', how="end")
#Period('2021-12', 'M')
- 也可以将 asfreq 转换为 s(秒)。
p.asfreq('H',how="s")
#Period('2021-01-01 00:00', 'H')
- (6) 对于 PeriodIndex 或 TimeSeries 的频率转换方式相同。
- 示例 1:
rng = pd.period_range('2006', '2009', freq='A-DEC')
rng
#PeriodIndex(['2006', '2007', '2008', '2009'], dtype='period[A-DEC]', freq='A-DEC')
- 示例 2:
ts = pd.Series(np.random.rand(len(rng)), rng)
ts
#2006 0.253270
#2007 0.369844
#2008 0.432757
#2009 0.290279
#Freq: A-DEC, dtype: float64
- 示例 3:
ts.asfreq('M', how='s')
#2006-01 0.253270
#2007-01 0.369844
#2008-01 0.432757
#2009-01 0.290279
#Freq: M, dtype: float64
- 示例 5:对 asfreq 的 how 参数不设置就默认为 end。
ts.asfreq('M')
#2006-12 0.253270
#2007-12 0.369844
#2008-12 0.432757
#2009-12 0.290279
#Freq: M, dtype: float64
- 示例 6:
ts2 = pd.Series(np.random.rand(len(rng)), index = rng.asfreq('D', how = 'start'))
ts2
#2006-01-01 0.906302
#2007-01-01 0.592067
#2008-01-01 0.715987
#2009-01-01 0.382779
#Freq: D, dtype: float64
- (7) 时间戳与时期之间的转换:pd.to_period()、pd.to_timestamp()。
- 我们通过生成一个从 20170101 开始,以月为间隔生成 10 个数据和从 2017 到 2018 以月为间隔的两个初始数据,便于后续的操作观察。
rng = pd.date_range('2017/1/1', periods = 10, freq = 'M')
prng = pd.period_range('2017','2018', freq = 'M')
print(rng)
print(prng)
#DatetimeIndex(['2017-01-31', '2017-02-28', '2017-03-31', '2017-04-30',
# '2017-05-31', '2017-06-30', '2017-07-31', '2017-08-31',
# '2017-09-30', '2017-10-31'],
# dtype='datetime64[ns]', freq='M')
#PeriodIndex(['2017-01', '2017-02', '2017-03', '2017-04', '2017-05', '2017-06',
# '2017-07', '2017-08', '2017-09', '2017-10', '2017-11', '2017-12',
# '2018-01'],
# dtype='period[M]', freq='M')
- 通过随机函数生成与 rng 长度个数相同的数据,并将标签设置为 rng。
ts1 = pd.Series(np.random.rand(len(rng)), index = rng)
ts1
#2017-01-31 0.088059
#2017-02-28 0.894889
#2017-03-31 0.700513
#2017-04-30 0.485417
#2017-05-31 0.868871
#2017-06-30 0.723924
#2017-07-31 0.422115
#2017-08-31 0.225944
#2017-09-30 0.816514
#2017-10-31 0.269220
#Freq: M, dtype: float64
- 我们可以将每月最后一日,转化为每月。to_period()参数为空, 推断每日频率(head 表示读取前五个数据)。
ts1.to_period().head()
#2017-01 0.088059
#2017-02 0.894889
#2017-03 0.700513
#2017-04 0.485417
#2017-05 0.868871
#Freq: M, dtype: float64
- 以和 rng 相同的方式通过随机函数生成 prng,并通过 head 读取他的前五个数据。
ts2 = pd.Series(np.random.rand(len(prng)), index = prng)
print(ts2.head())
#2017-01 0.766327
#2017-02 0.662090
#2017-03 0.653259
#2017-04 0.737125
#2017-05 0.530920
#Freq: M, dtype: float64
- 可以将其转换为时间戳。
print(ts2.to_timestamp().head())
#2017-01-01 0.766327
#2017-02-01 0.662090
#2017-03-01 0.653259
#2017-04-01 0.737125
#2017-05-01 0.530920
#Freq: MS, dtype: float64
三、时间序列 - 重采样 resample
- Pandas 中的 resample 函数,被叫做重新采样,是对原样本重新处理的一个方法,是一个对常规时间序列数据重新采样和频率转换的便捷的方法。
- 他的作用是重新取样时间序列数据。
- 对象必须具有类似 datetime 的索引(DatetimeIndex、PeriodIndex 或 TimedeltaIndex),或将类似 datetime 的值传递给 on 或 level 关键字。
- 其语法模板如下:
DataFrame.resample(rule, closed=None, label=None, level=None)
- 其部分参数含义如下:
- rule 表示目标转换的偏移量字符串或对象。
- closed 表示在降采样时,各时间段的哪一段是闭合的,‘right’ 或 ‘left’,默认‘right’。
- label 表示在降采样时,如何设置聚合值的标签,例如,9:30-9:35会被标记成9:30还是9:35,默认是 9:35。
- 我们可以生成从 20170101 开始的 12 个数,并以他为标签,通过函数生成对应的数据,
rng = pd.date_range('20170101', periods = 12)
ts = pd.Series(np.arange(12), index = rng)
print(ts)
#2017-01-01 0
#2017-01-02 1
#2017-01-03 2
#2017-01-04 3
#2017-01-05 4
#2017-01-06 5
#2017-01-07 6
#2017-01-08 7
#2017-01-09 8
#2017-01-10 9
#2017-01-11 10
#2017-01-12 11
Freq: D, dtype: int32
- 我们将序列下采样到 5 天的数据箱中(以 5 天为时间单位),并将放入数据箱的时间戳的值相加。
ts.resample('5D').sum()
#2017-01-01 10
#2017-01-06 35
#2017-01-11 21
#Freq: 5D, dtype: int32
- 我们就会得到一个新的聚合后的 Series,聚合方式为求和,当然,我们也可以生成其他的聚合方式。
print(ts.resample('5D').mean(),'→ 求平均值\\n')
print(ts.resample('5D').max(),'→ 求最大值\\n')
print(ts.resample('5D').min(),'→ 求最小值\\n')
print(ts.resample('5D').median(),'→ 求中值\\n')
print(ts.resample('5D').first(),'→ 返回第一个值\\n')
print(ts.resample('5D').last(),'→ 返回最后一个值\\n')
print(ts.resample('5D').ohlc(),'→ OHLC重采样\\n')
- 知识点补充:
- OHLC 是金融领域的时间序列聚合方式,包括 open开盘、high 最大值、low 最小值、close 收盘。
- closed 表示各时间段哪一端是闭合(即包含)的,默认是右端闭合。
- 详解:这里 values 为 0-11,按照 5D 重采样,可以分为以下三类 [1,2,3,4,5],[6,7,8,9,10],[11,12]。
- left 表示指定间隔左边为结束,就是 [1,2,3,4,5],[6,7,8,9,10],[11,12]。
- right 表示指定间隔右边为结束,就是 [1],[2,3,4,5,6],[7,8,9,10,11],[12]。
- 我们将系列降采样到 5 天的箱中,但关闭箱间隔的左侧。
print(ts.resample('5D', closed = 'left').sum(), '→ left\\n')
#2006-01-01 0.25327
#2006-01-06 NaN
#2006-01-11 NaN
#2006-01-16 NaN
#2006-01-21 NaN
# ...
#2009-12-11 NaN
#2009-12-16 NaN
#2009-12-21 NaN
#2009-12-26 NaN
#2009-12-31 NaN
#Freq: 5D, Length: 293, dtype: float64 → left
- 我们将系列降采样到 5 天的箱中,但关闭箱间隔的右侧。
print(ts.resample('5D', closed = 'right').sum(), '→ right\\n')
#2005-12-27 NaN
#2006-01-01 0.25327
#2006-01-06 NaN
#2006-01-11 NaN
#2006-01-16 NaN
# ...
#2009-12-06 NaN
#2009-12-11 NaN
#2009-12-16 NaN
#2009-12-21 NaN
#2009-12-26 NaN
#Freq: 5D, Length: 293, dtype: float64 → right
以上是关于Python 之 Pandas 生成时间戳范围Pandas 的时期函数 Period() 和时间序列 - 重采样 resample的主要内容,如果未能解决你的问题,请参考以下文章