昨天快下班时的需求,对时间进行偏移计算。找了一篇介绍Pandas时间序列的文章,大致翻译一下(把英文粘贴到谷歌翻译,再把中文拷贝过来)。
原文:https://jakevdp.github.io/PythonDataScienceHandbook/03.11-working-with-time-series.html
原文的版权声明:
This is an excerpt from the Python Data Science Handbook by Jake VanderPlas; Jupyter notebooks are available on GitHub.
The text is released under the CC-BY-NC-ND license, and code is released under the MIT license. If you find this content useful, please consider supporting the work by buying the book!
需求的解决方案:https://stackoverflow.com/questions/46399204/add-pandas-timedelta-to-a-datetime64-series-or-column-in-dataframe
delta = pd.Timedelta(hours=5, minutes=30)
df['ts1'] = df['ts'] - delta
df['ts2'] = df['ts'] + delta
以下为文章翻译:
Pandas是在金融行业的背景下开发的,正如您所料,它包含一组相当广泛的工具,用于处理日期,时间和时间索引数据。 日期和时间数据有几种,我们将在这里讨论:
在本节中,我们将介绍如何在Pandas中使用这些类型的日期/时间数据。 这篇短文不是Python或Pandas中可用的时间序列工具的完整指南,而是作为用户如何处理时间序列的广泛概述。 我们将首先简要讨论在Python中处理日期和时间的工具,然后再更具体地讨论Pandas提供的工具。 在列出一些更深入的资源之后,我们将回顾一些在Pandas中处理时间序列数据的简短示例。
Python中的日期和时间
Python世界有许多可用的日期,时间,增量和时间表的表示形式。 虽然Pandas提供的时间序列工具往往对数据科学应用程序最有用,但查看它们与Python中使用的其他包的关系会很有帮助。
原生Python日期和时间:datetime和dateutil
Python处理日期和时间的基本对象驻留在内置的datetime模块中。 与第三方dateutil模块一起,您可以使用它在日期和时间快速执行许多有用的功能。 例如,您可以使用datetime类型手动构建日期:
from datetime import datetime
datetime(year=2015, month=7, day=4)
输出:datetime.datetime(2015, 7, 4, 0, 0)
或者,使用dateutil模块,您可以从各种字符串格式解析日期:
from dateutil import parser
date = parser.parse("4th of July, 2015")
date
输出:datetime.datetime(2015, 7, 4, 0, 0)
拿到datetime对象后,您可以执行打印星期几等操作:
date.strftime('%A')
输出:'Saturday'
在上述代码中,我们使用了一个标准字符串格式代码('%A')来打印日期,此类代码您可以在Python的datetime文档(datetime documentation)的strftime部分找到详细信息(strftime section )。其他有用的日期实用程序的文档可以在dateutil的在线文档中找到(dateutil's online documentation)。 需要注意的一个相关包是pytz
, 时间序列中让人头疼的时区,可以用这个包处理。
datetime和dateutil的强大之处,在于它们的灵活性和简单的语法:您可以使用这些对象及其内置方法轻松执行您可能感兴趣的几乎任何操作。不过寸有所短,它们不擅长处理大型数组中的日期和时间:正如Python数值变量的list,不如NumPy样式的数字数组(numerical arrays)更适合处理大量数据,Python日期时间对象的list,也不如与编码日期的类型数组更适合处理大量的日期时间数据。
时间类型数组:NumPy的datetime64
Python日期时间格式的弱点,让NumPy团队向NumPy添加一组原生的时间序列数据类型。 datetime64 dtype将日期编码为64位整数,因此可以非常紧凑地表示日期数组。 datetime64需要非常特定的输入格式:
import numpy as np
date = np.array('2015-07-04', dtype=np.datetime64)
date
输出:array(datetime.date(2015, 7, 4), dtype='datetime64[D]')
但是,一旦我们格式化了这个日期,我们就可以快速对它进行矢量化操作:
date + np.arange(12)
输出:
array(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
'2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
'2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'], dtype='datetime64[D]')
由于NumPy datetime64数组中的统一类型,这种类型的操作可以比我们直接使用Python的datetime对象更快地完成,特别是当数组变大时(在这篇文章中Computation on NumPy Arrays: Universal Functions,我们介绍了这种类型的向量化)
datetime64和timedelta64对象的一个细节是它们构建在基本时间单元上。 由于datetime64对象限制为64位精度,因此可编码时间的范围是此基本单位的$ 2 ^ {64} $倍。 换句话说,datetime64在时间分辨率和最大时间跨度之间进行权衡。
例如,如果您想要一个纳秒的时间分辨率,您只有足够的信息来编码$ 2 ^ {64} $纳秒或不到600年的范围。 NumPy将从输入中推断出所需的单位; 例如,这是基于日期的datetime:
np.datetime64('2015-07-04')
输出:numpy.datetime64('2015-07-04')
这是一个基于分钟的datetime:
np.datetime64('2015-07-04 12:00')
输出:numpy.datetime64('2015-07-04T12:00')
请注意,时区会自动设置为执行代码的计算机上的本地时间。 您可以使用多种格式代码之一强制任何所需的基本单位; 例如,在这里我们将强制基于纳秒的时间:
np.datetime64('2015-07-04 12:59:59.50', 'ns')
输出:numpy.datetime64('2015-07-04T12:59:59.500000000')
下表是从NumPy datetime64 documentation中提取的,列出了可用的格式代码以及它们可以编码的相对和绝对时间跨度:
对于我们在现实世界中看到的数据类型,一个有用的默认值是datetime64 [ns],因为它可以编码一个有用的现代日期范围,具有适当的精确度。
最后,我们将注意到,虽然datetime64数据类型解决了内置Python日期时间类型的一些缺陷,但它缺少datetime提供的许多方便的方法和功能,尤其是dateutil。 更多信息可以在NumPy的datetime64文档中找到。
Pandas的日期和时间:两全其美
Pandas构建了刚才讨论的所有工具,以提供Timestamp对象,它将datetime和dateutil的易用性与numpy.datetime64的高效存储和矢量化接口相结合。 从一组这些Timestamp对象中,Pandas可以构造一个DatetimeIndex,可用于索引Series或DataFrame中的数据; 我们将在下面看到许多这样的例子。
例如,我们可以使用Pandas工具重复上面的演示。 我们可以解析灵活格式化的字符串日期,并使用格式代码输出星期几:
import pandas as pd
date = pd.to_datetime("4th of July, 2015")
date
输出:Timestamp('2015-07-04 00:00:00')
date.strftime('%A')
输出:'Saturday'
另外,我们可以直接在同一个对象上进行NumPy风格的矢量化操作:
date + pd.to_timedelta(np.arange(12), 'D')
输出:
DatetimeIndex(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
'2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
'2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'],
dtype='datetime64[ns]', freq=None)
在下一节中,我们将仔细研究使用Pandas提供的工具处理时间序列数据。
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
现在我们已经将这些数据放在一个系列中,我们可以使用前面章节中讨论的任何系列索引模式,将可以强制转换的值传递给日期:
data['2014-07-04':'2015-07-04']
输出:
2014-07-04 0
2014-08-04 1
2015-07-04 2
dtype: int64
还有其他特殊的仅限日期的索引操作,例如通过一年来获取该年度所有数据的切片:
data['2015']
输出:
2015-07-04 2
2015-08-04 3
dtype: int64
稍后,我们将看到日期作为索引的便利性的其他示例。 但首先,仔细研究可用的时间序列数据结构。
Pandas时间序列数据结构
本节将介绍用于处理时间序列数据的基本Pandas数据结构:
这些日期/时间对象中最基本的是Timestamp和DatetimeIndex对象。 虽然可以直接调用这些类对象,但更常见的是使用pd.to_datetime()函数,该函数可以解析各种格式。 将单个日期传递给pd.to_datetime()会产生一个时间戳; 默认情况下传递一系列日期会产生DatetimeIndex:
dates = pd.to_datetime([datetime(2015, 7, 3), '4th of July, 2015',
'2015-Jul-6', '07-07-2015', '20150708'])
dates
输出:
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
'2015-07-08'],
dtype='datetime64[ns]', freq=None)
可以使用to_period()函数将任何DatetimeIndex转换为PeriodIndex,并添加频率代码; 在这里我们将使用'D'来表示每日频率:
dates.to_period('D')
输出:
PeriodIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
'2015-07-08'],
dtype='int64', freq='D')
例如,当从另一个日期中减去日期时,会创建TimedeltaIndex:
dates - dates[0]
输出:
TimedeltaIndex(['0 days', '1 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)
Regular sequences: pd.date_range()
为了更方便地创建常规日期序列,Pandas为此提供了一些函数:pd.date_range()用于时间戳,pd.period_range()用于周期,pd.timedelta_range()用于时间增量。 我们已经看到Python的range()和NumPy的np.arange()将起始点,端点和可选的步长转换为序列。 同样,pd.date_range()接受开始日期,结束日期和可选频率代码,以创建常规日期序列。 默认情况下,频率为一天:
pd.date_range('2015-07-03', '2015-07-10')
输出:
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
'2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
dtype='datetime64[ns]', freq='D')
或者,可以不使用起点和终点指定日期范围,而是使用起点和周期数指定日期范围:
pd.date_range('2015-07-03', periods=8)
输出:
DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 01:00:00',
'2015-07-03 02:00:00', '2015-07-03 03:00:00',
'2015-07-03 04:00:00', '2015-07-03 05:00:00',
'2015-07-03 06:00:00', '2015-07-03 07:00:00'],
dtype='datetime64[ns]', freq='H')
要创建Period或Timedelta值的常规序列,非常相似的pd.period_range()和pd.timedelta_range()函数很有用。 以下是一些月度期间:
pd.period_range('2015-07', periods=8, freq='M')
输出:
PeriodIndex(['2015-07', '2015-08', '2015-09', '2015-10', '2015-11', '2015-12',
'2016-01', '2016-02'],
dtype='int64', freq='M')
一系列持续时间增加一小时:
pd.timedelta_range(0, periods=10, freq='H')
输出:
TimedeltaIndex(['00:00:00', '01:00:00', '02:00:00', '03:00:00', '04:00:00',
'05:00:00', '06:00:00', '07:00:00', '08:00:00', '09:00:00'],
dtype='timedelta64[ns]', freq='H')
所有这些都需要了解Pandas频率代码,我们将在下一节中进行总结。
Frequencies and Offsets
这些Pandas时间序列工具的基础是频率(frequencies)或日期偏移(offsets)的概念。 就像我们看到上面的D(日)和H(小时)代码一样,我们可以使用这些代码来指定任何所需的频率间隔。 下表总结了可用的主要代码:
每月,每季度和每年的频率都在指定时间段结束时标记。 通过在其中任何一个中添加S后缀,它们将在开头标记:
此外,您可以通过添加三个字母的月份代码作为后缀来更改用于标记任何季度或年度代码的月份:
同样,可以通过添加三个字母的工作日代码来修改每周频率的分割点:
W-SUN
, W-MON
, W-TUE
, W-WED
, etc.
除此之外,代码可以与数字组合以指定其他频率。 例如,对于2小时30分钟的频率,我们可以将小时(H)和分钟(T)代码组合如下:
pd.timedelta_range(0, periods=9, freq="2H30T")
输出:
TimedeltaIndex(['00:00:00', '02:30:00', '05:00:00', '07:30:00', '10:00:00',
'12:30:00', '15:00:00', '17:30:00', '20:00:00'],
dtype='timedelta64[ns]', freq='150T')
所有这些短代码都是指Pandas时间序列偏移的特定实例,可以在pd.tseries.offsets模块中找到。 例如,我们可以直接创建工作日偏移,如下所示:
from pandas.tseries.offsets import BDay
pd.date_range('2015-07-01', periods=5, freq=BDay())
输出:
DatetimeIndex(['2015-07-01', '2015-07-02', '2015-07-03', '2015-07-06',
'2015-07-07'],
dtype='datetime64[ns]', freq='B')
有关使用频率和偏移的更多讨论,请参阅Pandas文档的“DateOffset”部分。
暂时翻译到这里,后面的Resampling, Shifting, and Windowing一节,如果以后有时间再继续。