时间序列/日期功能#
pandas 包含了用于处理各领域时间序列数据的丰富功能和特性。利用 NumPy 的 datetime64 和 timedelta64 数据类型,pandas 整合了来自 scikits.timeseries 等其他 Python 库的大量特性,并创造了大量新功能来操作时间序列数据。
例如,pandas 支持:
从各种来源和格式解析时间序列信息
生成固定频率的日期和时间跨度序列
带有时区信息地操作和转换日期时间
将时间序列重采样或转换为特定频率
使用绝对或相对时间增量进行日期和时间算术运算
pandas 提供了一套相对紧凑、自给自足的工具集来执行上述任务以及更多功能。
概述#
pandas 捕捉了 4 个通用的时间相关概念:
日期时间(Date times):一个特定的日期和时间,支持时区。类似于标准库中的
datetime.datetime。时间差(Time deltas):一个绝对的时间持续。类似于标准库中的
datetime.timedelta。时间跨度(Time spans):由一个时间点及其关联频率定义的一段持续时间。
日期偏移量(Date offsets):一个相对持续时间,尊重日历运算。类似于
dateutil包中的dateutil.relativedelta.relativedelta。
概念 |
标量类 |
数组类 |
pandas 数据类型 |
主要创建方法 |
|---|---|---|---|---|
日期时间 |
|
|
|
|
时间差 |
|
|
|
|
时间跨度 |
|
|
|
|
日期偏移量 |
|
|
|
|
对于时间序列数据,传统做法是将时间分量表示为 Series 或 DataFrame 的索引,以便进行关于时间元素的相应操作。
然而,Series 和 DataFrame 也可以直接支持将时间分量作为数据本身。
Series 和 DataFrame 在传递给那些构造函数时,对 datetime、timedelta 和 Period 数据具有扩展的数据类型支持和功能。但是 DateOffset 数据将被存储为 object 数据。
最后,pandas 将 null 的日期时间、时间差和时间跨度表示为 NaT,这对于表示缺失或 null 的日期类值非常有用,并且在行为上与浮点数据的 np.nan 类似。
时间戳 vs. 时间跨度#
带时间戳的数据是最基本的时间序列数据类型,它将值与时间点关联起来。对于 pandas 对象,这意味着使用时间点。
然而,在很多情况下,将变化变量等事物与一个时间跨度关联起来更为自然。Period 所表示的时间跨度可以显式指定,也可以从日期时间字符串格式中推断出来。
例如:
Timestamp 和 Period 可以作为索引。Timestamp 和 Period 的列表会被自动强制转换为 DatetimeIndex 和 PeriodIndex 。
pandas 允许您同时捕捉这两种表示形式并在它们之间进行转换。在底层,pandas 使用 Timestamp 实例来表示时间戳,并使用 DatetimeIndex 实例来表示时间戳序列。对于规则的时间跨度,pandas 对标量值使用 Period 对象,对跨度序列使用 PeriodIndex。对具有任意开始和结束点的非规则区间将来的版本会有更好的支持。
转换为时间戳#
要将 Series 或类列表对象(例如日期字符串、纪元时间戳或它们的混合)转换为时间戳,您可以使用 to_datetime 函数。当传递一个 Series 时,它会返回一个 Series``(具有相同的索引),而一个类列表对象则被转换为 ``DatetimeIndex:
如果您使用的日期以日开头(即欧洲风格),您可以传递 dayfirst 标志:
警告
您在上面的示例中看到 dayfirst 并不是严格执行的。如果一个日期无法以日优先的方式解析,它将被解析为 dayfirst 为 False 的情况,并且还会引发警告。
如果您将单个字符串传递给 to_datetime,它将返回单个 Timestamp。Timestamp 也可以接受字符串输入,但它不接受诸如 dayfirst 或 format 之类的字符串解析选项,因此如果需要这些选项,请使用 to_datetime。
您也可以直接使用 DatetimeIndex 构造函数:
可以传递字符串 ‘infer’ 以在创建时将索引的频率设置为推断出的频率:
提供 format 参数#
除了必需的日期时间字符串外,还可以传递一个 format 参数来确保特定的解析。这也有可能大大加快转换速度。
有关指定 format 选项时可用选项的更多信息,请参阅 Python 的 datetime documentation 。
从多个 DataFrame 列组装日期时间#
您还可以传递一个包含整数或字符串列的 DataFrame 来组装成一个 Series 的 Timestamps。
您可以仅传递您需要组装的列。
pd.to_datetime 会查找日期时间组件的以下标准命名约定:
必需:
year、month、day可选:
hour、minute、second、millisecond、microsecond、nanosecond
无效数据#
默认行为 errors='raise' 是在无法解析时引发错误:
传递 errors='coerce' 将无法解析的数据转换为 ``NaT``(不是时间):
纪元时间戳#
pandas 支持将整数或浮点数纪元时间转换为 Timestamp 和 DatetimeIndex。默认单位是纳秒,因为这是 Timestamp 对象内部存储的方式。然而,纪元时间通常以其他 unit 存储,可以指定。这些是根据 origin 参数指定的起点计算的。
备注
unit 参数使用的字符串与上面讨论的 format 参数不同 above 。可用的单位列在 pandas.to_datetime() 的文档中。
使用 tz 参数构造一个带有纪元时间戳的 Timestamp 或 DatetimeIndex 将引发 ValueError。如果您有另一个时区的实际时间纪元,您可以将这些纪元读作无时区的时间戳,然后将其本地化到相应的时区:
备注
纪元时间将被舍入到最近的纳秒。
警告
浮点数纪元时间的转换可能导致不准确和意想不到的结果。 Python floats 的十进制精度约为 15 位。从浮点数到高精度 Timestamp 的转换过程中舍入是不可避免的。实现精确转换的唯一方法是使用定宽类型(例如 int64)。
参见
从时间戳到纪元#
要执行上述操作的逆操作,即从 Timestamp 转换为“unix”纪元:
我们减去纪元(1970 年 1 月 1 日 UTC 午夜),然后按“单位”(1 秒)进行整除。
使用 origin 参数#
使用 origin 参数,可以为创建 DatetimeIndex 指定一个替代的起点。例如,使用 1960-01-01 作为起始日期:
默认设置为 origin='unix',它默认为 1970-01-01 00:00:00。通常称为“unix 纪元”或 POSIX 时间。
生成时间戳范围#
要生成带时间戳的索引,可以使用 DatetimeIndex 或 Index 构造函数,并传入日期时间对象的列表:
In practice this becomes very cumbersome because we often need a very long
index with a large number of timestamps. If we need timestamps on a regular
frequency, we can use the date_range() and bdate_range() functions
to create a DatetimeIndex. The default frequency for date_range is a
calendar day while the default for bdate_range is a business day:
date_range 和 bdate_range 等便利函数可以利用各种 frequency aliases :
date_range 和 bdate_range 可以轻松地使用 start、end、periods 和 freq 等参数的各种组合来生成一个日期范围。开始日期和结束日期是严格包含的,因此不会生成指定范围之外的日期:
指定 start、end 和 periods 将生成一个从 start 到 end``(包含两端)的均匀分布的日期范围,结果 ``DatetimeIndex 中包含 periods 个元素:
自定义频率范围#
bdate_range 还可以通过使用 weekmask 和 holidays 参数来生成自定义频率的日期范围。只有在传递自定义频率字符串时,这些参数才会被使用。
参见
时间戳限制#
时间戳表示的限制取决于所选的分辨率。对于纳秒分辨率,使用 64 位整数表示的时间跨度大约限制在 584 年:
当选择秒分辨率时,可用范围会增长到 +/- 2.9e11 年。不同的分辨率可以通过 as_unit 进行相互转换。
参见
索引#
DatetimeIndex 的主要用途之一是用作 pandas 对象的索引。 DatetimeIndex 类包含许多与时间序列相关的优化:
各种偏移量的日期范围在底层被预先计算并缓存,以便非常快速地生成后续的日期范围(只需获取一个切片)。
使用 pandas 对象的
shift方法进行快速移位。重叠的
DatetimeIndex对象以相同的频率进行联合运算速度非常快(这对于快速数据对齐很重要)。通过
year、month等属性可以快速访问日期字段。snap等正则化函数和非常快速的asof逻辑。
DatetimeIndex 对象拥有常规 Index 对象的所有基本功能,以及大量高级时间序列专用方法,便于进行频率处理。
备注
虽然 pandas 不强制要求日期索引排序,但如果日期未排序,其中一些方法可能会产生意外或不正确的行为。
DatetimeIndex 可以像常规索引一样使用,并提供其所有智能功能,如选择、切片等。
部分字符串索引#
可以传递日期和可解析为时间戳的字符串作为索引参数:
为了方便访问较长的时间序列,还可以将年份或年份和月份作为字符串传递:
这种切片方式也适用于带有 DatetimeIndex 的 DataFrame。由于部分字符串选择是一种标签切片的形式,因此端点**将被**包含在内。这包括匹配包含日期上的时间:
警告
在 pandas 1.2.0 版本开始,使用 getitem 索引 DataFrame 行(例如 frame[dtstring])的行为已被弃用(因为其歧义性,不确定是索引行还是选择列),并且将在未来版本中移除。使用 .loc 的等效方法(例如 frame.loc[dtstring])仍然受支持。
这从当月的第一个时间点开始,并包含该月的最后一天和最后一个时间点:
这指定了一个**包含该月最后一天的所有时间点**的停止时间:
这指定了一个**精确**的停止时间(与上面的不等价):
我们停止在包含的端点上,因为它在索引中:
DatetimeIndex 的部分字符串索引也适用于包含 MultiIndex 的 DataFrame:
使用字符串索引进行切片也会考虑 UTC 偏移。
切片与精确匹配#
用作索引参数的同一字符串,可以被视为切片或精确匹配,这取决于索引的分辨率。如果字符串的精度低于索引,则将其视为切片;否则,将其视为精确匹配。
考虑一个具有分钟级分辨率索引的 Series 对象:
精度低于分钟的时间戳字符串会得到一个 Series 对象。
精度为分钟(或更高)的时间戳字符串会得到一个标量,即它不会被转换为切片。
如果索引分辨率是秒,那么分钟精度的时间戳会得到一个 Series。
如果时间戳字符串被视为切片,它也可以使用 .loc[] 对 DataFrame 进行索引。
警告
但是,如果字符串被视为精确匹配,则在 DataFrame 的 [] 中的选择将是按列进行的,而不是按行进行的,请参阅 Indexing Basics 。例如,dft_minute['2011-12-31 23:59'] 将引发 KeyError,因为 '2012-12-31 23:59' 的分辨率与索引相同,并且没有名为该字符串的列:
要*始终*获得无歧义的选择,无论该行被视为切片还是单个选择,请使用 .loc。
另请注意,DatetimeIndex 的分辨率不能低于天。
精确索引#
如前一节所述,使用部分字符串索引 DatetimeIndex 取决于该时间段的“精度”,换句话说,该时间间隔相对于索引分辨率的精确程度。相反,使用 Timestamp 或 datetime 对象进行索引是精确的,因为这些对象具有确切的含义。它们也遵循*包含两个端点*的语义。
这些 Timestamp 和 datetime 对象具有精确的 hours, minutes, 和 seconds,即使它们没有被明确指定(它们是 0)。
默认情况下。
截断和花式索引#
提供了一个方便的 truncate() 函数,它类似于切片。请注意,truncate 假设 DatetimeIndex 中任何未指定的日期组件的值为 0,这与返回任何部分匹配日期的切片不同:
即使是复杂的、破坏 DatetimeIndex 频率规律的花式索引,也会生成一个 DatetimeIndex,尽管频率会丢失:
时间和日期组件#
从 Timestamp 或诸如 DatetimeIndex 这样的时间戳集合中,可以访问几个时间和日期属性。
属性 |
描述 |
|---|---|
year |
日期的年份 |
month |
日期的月份 |
day |
日期的天 |
hour |
日期的小时 |
minute |
日期的分钟 |
second |
日期的秒 |
microsecond |
日期的微秒 |
nanosecond |
日期的纳秒 |
date |
返回 datetime.date(不包含时区信息) |
time |
返回 datetime.time(不包含时区信息) |
timetz |
返回带有时区信息的本地时间 datetime.time |
dayofyear |
一年中的序数日 |
day_of_year |
一年中的序数日 |
weekofyear |
一年中的周序数 |
week |
一年中的周序数 |
dayofweek |
星期几,周一=0,周日=6 |
day_of_week |
星期几,周一=0,周日=6 |
weekday |
星期几,周一=0,周日=6 |
quarter |
日期的季度:1 月-3 月=1,4 月-6 月=2,以此类推。 |
days_in_month |
日期所在月份的天数 |
is_month_start |
指示是否为月份第一天的逻辑值(由频率定义) |
is_month_end |
指示是否为月份最后一天的逻辑值(由频率定义) |
is_quarter_start |
指示是否为季度第一天的逻辑值(由频率定义) |
is_quarter_end |
指示是否为季度最后一天的逻辑值(由频率定义) |
is_year_start |
指示是否为年份第一天的逻辑值(由频率定义) |
is_year_end |
指示是否为年份最后一天的逻辑值(由频率定义) |
is_leap_year |
指示日期是否属于闰年的逻辑值 |
此外,如果您有一个包含日期时间值的 Series,您可以通过 .dt 访问器访问这些属性,具体请参阅 .dt accessors 部分。
您可以从 ISO 8601 标准中获取 ISO 年份的年、周和日组件:
DateOffset 对象#
在前面的示例中,使用频率字符串(例如 'D')指定了定义以下内容的频率:
在使用
DatetimeIndex时date_range()中的日期时间间隔的间隔方式Period或PeriodIndex的频率
这些频率字符串映射到 DateOffset 对象及其子类。DateOffset 类似于表示时间跨度的 Timedelta ,但遵循特定的日历跨度规则。例如,Timedelta 的一天将始终在 datetimes 上增加 24 小时,而 DateOffset 的一天将把 datetimes 增加到第二天的同一时间,无论这一天是代表 23、24 还是 25 小时(由于夏令时)。但是,所有小于或等于小时的 DateOffset 子类(Hour、Minute、Second、Milli、Micro、Nano)其行为类似于 Timedelta ,并尊重绝对时间。
基本的 DateOffset 行为类似于 dateutil.relativedelta (relativedelta documentation ,它会根据指定的日历跨度来移位日期时间。可以使用算术运算符(+)来执行移位。
大多数 DateOffsets 都有关联的频率字符串或偏移别名,可以传递给 freq 关键字参数。可用的日期偏移和关联的频率字符串如下:
日期偏移 |
频率字符串 |
描述 |
|---|---|---|
无 |
通用偏移类,默认为绝对 24 小时 |
|
|
|
工作日(周一至周五) |
|
自定义工作日 |
|
|
一周,可以选择锚定在一周中的某一天 |
|
|
每个月的第 y 周的第 x 天 |
|
|
每个月最后一周的第 x 天 |
|
|
日历月尾 |
|
|
日历月首 |
|
|
工作日月份的结尾 |
|
|
工作日月份的开始 |
|
|
自定义工作日月份的结尾 |
|
|
自定义工作日月份的开始 |
|
|
15 号(或其他日期)和日历月尾 |
|
|
15 号(或其他日期)和日历月首 |
|
|
日历季度尾 |
|
|
日历季度首 |
|
|
工作日季度尾 |
|
|
工作日季度首 |
|
|
零售(又称 52-53 周)季度 |
|
|
日历年尾 |
|
|
日历年首 |
|
|
工作日年尾 |
|
|
工作日年首 |
|
|
零售(又称 52-53 周)年 |
|
无 |
复活节假期 |
|
|
工作小时 |
|
|
自定义工作小时 |
|
|
一个绝对天 |
|
|
一小时 |
|
|
一分钟 |
|
|
一秒钟 |
|
|
一毫秒 |
|
|
一微秒 |
|
|
一纳秒 |
DateOffsets 还有 rollforward() 和 rollback() 方法,用于将日期向前或向后移动到相对于该偏移量的有效偏移日期。例如,业务偏移量会将落在周末(周六和周日)的日期向前滚动到周一,因为业务偏移量在工作日运行。
这些操作默认保留时间(小时、分钟等)信息。要将时间重置为午夜,请在应用操作之前或之后使用 normalize() (取决于您是否希望在操作中包含时间信息)。
参数化偏移量#
一些偏移量在创建时可以被“参数化”以产生不同的行为。例如,“Week”偏移量用于生成每周数据,它接受一个 weekday 参数,该参数导致生成的日期始终位于一周中的特定某一天:
normalize 选项将对加法和减法生效。
另一个例子是参数化 YearEnd 并指定具体的结束月份:
将偏移量与 Series / DatetimeIndex 一起使用#
可以将偏移量与 Series 或 DatetimeIndex 一起使用,以将偏移量应用于每个元素。
如果偏移量类直接映射到 Timedelta``(``Day、Hour、Minute、Second、Micro、Milli、Nano),则它可以像 Timedelta 一样使用——更多示例请参阅 Timedelta section 。
请注意,某些偏移量(例如 BQuarterEnd)没有矢量化实现。它们仍然可以使用,但计算速度可能会显著变慢,并且会显示 PerformanceWarning。
自定义工作日#
CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,这些日历会考虑当地的节假日和当地的周末惯例。
作为有趣的例子,让我们看看埃及,那里普遍实行周五至周六的周末。
我们来映射到星期几的名称:
可以使用节假日日历提供节假日列表。有关更多信息,请参阅 holiday calendar 部分。
可以按照通常的方式定义尊重特定节假日日历的每月偏移量。
备注
频率字符串 ‘C’ 用于表示使用了 CustomBusinessDay DateOffset,需要注意的是,由于 CustomBusinessDay 是一个参数化类型,CustomBusinessDay 的实例可能不同,并且从 ‘C’ 频率字符串无法检测到这种差异。因此,用户需要确保在用户的应用程序中一致地使用 ‘C’ 频率字符串。
工作小时#
BusinessHour 类在 BusinessDay 上提供了工作小时表示,允许使用特定的开始和结束时间。
默认情况下,BusinessHour 使用 9:00 - 17:00 作为工作时间。添加 BusinessHour 将按小时频率递增 Timestamp。如果目标 Timestamp 超出工作时间,则移至下一个工作小时然后递增。如果结果超出工作时间的结束时间,则将剩余的小时添加到下一个工作日。
您还可以通过关键字参数指定 start 和 end 时间。该参数必须是具有 hour:minute 表示的 str 或 datetime.time 实例。指定秒、微秒和纳秒作为工作小时将导致 ValueError。
传入比 end 更晚的 start 时间表示午夜工作时间。在这种情况下,工作小时会跨越午夜并延续到第二天。有效的工作小时通过是否从有效 BusinessDay 开始来区分。
将 BusinessHour.rollforward 和 rollback 应用于非工作时间,结果是下一个工作小时的开始或前一天的结束。与其他偏移量不同,BusinessHour.rollforward 根据定义可能产生与 apply 不同的结果。
这是因为一天的营业时间结束等于下一天的营业时间开始。例如,在默认营业时间(9:00 - 17:00)下,2014-08-01 17:00 和 2014-08-04 09:00 之间没有间隔(0分钟)。
BusinessHour 认为周六和周日为节假日。要使用任意节假日,您可以使用 CustomBusinessHour 偏移量,具体解释如下节。
自定义营业时间#
CustomBusinessHour 是 BusinessHour 和 CustomBusinessDay 的混合体,允许您指定任意节假日。CustomBusinessHour 的工作方式与 BusinessHour 相同,只是它会跳过指定的自定义节假日。
您可以使用 BusinessHour 和 CustomBusinessDay 支持的关键字参数。
偏移量别名#
为有用的常见时间序列频率提供了一系列字符串别名。我们将这些别名称为*偏移量别名*。
别名 |
描述 |
|---|---|
B |
工作日频率 |
C |
自定义工作日频率 |
D |
日历日频率 |
W |
每周频率 |
ME |
月末频率 |
SME |
半月月末频率(15日和月末) |
BME |
工作月月末频率 |
CBME |
自定义工作月月末频率 |
MS |
月初频率 |
SMS |
半月月初频率(1日和15日) |
BMS |
工作月月初频率 |
CBMS |
自定义工作月月初频率 |
QE |
季末频率 |
BQE |
工作季末频率 |
QS |
季初频率 |
BQS |
工作季初频率 |
YE |
年末频率 |
BYE |
工作年年末频率 |
YS |
年年初频率 |
BYS |
工作年年初频率 |
h |
每小时频率 |
bh |
工作小时频率 |
cbh |
自定义工作小时频率 |
min |
每分钟频率 |
s |
每秒频率 |
ms |
毫秒 |
us |
微秒 |
ns |
纳秒 |
自 2.2.0 版本弃用: 别名 H、BH、CBH、T、S、L、U 和 N 已弃用,推荐使用别名 h、bh、cbh、min、s、ms、us 和 ns。
备注
在使用上述偏移量别名时,需要注意的是,像
date_range()、bdate_range()这样的函数只会返回start_date和end_date定义区间内的有效时间戳。如果start_date与频率不匹配,则返回的时间戳将从下一个有效时间戳开始;同样,对于end_date,返回的时间戳将停止在上一个有效时间戳。
例如,对于偏移量 MS,如果 start_date 不是月份的第一天,则返回的时间戳将从下一个月的 first day 开始。如果 end_date 不是月份的第一天,则最后一个返回的时间戳将是相应月份的 first day。
我们可以在上面的示例中看到 date_range() 和 bdate_range() 只会返回 start_date 和 end_date 之间的有效时间戳。如果这些不是给定频率的有效时间戳,它将回滚到 start_date 的下一个值(对于 end_date 则是上一个值)
周期别名#
为有用的常见时间序列频率提供了一系列字符串别名。我们将这些别名称为*周期别名*。
别名 |
描述 |
|---|---|
B |
工作日频率 |
D |
日历日频率 |
W |
每周频率 |
M |
每月频率 |
Q |
每季度频率 |
-12.4 |
每年频率 |
h |
每小时频率 |
min |
每分钟频率 |
s |
每秒频率 |
ms |
毫秒 |
us |
微秒 |
ns |
纳秒 |
自 2.2.0 版本弃用: 别名 A、H、T、S、L、U 和 N 已弃用,推荐使用别名 Y、h、min、s、ms、us 和 ns。
组合别名#
正如我们之前所见,在大多数函数中,别名和偏移量实例是可互换的:
您可以组合日和日内偏移量:
锚定偏移量#
对于某些频率,您可以指定锚定后缀:
别名 |
描述 |
|---|---|
W-SUN |
每周频率(周日)。与 ‘W’ 相同 |
W-MON |
每周频率(周一) |
W-TUE |
每周频率(周二) |
W-WED |
每周频率(周三) |
W-THU |
每周频率(周四) |
W-FRI |
每周频率(周五) |
W-SAT |
每周频率(周六) |
(B)Q(E)(S)-DEC |
每季度频率,年末为12月。与 ‘QE’ 相同 |
(B)Q(E)(S)-JAN |
每季度频率,年末为1月 |
(B)Q(E)(S)-FEB |
每季度频率,年末为2月 |
(B)Q(E)(S)-MAR |
每季度频率,年末为3月 |
(B)Q(E)(S)-APR |
每季度频率,年末为4月 |
(B)Q(E)(S)-MAY |
季度频率,财年于5月结束 |
(B)Q(E)(S)-JUN |
季度频率,财年于6月结束 |
(B)Q(E)(S)-JUL |
季度频率,财年于7月结束 |
(B)Q(E)(S)-AUG |
季度频率,财年于8月结束 |
(B)Q(E)(S)-SEP |
季度频率,财年于9月结束 |
(B)Q(E)(S)-OCT |
季度频率,财年于10月结束 |
(B)Q(E)(S)-NOV |
季度频率,财年于11月结束 |
(B)Y(E)(S)-DEC |
年度频率,固定在12月底。与 ‘YE’ 相同 |
(B)Y(E)(S)-JAN |
年度频率,固定在1月结束 |
(B)Y(E)(S)-FEB |
年度频率,固定在2月结束 |
(B)Y(E)(S)-MAR |
年度频率,固定在3月结束 |
(B)Y(E)(S)-APR |
年度频率,固定在4月结束 |
(B)Y(E)(S)-MAY |
年度频率,固定在5月结束 |
(B)Y(E)(S)-JUN |
年度频率,固定在6月结束 |
(B)Y(E)(S)-JUL |
年度频率,固定在7月结束 |
(B)Y(E)(S)-AUG |
年度频率,固定在8月结束 |
(B)Y(E)(S)-SEP |
年度频率,固定在9月结束 |
(B)Y(E)(S)-OCT |
年度频率,固定在10月结束 |
(B)Y(E)(S)-NOV |
年度频率,固定在11月结束 |
这些可以作为 date_range、bdate_range、DatetimeIndex 的构造函数参数,以及 pandas 中各种其他时间序列相关函数的参数。
锚定偏移量语义#
对于那些锚定到特定频率的开始或结束的偏移量(MonthEnd、MonthBegin、WeekEnd 等),以下规则适用于向前和向后滚动。
当 n 不为 0 时,如果给定日期不在锚定点上,则将其调整到下一个(上一个)锚定点,并向前或向后移动 |n|-1 步。
如果给定日期 在 锚定点上,则向前或向后移动 |n| 步。
当 n=0 时,如果日期在锚定点上则不移动,否则向前滚动到下一个锚定点。
假期 / 假日日历#
假期和日历提供了一种简单的方法来定义假期规则,以供 CustomBusinessDay 或其他需要预定义假期集进行分析的场景使用。``AbstractHolidayCalendar` 类提供了返回假期列表所需的所有方法,并且只需要在特定的假期日历类中定义 rules。此外,``start_date` 和 end_date 类属性决定了假期生成的日期范围。这些应该在 AbstractHolidayCalendar 类上被覆盖,以便范围适用于所有日历子类。``USFederalHolidayCalendar` 是唯一存在的日历,并且主要用作开发其他日历的示例。
对于发生在固定日期的假期(例如,美国的阵亡将士纪念日或7月4日),如果该假期落在周末或非观察日,则会有一个观察规则来确定何时观察该假期。定义的观察规则有:
规则 |
描述 |
|---|---|
nearest_workday |
将周六移至周五,将周日移至周一 |
sunday_to_monday |
将周日移至下一个周一 |
next_monday_or_tuesday |
将周六移至周一,将周日/周一移至周二 |
previous_friday |
将周六和周日移至上一个周五” |
next_monday |
将周六和周日移至下一个周一 |
以下是如何定义假期和假日日历的示例:
- 提示:
weekday=MO(2) 与 2 * Week(weekday=2) 相同
使用此日历,创建索引或进行偏移量算术将跳过周末和节假日(即,阵亡将士纪念日/7月4日)。例如,下面使用 ExampleCalendar 定义了一个自定义工作日偏移量。与任何其他偏移量一样,它可以用于创建 DatetimeIndex 或添加到 datetime 或 Timestamp 对象中。
范围由 AbstractHolidayCalendar 的 start_date 和 end_date 类属性定义。默认值如下所示。
可以通过将属性设置为 datetime/Timestamp/string 来覆盖这些日期。
每个日历类都可以通过名称使用 get_calendar 函数进行访问,该函数返回一个日历类实例。任何导入的日历类都将通过此函数自动可用。此外,HolidayCalendarFactory 提供了一个简单的接口来创建由日历组合而成的日历或添加了额外规则的日历。
重采样#
pandas 提供了简单、强大且高效的功能,可在频率转换期间执行重采样操作(例如,将秒级数据转换为 5 分钟级数据)。这在金融应用中非常普遍,但不仅限于金融应用。
resample() 是一种基于时间的groupby,然后在其每个组上执行降低方法。有关高级策略,请参阅一些 cookbook examples 。
resample() 方法可以直接从 DataFrameGroupBy 对象使用,请参阅 groupby docs 。
基础知识#
resample 函数非常灵活,允许您指定许多不同的参数来控制频率转换和重采样操作。
通过 GroupBy 可用的任何内置方法都可以作为返回对象的函数使用,包括 sum、mean、std、sem、max、min、median、first、last、ohlc:
对于降采样,可以将 closed 设置为 ‘left’ 或 ‘right’ 来指定区间的哪一端是闭合的:
label 等参数用于操作结果标签。label 指定结果是以区间开始还是结束作为标签。
警告
除 ‘ME’、’YE’、’QE’、’BME’、’BYE’、’BQE’ 和 ‘W’ 外,所有频率偏移量的默认值 label 和 closed 均为 ‘left’,而这些偏移量的默认值均为 ‘right’。
这可能会无意中导致前瞻性查找,其中将较晚时间的值拉回到较早时间,例如下面使用 BusinessDay 频率的示例:
注意,星期日的值是如何被拉回到前一个星期五的。要实现星期日的值被推迟到星期一的行为,请改用
可以将 axis 参数设置为 0 或 1,它允许您为 DataFrame 重采样指定的轴。
可以将 kind 设置为 ‘timestamp’ 或 ‘period’,以将结果索引转换为/从时间戳和时间跨度表示。默认情况下,resample 保留输入表示。
在对周期数据进行重采样时(详细信息见下文),可以将 convention 设置为 ‘start’ 或 ‘end’。它指定了如何将低频周期转换为高频周期。
升采样#
对于升采样,您可以指定一种升采样方式和 limit 参数来插值生成的间隙:
稀疏重采样#
稀疏时间序列是指与需要重采样的时间量相比,点的数量要少得多的时间序列。对稀疏序列进行朴素升采样可能会生成大量中间值。当您不想使用某种方法来填充这些值时(例如,fill_method 为 None),中间值将被填充为 NaN。
由于 resample 是一个基于时间的分组,以下是如何仅对所有值都不是 NaN 的组进行高效重采样的.
如果我们想重采样到序列的完整范围:
我们可以如下只重采样那些有数据点的组:
聚合#
resample() 方法返回一个 pandas.api.typing.Resampler 实例。 类似于 aggregating API 、groupby API 和 window API ,Resampler 可以被选择性地重采样。
重采样 DataFrame 时,默认会对所有列应用相同的函数。
我们可以使用标准的 getitem 来选择一个或多个特定列。
你可以传递一个函数列表或字典进行聚合,输出一个 DataFrame:
在重采样过的 DataFrame 上,你可以传递一个函数列表应用于每一列,这会产生一个具有分层索引的聚合结果:
通过将字典传递给 aggregate,您可以将不同的聚合应用于 DataFrame 的列:
函数名也可以是字符串。 为了使字符串有效,它必须在重采样对象上实现:
此外,您还可以分别为每列指定多个聚合函数。
如果 DataFrame 没有 datetimelike 索引,而是想根据帧中的 datetimelike 列进行重采样,则可以将该列传递给 on 关键字。
同样,如果您想通过 MultiIndex 的 datetimelike 级别进行重采样,可以将它的名称或位置传递给 level 关键字。
迭代分组#
获得 Resampler 对象后,遍历分组数据非常自然,并且其功能类似于 itertools.groupby()
有关更多信息,请参阅 迭代分组 或 Resampler.__iter__ 。
使用 origin 或 offset 来调整分箱的起始点#
分组的分箱基于时间序列起点的日期开始调整。 这对于频率是日倍数(如 30D)或能平均整除一天(如 90s 或 1min)的频率效果很好。 这可能会与不满足此标准的某些频率产生不一致。 要更改此行为,可以通过指定一个固定的 Timestamp 来实现 origin 参数。
例如:
这里我们可以看到,当使用 origin 及其默认值('start_day')时,结果在 '2000-10-02 00:00:00' 之后会因时间序列的起始点不同而有所差异:
这里我们可以看到,当设置 origin 为 'epoch' 时,结果在 '2000-10-02 00:00:00' 之后对于不同的时间序列起始点是相同的:
如果需要,您可以使用自定义的时间戳作为 origin:
如果需要,您还可以仅使用一个 offset Timedelta 来调整分箱,该 Timedelta 将加到默认的 origin 上。 对于这个时间序列,这两个例子是等效的:
注意最后一个例子中 origin 使用了 'start'。 在那种情况下,origin 将被设置为时间序列的第一个值。
后向重采样#
在 1.3.0 版本加入.
有时,我们不需要调整分箱的开始点,而是需要固定分箱的结束点,以便以给定的 freq 进行后向重采样。 后向重采样默认将 closed 设置为 'right',因为最后一个值应该被视为最后一个分箱的边界点。
我们可以将 origin 设置为 'end'。 对于特定的 Timestamp 索引,其值代表从当前 Timestamp 减去 freq 到当前 Timestamp 的重采样结果,并且右边界是闭合的。
此外,与 'start_day' 选项相反,还支持 end_day。 这会将 origin 设置为最大 Timestamp 的上半夜午夜。
上面的结果使用了 2000-10-02 00:29:00 作为最后一个分箱的右边界,这是基于以下计算得出的。
时间跨度表示#
pandas 中使用 Period 对象表示规则的时间间隔,而 Period 对象序列则收集在 PeriodIndex 中,可以通过方便的 period_range 函数创建。
Period#
Period 代表一个时间跨度(例如,一天、一个月、一个季度等)。您可以使用频率别名通过 freq 关键字指定跨度,如下所示。 因为 freq 代表 Period 的跨度,所以它不能是负数,如 “-3D”。
对 Period 进行加减运算会根据其自身的频率来移动周期。不允许对具有不同 freq (跨度) 的 Period 进行算术运算。
如果 Period 的频率是每日或更高级别(D, h, min, s, ms, us, 和 ns),当结果的频率可以保持不变时,可以添加 offsets 和类似 timedelta 的对象。否则,将引发 ValueError。
如果 Period 具有其他频率,则只能添加相同的 offsets。否则,将引发 ValueError。
计算具有相同频率的 Period 实例的差值将返回它们之间频率单位的数量:
PeriodIndex 和 period_range#
Period 对象的规则序列可以收集在 PeriodIndex 中,该对象可以使用 period_range 便利函数构造:
也可以直接使用 PeriodIndex 构造函数:
传递乘以频率会输出一个跨度翻倍的 Period 序列。
如果 start 或 end 是 Period 对象,它们将作为锚定终点,用于具有与 PeriodIndex 构造函数匹配的频率的 PeriodIndex。
就像 DatetimeIndex 一样,PeriodIndex 也可以用来索引 pandas 对象:
PeriodIndex 支持与 Period 相同的加减规则。
PeriodIndex 具有自己的名为 period 的 dtype,请参阅 Period Dtypes 。
Period dtypes#
PeriodIndex 具有自定义的 period dtype。这是一种 pandas 扩展 dtype,类似于 timezone aware dtype (datetime64[ns, tz])。
period dtype 包含 freq 属性,并表示为 period[freq],例如 period[D] 或 period[M],使用 frequency strings 。
period dtype 可用于 .astype(...)。它允许你更改 PeriodIndex 的 freq,类似于 .asfreq(),并像 to_period() 一样将 DatetimeIndex 转换为 PeriodIndex:
PeriodIndex 部分字符串索引#
PeriodIndex 现在支持与非单调索引的部分字符串切片。
您可以与 DatetimeIndex 类似地将日期和字符串传递给具有 PeriodIndex 的 Series 和 DataFrame。有关详细信息,请参阅 DatetimeIndex Partial String Indexing 。
传递一个表示低于 PeriodIndex 的频率的字符串将返回部分切片数据。
与 DatetimeIndex 一样,结果将包含端点。下面的示例切片数据从 10:00 到 11:59。
PeriodIndex 的频率转换和重采样#
Period 和 PeriodIndex 的频率可以通过 asfreq 方法进行转换。让我们从 2011 财年开始,该财年结束于 12 月:
我们可以将其转换为月度频率。使用 how 参数,我们可以指定返回月份的开始还是结束:
提供了简写 ‘s’ 和 ‘e’ 以方便使用:
转换为“超周期”(例如,年度频率是季度频率的超周期)将自动返回包含输入周期的超周期:
请注意,由于我们转换为以 11 月结束的年度频率,因此 2011 年 12 月的月度周期实际上属于 2012 Y-NOV 周期。
具有锚定频率的周期转换对于处理经济、商业和其他领域常见的各种季度数据特别有用。许多组织根据其财政年度的开始和结束月份来定义季度。因此,2011 年的第一季度可能始于 2010 年或 2011 年的几个月后。通过锚定频率,pandas 支持所有季度频率 Q-JAN 到 Q-DEC。
Q-DEC 定义常规日历季度:
Q-MAR 定义以三月结束的财年:
表示之间的转换#
可以使用 to_period 将带时间戳的数据转换为 PeriodIndex 的数据,反之亦如使用 to_timestamp:
请记住,可以使用 ‘s’ 和 ‘e’ 来返回周期开始或结束时的时间戳:
在周期和时间戳之间进行转换可以实现一些方便的算术运算。在下面的示例中,我们将以 11 月结束的年度频率转换为季度结束后的次月 9 点:
表示越界的时间段#
如果您有超出 Timestamp 边界的数据,请参阅 Timestamp limitations ,那么您可以使用 PeriodIndex 和/或 Periods 的 Series 来进行计算。
从基于 int64 的 YYYYMMDD 表示形式进行转换。
这些可以轻松地转换为 PeriodIndex:
时区处理#
pandas 使用 pytz 和 dateutil 库或标准库中的 datetime.timezone 对象,为处理不同时区的时间戳提供了丰富的支持。
使用时区#
默认情况下,pandas 对象是时区无关的:
要将这些日期本地化到某个时区(为朴素日期分配一个特定的时区),您可以使用 tz_localize 方法或 date_range() 、Timestamp 或 DatetimeIndex 中的 tz 关键字参数。您可以传入 pytz 或 dateutil 时区对象,或者 Olson 时区数据库字符串。Olson 时区字符串默认将返回 pytz 时区对象。要返回 dateutil 时区对象,请在字符串前加上 dateutil/。
在
pytz中,您可以使用from pytz import common_timezones, all_timezones找到常用(和不常用)时区的列表。dateutil使用操作系统时区,因此没有固定的列表可用。对于常用时区,其名称与pytz相同。
请注意,UTC 时区在 dateutil 中是特例,应显式构造为 dateutil.tz.tzutc 的实例。您也可以先显式构造其他时区对象。
要将时区感知的 pandas 对象从一个时区转换为另一个时区,您可以使用 tz_convert 方法。
备注
当使用 pytz 时区时,DatetimeIndex 会为相同的时区输入构建一个不同于 Timestamp 的时区对象。DatetimeIndex 可以容纳 Timestamp 对象的集合,这些对象可能具有不同的 UTC 偏移,并且无法用一个 pytz 时区实例简洁地表示,而一个 Timestamp 则表示一个具有特定 UTC 偏移的时间点。
警告
请注意不同库之间的转换。对于某些时区,pytz 和 dateutil 对该时区的定义不同。这对于“标准”时区(如 US/Eastern)来说问题不大,但对于不常见的时区来说则是一个问题。
警告
请注意,不同版本时区库之间的时区定义可能不被视为相等。这可能会在使用一个版本进行本地化并用另一个版本操作已存储数据时引起问题。有关如何处理这种情况,请参阅 here 。
警告
对于 pytz 时区,直接将时区对象传递给 datetime.datetime 构造函数(例如 datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern')))是不正确的。相反,需要使用 pytz 时区对象上的 localize 方法来本地化日期时间。
警告
请注意,对于未来的日期,任何时区库都不能保证时区(和 UTC)之间的正确转换,因为相应政府机构可能会更改时区相对于 UTC 的偏移量。
警告
如果您使用的是 2038 年 1 月 18 日之后的日期,由于底层库在 2038 年问题方面的当前不足,将不会应用夏令时 (DST) 对时区感知日期的调整。一旦底层库得到修复,DST 转换将得到应用。
例如,对于两个处于英国夏令时(因此通常为 GMT+1)的日期,以下两个断言都为真:
底层来看,所有时间戳都以 UTC 存储。来自时区感知 DatetimeIndex 或 Timestamp 的值,其字段(日、小时、分钟等)将根据时区进行本地化。然而,具有相同 UTC 值的两个时间戳,即使它们处于不同的时区,仍被认为是相等的:
不同时区 Series 之间的操作将产生 UTC Series ,并根据 UTC 时间戳对数据进行对齐:
要移除时区信息,请使用 tz_localize(None) 或 tz_convert(None)。tz_localize(None) 将移除时区,产生本地时间表示。tz_convert(None) 将在转换为 UTC 时间后移除时区。
折叠#
对于模糊时间,pandas 支持显式指定 keyword-only 的 fold 参数。由于夏令时,从夏令时切换到冬令时时,一个挂钟时间可能会出现两次;fold 描述了 datetime-like 对应的是挂钟第一次(0)还是第二次(1)到达模糊时间。Fold 仅支持从 naive datetime.datetime``(有关详细信息,请参阅 `datetime documentation <https://docs.python.org/3/library/datetime.html>`__ )或 :class:`Timestamp` 构建,或从组件构建(见下文)。仅支持 ``dateutil 时区(有关处理模糊 datetimes 的 dateutil 方法,请参阅 dateutil documentation ),因为 pytz 时区不支持 fold(有关 pytz 如何处理模糊 datetimes 的详细信息,请参阅 pytz documentation )。要使用 pytz 本地化模糊 datetime,请使用 Timestamp.tz_localize() 。通常,如果您需要直接控制模糊 datetimes 的处理方式,我们建议在本地化模糊 datetimes 时依赖 Timestamp.tz_localize() 。
本地化时的模糊时间#
由于本地时区的夏令时(DST)会使某些时间在一天内出现两次(“时钟回拨”),tz_localize 可能无法确定时间戳的 UTC 偏移量。可用的选项如下:
'raise':引发 ``pytz.AmbiguousTimeError``(默认行为)'infer':尝试根据时间戳的单调性确定正确的偏移量'NaT':将模糊时间替换为NaTbool:True表示 DST 时间,False表示非 DST 时间。支持布尔值的类数组形式,用于表示时间序列。
这将失败,因为存在模糊时间('11/06/2011 01:00')
通过指定以下选项来处理这些模糊时间。
本地化时的不存在时间#
DST 转换还可能将本地时间提前 1 小时,从而产生不存在的本地时间(“时钟前跳”)。本地化不存在时间的时序的行为可以通过 nonexistent 参数进行控制。可用的选项如下:
'raise':引发 ``pytz.NonExistentTimeError``(默认行为)'NaT':将不存在的时间替换为NaT'shift_forward':将不存在的时间向前推移到最接近的实际时间'shift_backward':将不存在的时间向后推移到最接近的实际时间timedelta 对象:将不存在的时间按 timedelta 持续时间推移
默认情况下,本地化不存在的时间将引发错误。
将不存在的时间转换为 NaT 或推移时间。
时区 Series 操作#
具有**naive** 时区值的 Series 表示为 datetime64[ns] dtype。
具有**aware** 时区值的 Series 表示为 datetime64[ns, tz] dtype,其中 tz 是时区。
这两个 Series 的时区信息都可以通过 .dt 访问器进行操作,请参阅 the dt accessor section 。
例如,本地化并将 naive 时间戳转换为时区 aware。
还可以使用 astype 方法来操作时区信息。此方法可以在不同的时区 aware dtypes 之间进行转换。
备注
对 Series 使用 Series.to_numpy() 会返回数据的 NumPy 数组。NumPy 目前不支持时区(尽管它*正在*以本地时区打印!),因此对于时区 aware 数据,将返回 Timestamps 的对象数组:
通过转换为对象数组 Timestamps,可以保留时区信息。例如,在转换回 Series 时:
但是,如果您想要一个实际的 NumPy datetime64[ns] 数组(并将值转换为 UTC),而不是对象数组,可以指定 dtype 参数: