简介
时间序列是一种非常重要的数据类型,在许多领域(金融、物理、经济、生态学)都有应用,它描述了多个时间点观察到的结果,形成一段时间序列。时间序列中的频率变化可以是定长或不定长的,幸运的是,pandas提供了一套完整的方法处理时间序列。本文将介绍如何创建日期范围,设置时间频率以及按某种方式移动整个时间轴。
在python标准库中关于时间的模块有time、datetime、calendar库,关于时间的转换在这里就不再介绍。
日期范围
可以使用 date_range() 方法生成日期范围,它的格式如下:
1 |
pandas.date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False, name=None, closed=None, **kwargs) |
直接来看一个例子:
1 2 3 4 5 6 7 8 |
>>>import numpy as np >>>import pandas as pd >>>date = pd.date_range('12/7/2018', '12/14/2018') >>>date DatetimeIndex(['2018-12-07', '2018-12-08', '2018-12-09', '2018-12-10', '2018-12-11', '2018-12-12', '2018-12-13', '2018-12-14'], dtype='datetime64[ns]', freq='D') |
在这里创建了一个日期范围,格式为 '月/日/年'字符串,得到的 date 是一个包含时间的 DatetimeIndex 对象,它是一个可迭代对象。 freq='D' 表示变化频率按天计算,这是默认情况。
还可以使用 list() 函数查看或者使用 iter() 函数转换成迭代器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>>list(date) [Timestamp('2018-12-07 00:00:00', freq='D'), Timestamp('2018-12-08 00:00:00', freq='D'), Timestamp('2018-12-09 00:00:00', freq='D'), Timestamp('2018-12-10 00:00:00', freq='D'), Timestamp('2018-12-11 00:00:00', freq='D'), Timestamp('2018-12-12 00:00:00', freq='D'), Timestamp('2018-12-13 00:00:00', freq='D'), Timestamp('2018-12-14 00:00:00', freq='D')] >>>a = iter(date) >>>next(a) Timestamp('2018-12-07 00:00:00', freq='D') >>>a.__next__(date) Timestamp('2018-12-08 00:00:00', freq='D') |
除此之外,还可以传入起始或者结束日期,并且通过 periods 参数指定一个周期:
1 2 3 4 5 6 7 8 9 |
>>>pd.date_range(start='12/7/2018', periods=7) DatetimeIndex(['2018-12-07', '2018-12-08', '2018-12-09', '2018-12-10', '2018-12-11', '2018-12-12', '2018-12-13'], dtype='datetime64[ns]', freq='D') >>>pd.date_range(end='12/13/2018', periods=7) DatetimeIndex(['2018-12-07', '2018-12-08', '2018-12-09', '2018-12-10', '2018-12-11', '2018-12-12', '2018-12-13'], dtype='datetime64[ns]', freq='D') |
通过 start 或者 end 参数指定起始或结束日期。
因为我们没有设置时分秒,默认使用 00:00:00 ,但是如果我们事先就给定了时分秒,那么这个时间就会保留:
1 2 3 4 |
>>>pd.date_range(start='12/7/2018 6:00:00', periods=3) DatetimeIndex(['2018-12-07 06:00:00', '2018-12-08 06:00:00', '2018-12-09 06:00:00'], dtype='datetime64[ns]', freq='D') |
有时,我们又不需要读入数据中的时分秒,可以通过 normalize=True 将其变成 00:00:00 :
1 2 3 4 5 |
>>>date1 = pd.date_range(start='12/7/2018 6:00:00', periods=3, normalize=True) >>>list(date1) [Timestamp('2018-12-07 00:00:00', freq='D'), Timestamp('2018-12-08 00:00:00', freq='D'), Timestamp('2018-12-09 00:00:00', freq='D')] |
频率
时间序列变化频率有间隔相同的,也有不同的。许多字符串别名被赋予有用的普通时间序列频率。我们将这些别名称为偏移别名。这些别名如下表所示:
别名 | 描述 |
---|---|
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 | 纳秒频率 |
我们举几个例子:
1 2 3 4 |
>>>pd.date_range('12/7/2018', periods=3, freq='2h') DatetimeIndex(['2018-12-07 00:00:00', '2018-12-07 02:00:00', '2018-12-07 04:00:00'], dtype='datetime64[ns]', freq='2H') |
freq='2h' 表示以2个小时为基础频率。
1 2 3 4 |
>>>pd.date_range('12/7/2018', periods=3, freq='1h30min') DatetimeIndex(['2018-12-07 00:00:00', '2018-12-07 01:30:00', '2018-12-07 03:00:00'], dtype='datetime64[ns]', freq='90T') |
可以看出 '1h30min' 这种写法也是可以识别的。
1 2 |
>>>pd.date_range('12/7/2018', '4/7/2019', freq='BM') DatetimeIndex(['2018-12-31', '2019-01-31', '2019-02-28', '2019-03-29'], dtype='datetime64[ns]', freq='BM') |
通过 'BM' 获取每个月最后一个工作日,这在金融领域非常有用。
移动日期
Series和DataFrame都有一个 shift 方法可以保证索引不变的情况下,对数据进行移动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
>>>data = pd.Series(np.arange(4), index=pd.date_range('12/7/2018', periods=4, freq='M')) >>>data 2018-12-31 0 2019-01-31 1 2019-02-28 2 2019-03-31 3 Freq: M, dtype: int32 >>>data.shift(2) 2018-12-31 NaN 2019-01-31 NaN 2019-02-28 0.0 2019-03-31 1.0 Freq: M, dtype: float64 >>>data.shift(-2) 2018-12-31 2.0 2019-01-31 3.0 2019-02-28 NaN 2019-03-31 NaN Freq: M, dtype: float64 |
shift 方法直接对数据进行了移动,而索引值不发生变化,为了使索引也移动,需要传入 freq 参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>>data.shift(2, freq='M') 2019-02-28 0 2019-03-31 1 2019-04-30 2 2019-05-31 3 Freq: M, dtype: int32 #或者写成 >>>data.shift(1, freq='2M') 2019-02-28 0 2019-03-31 1 2019-04-30 2 2019-05-31 3 Freq: M, dtype: int32 |
此时就能看到索引值发生了移动。
总结
在本文中,我们介绍了创建日期范围的函数 date_range() ,它的应用是非常灵活的,结合频率和移动,能够获得非常有效的时间序列,在后面我们还会继续介绍更多处理时间序列的方式。