处理时间序列
文章目录
- 由于 Pandas 最初是为金融模型而创建的,因此它拥有一些功能非常强大的日期、时间、带时间索引数据的处理工具。日期与时间数据主要包含三类:
- 时间戳表示某个具体的时间点(例如 2018 年 12 月 16 日上午 7点)。
- 时间间隔与周期表示开始时间点与结束时间点之间的时间长度,例如 2018 年(指的是 2018 年 1 月 1 日至 2018 年 12 月 31 日这段时间间隔)。周期通常是指一种特殊形式的时间间隔,每个间隔长度相同,彼此之间不会重叠(例如,以 24 小时为周期构成每一天)。
- 时间增量(time delta)或持续时间(duration)表示精确的时间长度(例如,某程序运行持续时间 22.56 秒)。
1. Python的日期与时间工具
在 Python 标准库与第三方库中有许多可以表示日期、时间、时间增量和时间跨度(timespan)的工具。尽管 Pandas 提供的时间序列工具更适合用来处理数据科学问题,但是了解 Pandas 与 Python 标准库以及第三方库中的其他时间序列工具之间的关联性将大有裨益。
1.1 原生Python的日期与时间工具:datetime与dateutil
- Python 基本的日期与时间功能都在标准库的
datetime
模块中。如果和第三方库dateutil
模块搭配使用,可以快速实现许多处理日期与时间的功能。例如,你可以用datetime
类型创建一个日期:
from datetime import datetime
datetime(year=2018, month=12, day=16)
datetime.datetime(2018, 12, 16, 0, 0)
- 或者使用
dateutil
模块对各种字符串格式的日期进行正确解析:
from dateutil import parser
date = parser.parse("16th of December, 2018")
date
datetime.datetime(2018, 12, 16, 0, 0)
- 一旦有了 datetime 对象,就可以进行许多操作了,例如打印出这一天是星期几:
date.strftime('%A')
'Sunday'
-
在最后一行代码中,为了打印出是星期几,我们使用了一个标准字符串格式
(standard string format)
代码"%A"
,你可以在 Python 的datetime 文档(https://docs.python.org/3/library/datetime.html )的“strftime”节(https://docs.python.org/3/library/datetime.html#strftime-and-strptimebehavior )查看具体信息。关于 dateutil 的其他日期功能可以通过 dateutil 的在线文档(http://labix.org/python-dateutil )学习。还有 一个值得关注的程序包是 pytz(http://pytz.sourceforge.net/ ),这个工具解决了绝大多数时间序列数据都会遇到的难题:时区。 -
datetime 和 dateutil 模块在灵活性与易用性方面都表现出色, 你可以用这些对象及其相应的方法轻松完成你感兴趣的任意操作。 但如果你处理的时间数据量比较大,那么速度就会比较慢。就像之 前介绍过的 Python 的原生列表对象没有 NumPy 中已经被编码的数值类型数组的性能好一样,Python 的原生日期对象同样也没有 NumPy 中已经被编码的日期(encoded dates)类型数组的性能好。
-
Python 原生日期格式的性能弱点促使 NumPy 团队为 NumPy 增加了 自己的时间序列类型。 datetime64 类型将日期编码为 64 位整 数,这样可以让日期数组非常紧凑(节省内存)。 datetime64 需 要在设置日期时确定具体的输入类型:
import numpy as np
date = np.array('2018-12-16', dtype=np.datetime64)
date
array('2018-12-16', dtype='datetime64[D]')
- 只要有了这个日期格式,就可以进行快速的向量化运算:
date + np.arange(12)
array(['2018-12-16', '2018-12-17', '2018-12-18', '2018-12-19',
'2018-12-20', '2018-12-21', '2018-12-22', '2018-12-23',
'2018-12-24', '2018-12-25', '2018-12-26', '2018-12-27'],
dtype='datetime64[D]')
-
因为 NumPy 的 datetime64 数组内元素的类型是统一的,所以这种数组的运算速度会比 Python 的 datetime 对象的运算速度快很多,尤其是在处理较大数组时。
-
datetime64 与 timedelta64 对象的一个共同特点是,它们都是在基本时间单位(fundamental time unit)的基础上建立的。由于datetime64 对象是 64 位精度,所以可编码的时间范围可以是基本单元的 2**64 倍。也就是说,datetime64 在时间精度(time resolution)与最大时间跨度(maximum time span)之间达成了一种平衡。
-
比如你想要一个时间纳秒(nanosecond,ns)级的时间精度,那么你就可以将时间编码到 0~264 纳秒或 600 年之内,NumPy 会自动判断输入时间需要使用的时间单位。例如,下面是一个以天为单位的日期:
np.datetime64('2018-12-16')
numpy.datetime64('2018-12-16')
而这是一个以分钟为单位的日期:
np.datetime64('2018-12-16 12:00')
numpy.datetime64('2018-12-16T12:00')
需要注意的是,时区将自动设置为执行代码的操作系统的当地时区。你可以通过各种格式的代码设置基本时间单位。例如,将时间单位设置为纳秒:
np.datetime64('2018-12-16 12:59:59.50', 'ns')
numpy.datetime64('2018-12-16T12:59:59.500000000')
- NumPy 的 datetime64 文档(http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html )总结了所有支持相对与绝对时间跨度的时间与日期单位格式代码,下表对此总结如下:
代码 | 含义 | 时间跨度 (相对) | 时间跨度 (绝对) |
---|---|---|---|
Y | 年(year) | ± 9.2e18 | 年[9.2e18 BC, 9.2e18 AD] |
M | 月(month) | ± 7.6e17 | 年[7.6e17 BC, 7.6e17 AD] |
W | 周(week) | ± 1.7e17 | 年[1.7e17 BC, 1.7e17 AD] |
D | 日(day) | ± 2.5e16 | 年[2.5e16 BC, 2.5e16 AD] |
h | 时(hour) | ± 1.0e15 | 年[1.0e15 BC, 1.0e15 AD] |
m | 分(minute) | ± 1.7e13 | 年[1.7e13 BC, 1.7e13 AD] |
s | 秒(second) | ± 2.9e12 | 年[ 2.9e9 BC, 2.9e9 AD] |
ms | 毫秒(millisecond) | ± 2.9e9 | 年[ 2.9e6 BC, 2.9e6 AD] |
us | 微秒(microsecond) | ± 2.9e6 | 年[290301 BC, 294241 AD] |
ns | 纳秒(nanosecond) | ± 292 | 年[ 1678 AD, 2262 AD] |
ps | 皮秒(picosecond) | ± 106 | 天[ 1969 AD, 1970 AD] |
fs | 飞秒(femtosecond) | ± 2.6 | 小时[ 1969 AD, 1970 AD] |
as | 原秒(attosecond) | ± 9.2 | 秒[ 1969 AD, 1970 AD] |
- 对于日常工作中的时间数据类型,默认单位都用纳秒datetime64[ns],因为用它来表示时间范围精度可以满足绝大部分需求。
1.2 Pandas的日期与时间工具:理想与现实的最佳解决方案
- Pandas 所有关于日期与时间的处理方法全部都是通过 Timestamp对象实现的,它利用 numpy.datetime64 的有效存储和向量化接口将 datetime 和 dateutil 的易用性有机结合起来。Pandas 通过一组 Timestamp 对象就可以创建一个可以作为 Series 或DataFrame 索引的 DatetimeIndex,我们将在后面介绍许多类似 的例子。
- 例如,可以用 Pandas 的方式演示前面介绍的日期与时间功能。我们可以灵活处理不同格式的日期与时间字符串,获取某一天是星期几:
import pandas as pd
date = pd.to_datetime("16th of December, 2018")
date
Timestamp('2018-12-16 00:00:00')
date.strftime('%A')
'Sunday'
- 另外,也可以直接进行 NumPy 类型的向量化运算:
date + pd.to_timedelta(np.arange(12), 'D')
DatetimeIndex(['2018-12-16', '2018-12-17', '2018-12-18', '2018-12-19',
'2018-12-20', '2018-12-21', '2018-12-22', '2018-12-23',
'2018-12-24', '2018-12-25', '2018-12-26', '2018-12-27'],
dtype='datetime64[ns]', freq=None)
2. Pandas时间序列:用时间作索引
- Pandas 时间序列工具非常适合用来处理带时间戳的索引数据。例如,我们可以通过一个时间索引数据创建一个 Series 对象:
index = pd.DatetimeIndex(['2014-07-04', '2014-08-04','2015-07-04', '2015-08-04'])
data = pd.Series([0, 1, 2, 3], index=index)
data
2014-07-04 0
2014-08-04 1
2015-07-04 2
2015-08-04 3
dtype: int64
- 有了一个带时间索引的 Series 之后,就能用它来演示之前介绍过的Series 取值方法,可以直接用日期进行切片取值:
data['2014-07-04':'2015-07-04']
2014-07-04 0
2014-08-04 1
2015-07-04 2
dtype: int64
- 另外,还有一些仅在此类 Series 上可用的取值操作,例如直接通过年份切片获取该年的数据:
data['2015']
2015-07-04 2
2015-08-04 3
dtype: int64
3. Pandas时间序列数据结构¶
这里将介绍 Pandas 用来处理时间序列的基础数据类型。
- 针对时间戳数据,Pandas 提供了
Timestamp
类型。与前面介绍的一样,它本质上是 Python 的原生 datetime 类型的替代品,但是在性能更好的numpy.datetime64
类型的基础上创建。对应的索引数据结构是DatetimeIndex
。 - 针对时间周期数据,Pandas 提供了
Period
类型。这是利用numpy.datetime64
类型将固定频率的时间间隔进行编码。对应的索引数据结构是PeriodIndex
。 - 针对时间增量或持续时间,Pandas 提供了
Timedelta
类型。Timedelta 是一种代替 Python 原生 datetime.timedelta 类型的高性能数据结构,同样是基于numpy.timedelta64
类型。对应的索引数据结构是TimedeltaIndex
。 - 最基础的日期 / 时间对象是 Timestamp 和 DatetimeIndex。这两种对象可以直接使用,最常用的方法是
pd.to_datetime()
函数,它可以解析许多日期与时间格式。对pd.to_datetime()
传递一个日期会返回一个 Timestamp 类型&#