时间序列/日期功能#

Pandas包含处理所有域的时间序列数据的广泛功能和特性。使用数字复制 datetime64timedelta64 Dtype,Pandas整合了其他Python库中的大量功能,如 scikits.timeseries 以及创建了大量用于处理时间序列数据的新功能。

例如,Pandas支持:

解析来自各种来源和格式的时间序列信息

In [1]: import datetime

In [2]: dti = pd.to_datetime(
   ...:     ["1/1/2018", np.datetime64("2018-01-01"), datetime.datetime(2018, 1, 1)]
   ...: )
   ...: 

In [3]: dti
Out[3]: DatetimeIndex(['2018-01-01', '2018-01-01', '2018-01-01'], dtype='datetime64[ns]', freq=None)

生成固定频率的日期和时间跨度序列

In [4]: dti = pd.date_range("2018-01-01", periods=3, freq="H")

In [5]: dti
Out[5]: 
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 01:00:00',
               '2018-01-01 02:00:00'],
              dtype='datetime64[ns]', freq='H')

使用时区信息处理和转换日期时间

In [6]: dti = dti.tz_localize("UTC")

In [7]: dti
Out[7]: 
DatetimeIndex(['2018-01-01 00:00:00+00:00', '2018-01-01 01:00:00+00:00',
               '2018-01-01 02:00:00+00:00'],
              dtype='datetime64[ns, UTC]', freq='H')

In [8]: dti.tz_convert("US/Pacific")
Out[8]: 
DatetimeIndex(['2017-12-31 16:00:00-08:00', '2017-12-31 17:00:00-08:00',
               '2017-12-31 18:00:00-08:00'],
              dtype='datetime64[ns, US/Pacific]', freq='H')

对时间序列进行重采样或将其转换为特定频率

In [9]: idx = pd.date_range("2018-01-01", periods=5, freq="H")

In [10]: ts = pd.Series(range(len(idx)), index=idx)

In [11]: ts
Out[11]: 
2018-01-01 00:00:00    0
2018-01-01 01:00:00    1
2018-01-01 02:00:00    2
2018-01-01 03:00:00    3
2018-01-01 04:00:00    4
Freq: H, dtype: int64

In [12]: ts.resample("2H").mean()
Out[12]: 
2018-01-01 00:00:00    0.5
2018-01-01 02:00:00    2.5
2018-01-01 04:00:00    4.0
Freq: 2H, dtype: float64

以绝对或相对时间增量执行日期和时间运算

In [13]: friday = pd.Timestamp("2018-01-05")

In [14]: friday.day_name()
Out[14]: 'Friday'

# Add 1 day
In [15]: saturday = friday + pd.Timedelta("1 day")

In [16]: saturday.day_name()
Out[16]: 'Saturday'

# Add 1 business day (Friday --> Monday)
In [17]: monday = friday + pd.offsets.BDay()

In [18]: monday.day_name()
Out[18]: 'Monday'

Pandas提供了一套相对紧凑和独立的工具来执行上述任务和更多任务。

概述#

Pandas捕捉到了4个与时间相关的概念:

  1. 日期和时间:支持时区的特定日期和时间。类似于 datetime.datetime 来自标准库的。

  2. 时间增量:绝对持续时间。类似于 datetime.timedelta 来自标准库的。

  3. 时间跨度:由时间点及其相关频率定义的时间跨度。

  4. 日期偏移量:尊重日历算法的相对持续时间。类似于 dateutil.relativedelta.relativedeltadateutil 包裹。

概念

标量类

数组类

Pandas数据类型

初级创作方法

日期和时间

Timestamp

DatetimeIndex

datetime64[ns] or datetime64[ns, tz]

to_datetime or date_range

时间增量

Timedelta

TimedeltaIndex

timedelta64[ns]

to_timedelta or timedelta_range

时间跨度

Period

PeriodIndex

period[freq]

Period or period_range

日期偏移

DateOffset

None

None

DateOffset

对于时间序列数据,传统上用指数表示时间分量 SeriesDataFrame 因此,可以相对于时间元素执行操作。

In [19]: pd.Series(range(3), index=pd.date_range("2000", freq="D", periods=3))
Out[19]: 
2000-01-01    0
2000-01-02    1
2000-01-03    2
Freq: D, dtype: int64

然而, SeriesDataFrame 还可以直接支持时间分量作为数据本身。

In [20]: pd.Series(pd.date_range("2000", freq="D", periods=3))
Out[20]: 
0   2000-01-01
1   2000-01-02
2   2000-01-03
dtype: datetime64[ns]

SeriesDataFrame 具有扩展的数据类型支持和功能,用于 datetimetimedeltaPeriod 数据传递到这些构造函数时。 DateOffset 但是,数据将存储为 object 数据。

In [21]: pd.Series(pd.period_range("1/1/2011", freq="M", periods=3))
Out[21]: 
0    2011-01
1    2011-02
2    2011-03
dtype: period[M]

In [22]: pd.Series([pd.DateOffset(1), pd.DateOffset(2)])
Out[22]: 
0         <DateOffset>
1    <2 * DateOffsets>
dtype: object

In [23]: pd.Series(pd.date_range("1/1/2011", freq="M", periods=3))
Out[23]: 
0   2011-01-31
1   2011-02-28
2   2011-03-31
dtype: datetime64[ns]

最后,Pandas将空日期时间、时间增量和时间跨度表示为 NaT 它对于表示缺少或空的日期类型值很有用,其行为类似于 np.nan 对浮点数据执行此操作。

In [24]: pd.Timestamp(pd.NaT)
Out[24]: NaT

In [25]: pd.Timedelta(pd.NaT)
Out[25]: NaT

In [26]: pd.Period(pd.NaT)
Out[26]: NaT

# Equality acts as np.nan would
In [27]: pd.NaT == pd.NaT
Out[27]: False

时间戳与时间跨度#

时间戳数据是将值与时间点相关联的最基本的时间序列数据类型。对于Pandas物体来说,这意味着使用时间点。

In [28]: pd.Timestamp(datetime.datetime(2012, 5, 1))
Out[28]: Timestamp('2012-05-01 00:00:00')

In [29]: pd.Timestamp("2012-05-01")
Out[29]: Timestamp('2012-05-01 00:00:00')

In [30]: pd.Timestamp(2012, 5, 1)
Out[30]: Timestamp('2012-05-01 00:00:00')

然而,在许多情况下,将诸如变化变量之类的东西与时间跨度相关联是更自然的。表示的跨度 Period 可以显式指定,也可以从日期时间字符串格式推断。

例如:

In [31]: pd.Period("2011-01")
Out[31]: Period('2011-01', 'M')

In [32]: pd.Period("2012-05", freq="D")
Out[32]: Period('2012-05-01', 'D')

TimestampPeriod 可以作为索引。一览表 TimestampPeriod 被自动强制到 DatetimeIndexPeriodIndex 分别为。

In [33]: dates = [
   ....:     pd.Timestamp("2012-05-01"),
   ....:     pd.Timestamp("2012-05-02"),
   ....:     pd.Timestamp("2012-05-03"),
   ....: ]
   ....: 

In [34]: ts = pd.Series(np.random.randn(3), dates)

In [35]: type(ts.index)
Out[35]: pandas.core.indexes.datetimes.DatetimeIndex

In [36]: ts.index
Out[36]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)

In [37]: ts
Out[37]: 
2012-05-01    0.469112
2012-05-02   -0.282863
2012-05-03   -1.509059
dtype: float64

In [38]: periods = [pd.Period("2012-01"), pd.Period("2012-02"), pd.Period("2012-03")]

In [39]: ts = pd.Series(np.random.randn(3), periods)

In [40]: type(ts.index)
Out[40]: pandas.core.indexes.period.PeriodIndex

In [41]: ts.index
Out[41]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='period[M]')

In [42]: ts
Out[42]: 
2012-01   -1.135632
2012-02    1.212112
2012-03   -0.173215
Freq: M, dtype: float64

Pandas允许您捕捉这两种表示并在它们之间进行转换。在引擎盖下,Pandas使用 Timestamp 和时间戳序列。 DatetimeIndex 。对于有规律的时间跨度,Pandas使用 Period 对象用于标量值和 PeriodIndex 用于跨距序列。在未来的版本中,将更好地支持具有任意开始和结束点的不规则间隔。

转换为时间戳#

要转换为 Series 或类似日期对象的列表对象,例如字符串、纪元或混合对象,您可以使用 to_datetime 功能。当传递给 Series ,这将返回一个 Series (具有相同的索引),而类似列表的转换为 DatetimeIndex

In [43]: pd.to_datetime(pd.Series(["Jul 31, 2009", "2010-01-10", None]))
Out[43]: 
0   2009-07-31
1   2010-01-10
2          NaT
dtype: datetime64[ns]

In [44]: pd.to_datetime(["2005/11/23", "2010.12.31"])
Out[44]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)

如果您使用以日期开头的日期(即欧洲风格),则可以将 dayfirst 标志:

In [45]: pd.to_datetime(["04-01-2012 10:00"], dayfirst=True)
Out[45]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None)

In [46]: pd.to_datetime(["14-01-2012", "01-14-2012"], dayfirst=True)
Out[46]: DatetimeIndex(['2012-01-14', '2012-01-14'], dtype='datetime64[ns]', freq=None)

警告

您可以在上面的例子中看到 dayfirst 并不严格。如果日期不能以日期为第一天进行解析,则它将被视为 dayfirst 是假的,并且在解析分隔的日期字符串的情况下(例如 31-12-2012 ),则也会发出警告。

如果将单个字符串传递给 to_datetime ,则它返回单个 TimestampTimestamp 也可以接受字符串输入,但不接受字符串解析选项,如 dayfirstformat ,所以使用 to_datetime 如果这些都是必需的。

In [47]: pd.to_datetime("2010/11/12")
Out[47]: Timestamp('2010-11-12 00:00:00')

In [48]: pd.Timestamp("2010/11/12")
Out[48]: Timestamp('2010-11-12 00:00:00')

您也可以使用 DatetimeIndex 直接构造函数:

In [49]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"])
Out[49]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq=None)

可以传递字符串‘INFER’,以便在创建时将索引的频率设置为推断频率:

In [50]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"], freq="infer")
Out[50]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq='2D')

提供格式参数#

除了所需的DateTime字符串之外,还需要一个 format 参数可以传递以确保特定的解析。这也可能极大地加快转换速度。

In [51]: pd.to_datetime("2010/11/12", format="%Y/%m/%d")
Out[51]: Timestamp('2010-11-12 00:00:00')

In [52]: pd.to_datetime("12-11-2010 00:00", format="%d-%m-%Y %H:%M")
Out[52]: Timestamp('2010-11-12 00:00:00')

有关可用选项的更多信息,请指定 format 选项,请参阅 datetime documentation

从多个DataFrame列组装日期时间#

您还可以传递一个 DataFrame 要组合成一个 SeriesTimestamps

In [53]: df = pd.DataFrame(
   ....:     {"year": [2015, 2016], "month": [2, 3], "day": [4, 5], "hour": [2, 3]}
   ....: )
   ....: 

In [54]: pd.to_datetime(df)
Out[54]: 
0   2015-02-04 02:00:00
1   2016-03-05 03:00:00
dtype: datetime64[ns]

您只能传递需要组装的柱。

In [55]: pd.to_datetime(df[["year", "month", "day"]])
Out[55]: 
0   2015-02-04
1   2016-03-05
dtype: datetime64[ns]

pd.to_datetime 在列名中查找DateTime组件的标准名称,包括:

  • 要求: yearmonthday

  • 可选: hourminutesecondmillisecondmicrosecondnanosecond

无效数据#

默认行为是 errors='raise' ,是指在无法解析时提出:

In [2]: pd.to_datetime(['2009/07/31', 'asd'], errors='raise')
ValueError: Unknown string format

经过 errors='ignore' 要在无法解析时返回原始输入:

In [56]: pd.to_datetime(["2009/07/31", "asd"], errors="ignore")
Out[56]: Index(['2009/07/31', 'asd'], dtype='object')

经过 errors='coerce' 将无法解析的数据转换为 NaT (不是时间):

In [57]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce")
Out[57]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None)

纪元时间戳#

Pandas支持将整型或浮点型纪元时间转换为 TimestampDatetimeIndex 。默认单位是纳秒,因为这就是 Timestamp 对象存储在内部。但是,纪元通常存储在另一个 unit 它可以被指定。方法指定的起始点开始计算 origin 参数。

In [58]: pd.to_datetime(
   ....:     [1349720105, 1349806505, 1349892905, 1349979305, 1350065705], unit="s"
   ....: )
   ....: 
Out[58]: 
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
               '2012-10-10 18:15:05', '2012-10-11 18:15:05',
               '2012-10-12 18:15:05'],
              dtype='datetime64[ns]', freq=None)

In [59]: pd.to_datetime(
   ....:     [1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500],
   ....:     unit="ms",
   ....: )
   ....: 
Out[59]: 
DatetimeIndex(['2012-10-08 18:15:05.100000', '2012-10-08 18:15:05.200000',
               '2012-10-08 18:15:05.300000', '2012-10-08 18:15:05.400000',
               '2012-10-08 18:15:05.500000'],
              dtype='datetime64[ns]', freq=None)

备注

这个 unit 参数使用的字符串与 format 已讨论的参数 above )。文档中列出了可用的单位 pandas.to_datetime()

在 1.0.0 版更改.

构建一个 TimestampDatetimeIndex 带有纪元时间戳的 tz 指定的参数将引发ValueError。如果您在另一个时区的墙上时间中有纪元,您可以将纪元读取为时区原始时间戳,然后本地化到适当的时区:

In [60]: pd.Timestamp(1262347200000000000).tz_localize("US/Pacific")
Out[60]: Timestamp('2010-01-01 12:00:00-0800', tz='US/Pacific')

In [61]: pd.DatetimeIndex([1262347200000000000]).tz_localize("US/Pacific")
Out[61]: DatetimeIndex(['2010-01-01 12:00:00-08:00'], dtype='datetime64[ns, US/Pacific]', freq=None)

备注

纪元时间将舍入到最接近的纳秒。

警告

转换浮点纪元时间可能会导致不准确和意外的结果。 Python floats 十进制数精度约为15位。从浮点型转换为高精度时的舍入 Timestamp 是不可避免的。实现精确精度的唯一方法是使用固定宽度的类型(例如int64)。

In [62]: pd.to_datetime([1490195805.433, 1490195805.433502912], unit="s")
Out[62]: DatetimeIndex(['2017-03-22 15:16:45.433000088', '2017-03-22 15:16:45.433502913'], dtype='datetime64[ns]', freq=None)

In [63]: pd.to_datetime(1490195805433502912, unit="ns")
Out[63]: Timestamp('2017-03-22 15:16:45.433502912')

从时间戳到纪元#

要从上面反转操作,即从 Timestamp 迈向Unix时代:

In [64]: stamps = pd.date_range("2012-10-08 18:15:05", periods=4, freq="D")

In [65]: stamps
Out[65]: 
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
               '2012-10-10 18:15:05', '2012-10-11 18:15:05'],
              dtype='datetime64[ns]', freq='D')

我们减去纪元(世界协调时1970年1月1日午夜),然后楼层除以“单位”(1秒)。

In [66]: (stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s")
Out[66]: Int64Index([1349720105, 1349806505, 1349892905, 1349979305], dtype='int64')

使用 origin 参数#

使用 origin 参数,则可以指定用于创建 DatetimeIndex 。例如,使用1960-01-01作为开始日期:

In [67]: pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01"))
Out[67]: DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], dtype='datetime64[ns]', freq=None)

缺省值设置为 origin='unix' ,它缺省为 1970-01-01 00:00:00 。通常称为“Unix纪元”或POSIX时间。

In [68]: pd.to_datetime([1, 2, 3], unit="D")
Out[68]: DatetimeIndex(['1970-01-02', '1970-01-03', '1970-01-04'], dtype='datetime64[ns]', freq=None)

生成时间戳的范围#

若要生成带有时间戳的索引,可以使用 DatetimeIndexIndex 构造函数并传入一组DateTime对象:

In [69]: dates = [
   ....:     datetime.datetime(2012, 5, 1),
   ....:     datetime.datetime(2012, 5, 2),
   ....:     datetime.datetime(2012, 5, 3),
   ....: ]
   ....: 

# Note the frequency information
In [70]: index = pd.DatetimeIndex(dates)

In [71]: index
Out[71]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)

# Automatically converted to DatetimeIndex
In [72]: index = pd.Index(dates)

In [73]: index
Out[73]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)

在实践中,这会变得非常麻烦,因为我们经常需要一个带有大量时间戳的非常长的索引。如果我们需要常规频率上的时间戳,我们可以使用 date_range()bdate_range() 函数来创建 DatetimeIndex 。的默认频率 date_range 是一种 日历日 而默认情况下 bdate_range 是一种 工作日

In [74]: start = datetime.datetime(2011, 1, 1)

In [75]: end = datetime.datetime(2012, 1, 1)

In [76]: index = pd.date_range(start, end)

In [77]: index
Out[77]: 
DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04',
               '2011-01-05', '2011-01-06', '2011-01-07', '2011-01-08',
               '2011-01-09', '2011-01-10',
               ...
               '2011-12-23', '2011-12-24', '2011-12-25', '2011-12-26',
               '2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30',
               '2011-12-31', '2012-01-01'],
              dtype='datetime64[ns]', length=366, freq='D')

In [78]: index = pd.bdate_range(start, end)

In [79]: index
Out[79]: 
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
               '2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
               '2011-01-13', '2011-01-14',
               ...
               '2011-12-19', '2011-12-20', '2011-12-21', '2011-12-22',
               '2011-12-23', '2011-12-26', '2011-12-27', '2011-12-28',
               '2011-12-29', '2011-12-30'],
              dtype='datetime64[ns]', length=260, freq='B')

便利功能,如 date_rangebdate_range 可以利用各种类型的 frequency aliases

In [80]: pd.date_range(start, periods=1000, freq="M")
Out[80]: 
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-30',
               '2011-05-31', '2011-06-30', '2011-07-31', '2011-08-31',
               '2011-09-30', '2011-10-31',
               ...
               '2093-07-31', '2093-08-31', '2093-09-30', '2093-10-31',
               '2093-11-30', '2093-12-31', '2094-01-31', '2094-02-28',
               '2094-03-31', '2094-04-30'],
              dtype='datetime64[ns]', length=1000, freq='M')

In [81]: pd.bdate_range(start, periods=250, freq="BQS")
Out[81]: 
DatetimeIndex(['2011-01-03', '2011-04-01', '2011-07-01', '2011-10-03',
               '2012-01-02', '2012-04-02', '2012-07-02', '2012-10-01',
               '2013-01-01', '2013-04-01',
               ...
               '2071-01-01', '2071-04-01', '2071-07-01', '2071-10-01',
               '2072-01-01', '2072-04-01', '2072-07-01', '2072-10-03',
               '2073-01-02', '2073-04-03'],
              dtype='datetime64[ns]', length=250, freq='BQS-JAN')

date_rangebdate_range 使用各种参数组合轻松生成一系列日期,例如 startendperiods ,以及 freq 。开始日期和结束日期严格包含在一起,因此不会生成指定日期以外的日期:

In [82]: pd.date_range(start, end, freq="BM")
Out[82]: 
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
               '2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
               '2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
              dtype='datetime64[ns]', freq='BM')

In [83]: pd.date_range(start, end, freq="W")
Out[83]: 
DatetimeIndex(['2011-01-02', '2011-01-09', '2011-01-16', '2011-01-23',
               '2011-01-30', '2011-02-06', '2011-02-13', '2011-02-20',
               '2011-02-27', '2011-03-06', '2011-03-13', '2011-03-20',
               '2011-03-27', '2011-04-03', '2011-04-10', '2011-04-17',
               '2011-04-24', '2011-05-01', '2011-05-08', '2011-05-15',
               '2011-05-22', '2011-05-29', '2011-06-05', '2011-06-12',
               '2011-06-19', '2011-06-26', '2011-07-03', '2011-07-10',
               '2011-07-17', '2011-07-24', '2011-07-31', '2011-08-07',
               '2011-08-14', '2011-08-21', '2011-08-28', '2011-09-04',
               '2011-09-11', '2011-09-18', '2011-09-25', '2011-10-02',
               '2011-10-09', '2011-10-16', '2011-10-23', '2011-10-30',
               '2011-11-06', '2011-11-13', '2011-11-20', '2011-11-27',
               '2011-12-04', '2011-12-11', '2011-12-18', '2011-12-25',
               '2012-01-01'],
              dtype='datetime64[ns]', freq='W-SUN')

In [84]: pd.bdate_range(end=end, periods=20)
Out[84]: 
DatetimeIndex(['2011-12-05', '2011-12-06', '2011-12-07', '2011-12-08',
               '2011-12-09', '2011-12-12', '2011-12-13', '2011-12-14',
               '2011-12-15', '2011-12-16', '2011-12-19', '2011-12-20',
               '2011-12-21', '2011-12-22', '2011-12-23', '2011-12-26',
               '2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30'],
              dtype='datetime64[ns]', freq='B')

In [85]: pd.bdate_range(start=start, periods=20)
Out[85]: 
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
               '2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
               '2011-01-13', '2011-01-14', '2011-01-17', '2011-01-18',
               '2011-01-19', '2011-01-20', '2011-01-21', '2011-01-24',
               '2011-01-25', '2011-01-26', '2011-01-27', '2011-01-28'],
              dtype='datetime64[ns]', freq='B')

指定 startend ,以及 periods 将生成一系列均匀分布的日期 startend 包括,与 periods 结果中的元素数 DatetimeIndex

In [86]: pd.date_range("2018-01-01", "2018-01-05", periods=5)
Out[86]: 
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
               '2018-01-05'],
              dtype='datetime64[ns]', freq=None)

In [87]: pd.date_range("2018-01-01", "2018-01-05", periods=10)
Out[87]: 
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 10:40:00',
               '2018-01-01 21:20:00', '2018-01-02 08:00:00',
               '2018-01-02 18:40:00', '2018-01-03 05:20:00',
               '2018-01-03 16:00:00', '2018-01-04 02:40:00',
               '2018-01-04 13:20:00', '2018-01-05 00:00:00'],
              dtype='datetime64[ns]', freq=None)

自定义频率范围#

bdate_range 属性生成一系列自定义频率日期。 weekmaskholidays 参数。只有在传递自定义频率字符串时才会使用这些参数。

In [88]: weekmask = "Mon Wed Fri"

In [89]: holidays = [datetime.datetime(2011, 1, 5), datetime.datetime(2011, 3, 14)]

In [90]: pd.bdate_range(start, end, freq="C", weekmask=weekmask, holidays=holidays)
Out[90]: 
DatetimeIndex(['2011-01-03', '2011-01-07', '2011-01-10', '2011-01-12',
               '2011-01-14', '2011-01-17', '2011-01-19', '2011-01-21',
               '2011-01-24', '2011-01-26',
               ...
               '2011-12-09', '2011-12-12', '2011-12-14', '2011-12-16',
               '2011-12-19', '2011-12-21', '2011-12-23', '2011-12-26',
               '2011-12-28', '2011-12-30'],
              dtype='datetime64[ns]', length=154, freq='C')

In [91]: pd.bdate_range(start, end, freq="CBMS", weekmask=weekmask)
Out[91]: 
DatetimeIndex(['2011-01-03', '2011-02-02', '2011-03-02', '2011-04-01',
               '2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
               '2011-09-02', '2011-10-03', '2011-11-02', '2011-12-02'],
              dtype='datetime64[ns]', freq='CBMS')

时间戳限制#

由于Pandas以纳秒的分辨率表示时间戳,因此可以使用64位整数表示的时间跨度被限制在大约584年:

In [92]: pd.Timestamp.min
Out[92]: Timestamp('1677-09-21 00:12:43.145224193')

In [93]: pd.Timestamp.max
Out[93]: Timestamp('2262-04-11 23:47:16.854775807')

标引#

的主要用途之一 DatetimeIndex 是作为Pandas对象的索引。这个 DatetimeIndex 类包含许多与时间序列相关的优化:

  • 各种偏移量的大范围日期都是预先计算的,并在幕后缓存,以便非常快速地生成后续日期范围(只需抓取一部分)。

  • 快速转换使用 shift 方法对Pandas对象执行操作。

  • 重叠的统一 DatetimeIndex 具有相同频率的对象非常快(对于快速数据对齐很重要)。

  • 通过以下属性快速访问日期字段 yearmonth 等。

  • 正则化函数,如 snap 而且非常快 asof 逻辑学。

DatetimeIndex 对象具有常规对象的所有基本功能 Index 对象,以及便于频率处理的高级时间序列特定方法的大杂烩。

备注

虽然Pandas不会强制您具有已排序的日期索引,但如果日期未排序,其中一些方法可能会有意外或不正确的行为。

DatetimeIndex 可以像常规索引一样使用,并提供其所有智能功能,如选择、切片等。

In [94]: rng = pd.date_range(start, end, freq="BM")

In [95]: ts = pd.Series(np.random.randn(len(rng)), index=rng)

In [96]: ts.index
Out[96]: 
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
               '2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
               '2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
              dtype='datetime64[ns]', freq='BM')

In [97]: ts[:5].index
Out[97]: 
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
               '2011-05-31'],
              dtype='datetime64[ns]', freq='BM')

In [98]: ts[::2].index
Out[98]: 
DatetimeIndex(['2011-01-31', '2011-03-31', '2011-05-31', '2011-07-29',
               '2011-09-30', '2011-11-30'],
              dtype='datetime64[ns]', freq='2BM')

部分字符串索引#

可以将解析为时间戳的日期和字符串作为索引参数传递:

In [99]: ts["1/31/2011"]
Out[99]: 0.11920871129693428

In [100]: ts[datetime.datetime(2011, 12, 25):]
Out[100]: 
2011-12-30    0.56702
Freq: BM, dtype: float64

In [101]: ts["10/31/2011":"12/31/2011"]
Out[101]: 
2011-10-31    0.271860
2011-11-30   -0.424972
2011-12-30    0.567020
Freq: BM, dtype: float64

为了方便访问更长的时间序列,您还可以将年或年月作为字符串传入:

In [102]: ts["2011"]
Out[102]: 
2011-01-31    0.119209
2011-02-28   -1.044236
2011-03-31   -0.861849
2011-04-29   -2.104569
2011-05-31   -0.494929
2011-06-30    1.071804
2011-07-29    0.721555
2011-08-31   -0.706771
2011-09-30   -1.039575
2011-10-31    0.271860
2011-11-30   -0.424972
2011-12-30    0.567020
Freq: BM, dtype: float64

In [103]: ts["2011-6"]
Out[103]: 
2011-06-30    1.071804
Freq: BM, dtype: float64

这种类型的切片将适用于 DataFrame 使用一个 DatetimeIndex 也是。由于部分字符串选择是一种标签切片形式,因此端点 将会是 包括在内。这将包括包含日期的匹配时间:

警告

标引 DataFrame 带有 单人 带getitem的字符串(例如 frame[dtstring] 从Pandas 1.2.0开始不推荐使用)(考虑到它是索引行还是选择列的模棱两可),并将在将来的版本中删除。等同于 .loc (例如 frame.loc[dtstring] )仍然受支持。

In [104]: dft = pd.DataFrame(
   .....:     np.random.randn(100000, 1),
   .....:     columns=["A"],
   .....:     index=pd.date_range("20130101", periods=100000, freq="T"),
   .....: )
   .....: 

In [105]: dft
Out[105]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043

[100000 rows x 1 columns]

In [106]: dft.loc["2013"]
Out[106]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043

[100000 rows x 1 columns]

这从该月的第一个时间开始,包括该月的最后日期和时间:

In [107]: dft["2013-1":"2013-2"]
Out[107]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-02-28 23:55:00  0.850929
2013-02-28 23:56:00  0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517

[84960 rows x 1 columns]

这指定了停止时间 这包括最后一天的所有时间。

In [108]: dft["2013-1":"2013-2-28"]
Out[108]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-02-28 23:55:00  0.850929
2013-02-28 23:56:00  0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517

[84960 rows x 1 columns]

这指定了一个 精确 停止时间(与上述不同):

In [109]: dft["2013-1":"2013-2-28 00:00:00"]
Out[109]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-02-27 23:56:00  1.197749
2013-02-27 23:57:00  0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501

[83521 rows x 1 columns]

我们在包含的端点上停止,因为它是索引的一部分:

In [110]: dft["2013-1-15":"2013-1-15 12:30:00"]
Out[110]: 
                            A
2013-01-15 00:00:00 -0.984810
2013-01-15 00:01:00  0.941451
2013-01-15 00:02:00  1.559365
2013-01-15 00:03:00  1.034374
2013-01-15 00:04:00 -1.480656
...                       ...
2013-01-15 12:26:00  0.371454
2013-01-15 12:27:00 -0.930806
2013-01-15 12:28:00 -0.069177
2013-01-15 12:29:00  0.066510
2013-01-15 12:30:00 -0.003945

[751 rows x 1 columns]

DatetimeIndex 部分字符串索引也适用于 DataFrame 使用一个 MultiIndex

In [111]: dft2 = pd.DataFrame(
   .....:     np.random.randn(20, 1),
   .....:     columns=["A"],
   .....:     index=pd.MultiIndex.from_product(
   .....:         [pd.date_range("20130101", periods=10, freq="12H"), ["a", "b"]]
   .....:     ),
   .....: )
   .....: 

In [112]: dft2
Out[112]: 
                              A
2013-01-01 00:00:00 a -0.298694
                    b  0.823553
2013-01-01 12:00:00 a  0.943285
                    b -1.479399
2013-01-02 00:00:00 a -1.643342
...                         ...
2013-01-04 12:00:00 b  0.069036
2013-01-05 00:00:00 a  0.122297
                    b  1.422060
2013-01-05 12:00:00 a  0.370079
                    b  1.016331

[20 rows x 1 columns]

In [113]: dft2.loc["2013-01-05"]
Out[113]: 
                              A
2013-01-05 00:00:00 a  0.122297
                    b  1.422060
2013-01-05 12:00:00 a  0.370079
                    b  1.016331

In [114]: idx = pd.IndexSlice

In [115]: dft2 = dft2.swaplevel(0, 1).sort_index()

In [116]: dft2.loc[idx[:, "2013-01-05"], :]
Out[116]: 
                              A
a 2013-01-05 00:00:00  0.122297
  2013-01-05 12:00:00  0.370079
b 2013-01-05 00:00:00  1.422060
  2013-01-05 12:00:00  1.016331

0.25.0 新版功能.

使用字符串索引的切片也支持UTC偏移量。

In [117]: df = pd.DataFrame([0], index=pd.DatetimeIndex(["2019-01-01"], tz="US/Pacific"))

In [118]: df
Out[118]: 
                           0
2019-01-01 00:00:00-08:00  0

In [119]: df["2019-01-01 12:00:00+04:00":"2019-01-01 13:00:00+04:00"]
Out[119]: 
                           0
2019-01-01 00:00:00-08:00  0

切片与完全匹配#

根据索引的分辨率,用作索引参数的同一字符串可以被视为切片,也可以被视为完全匹配。如果字符串的准确性低于索引,则它将被视为切片,否则将被视为完全匹配。

考虑一个 Series 具有分钟分辨率索引的对象:

In [120]: series_minute = pd.Series(
   .....:     [1, 2, 3],
   .....:     pd.DatetimeIndex(
   .....:         ["2011-12-31 23:59:00", "2012-01-01 00:00:00", "2012-01-01 00:02:00"]
   .....:     ),
   .....: )
   .....: 

In [121]: series_minute.index.resolution
Out[121]: 'minute'

精度不到一分钟的时间戳字符串给出一个 Series 对象。

In [122]: series_minute["2011-12-31 23"]
Out[122]: 
2011-12-31 23:59:00    1
dtype: int64

具有分钟分辨率(或更准确)的时间戳字符串提供标量,即不会强制转换为切片。

In [123]: series_minute["2011-12-31 23:59"]
Out[123]: 1

In [124]: series_minute["2011-12-31 23:59:00"]
Out[124]: 1

如果索引分辨率为秒,则精确到分钟的时间戳将给出 Series

In [125]: series_second = pd.Series(
   .....:     [1, 2, 3],
   .....:     pd.DatetimeIndex(
   .....:         ["2011-12-31 23:59:59", "2012-01-01 00:00:00", "2012-01-01 00:00:01"]
   .....:     ),
   .....: )
   .....: 

In [126]: series_second.index.resolution
Out[126]: 'second'

In [127]: series_second["2011-12-31 23:59"]
Out[127]: 
2011-12-31 23:59:59    1
dtype: int64

如果时间戳字符串被视为片,则可以使用它来索引 DataFrame 使用 .loc[] 也是。

In [128]: dft_minute = pd.DataFrame(
   .....:     {"a": [1, 2, 3], "b": [4, 5, 6]}, index=series_minute.index
   .....: )
   .....: 

In [129]: dft_minute.loc["2011-12-31 23"]
Out[129]: 
                     a  b
2011-12-31 23:59:00  1  4

警告

但是,如果该字符串被视为完全匹配,则 DataFrame %s [] 将按列而不是按行显示,请参见 Indexing Basics 。例如 dft_minute['2011-12-31 23:59'] 将筹集 KeyError 作为 '2012-12-31 23:59' 与索引具有相同的分辨率,并且没有具有该名称的列:

始终 具有明确的选择,无论该行被视为切片还是单个选择,请使用 .loc

In [130]: dft_minute.loc["2011-12-31 23:59"]
Out[130]: 
a    1
b    4
Name: 2011-12-31 23:59:00, dtype: int64

另请注意, DatetimeIndex 分辨率不能低于白昼的精度。

In [131]: series_monthly = pd.Series(
   .....:     [1, 2, 3], pd.DatetimeIndex(["2011-12", "2012-01", "2012-02"])
   .....: )
   .....: 

In [132]: series_monthly.index.resolution
Out[132]: 'day'

In [133]: series_monthly["2011-12"]  # returns Series
Out[133]: 
2011-12-01    1
dtype: int64

精确标引#

如上一节中所讨论的,为 DatetimeIndex 部分字符串取决于时间段的“准确性”,换句话说,时间间隔与索引分辨率的关系有多具体。相比之下,使用 Timestampdatetime 对象是精确的,因为对象具有确切的含义。这些也遵循了 包括两个端点

这些 Timestampdatetime 对象具有精确的 hours, minutes,seconds ,即使它们没有明确指定(它们是 0 )。

In [134]: dft[datetime.datetime(2013, 1, 1): datetime.datetime(2013, 2, 28)]
Out[134]: 
                            A
2013-01-01 00:00:00  0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00  0.113648
2013-01-01 00:04:00 -1.478427
...                       ...
2013-02-27 23:56:00  1.197749
2013-02-27 23:57:00  0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501

[83521 rows x 1 columns]

没有默认设置。

In [135]: dft[
   .....:     datetime.datetime(2013, 1, 1, 10, 12, 0): datetime.datetime(
   .....:         2013, 2, 28, 10, 12, 0
   .....:     )
   .....: ]
   .....: 
Out[135]: 
                            A
2013-01-01 10:12:00  0.565375
2013-01-01 10:13:00  0.068184
2013-01-01 10:14:00  0.788871
2013-01-01 10:15:00 -0.280343
2013-01-01 10:16:00  0.931536
...                       ...
2013-02-28 10:08:00  0.148098
2013-02-28 10:09:00 -0.388138
2013-02-28 10:10:00  0.139348
2013-02-28 10:11:00  0.085288
2013-02-28 10:12:00  0.950146

[83521 rows x 1 columns]

截断与花式标引#

A truncate() 提供了类似于切片的便利功能。请注意 truncate 中任何未指定的日期组件假定值为0。 DatetimeIndex 与返回任何部分匹配的日期的切片不同:

In [136]: rng2 = pd.date_range("2011-01-01", "2012-01-01", freq="W")

In [137]: ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2)

In [138]: ts2.truncate(before="2011-11", after="2011-12")
Out[138]: 
2011-11-06    0.437823
2011-11-13   -0.293083
2011-11-20   -0.059881
2011-11-27    1.252450
Freq: W-SUN, dtype: float64

In [139]: ts2["2011-11":"2011-12"]
Out[139]: 
2011-11-06    0.437823
2011-11-13   -0.293083
2011-11-20   -0.059881
2011-11-27    1.252450
2011-12-04    0.046611
2011-12-11    0.059478
2011-12-18   -0.286539
2011-12-25    0.841669
Freq: W-SUN, dtype: float64

即使是复杂的花哨索引也打破了 DatetimeIndex 频率规律性将导致 DatetimeIndex ,虽然频率丢失:

In [140]: ts2[[0, 2, 6]].index
Out[140]: DatetimeIndex(['2011-01-02', '2011-01-16', '2011-02-13'], dtype='datetime64[ns]', freq=None)

时间/日期组件#

用户可以从以下几个时间/日期属性进行访问 Timestamp 或时间戳的集合,如 DatetimeIndex

属性

描述

日期时间的年份

月份

日期时间的月份

约会时间的日子

小时

约会时间的小时

分钟

约会时间的分钟数

第二

日期时间的秒数

微秒级

日期时间的微秒数

纳秒

日期时间的纳秒

日期

返回日期时间.date(不包含时区信息)

时间

返回日期时间.time(不包含时区信息)

时间表

返回带有时区信息的本地时间形式的DateTim.time

一年中的哪一天

一年的第几天

day_of_year

一年的第几天

一年中的星期

一年中的第几周

星期

一年中的第几周

星期几

星期一=0,星期日=6的星期几

day_of_week

星期一=0,星期日=6的星期几

工作日

星期一=0,星期日=6的星期几

日期的季度: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

1.1.0 新版功能.

您可以从ISO 8601标准获取ISO年的年、周和日组件:

In [141]: idx = pd.date_range(start="2019-12-29", freq="D", periods=4)

In [142]: idx.isocalendar()
Out[142]: 
            year  week  day
2019-12-29  2019    52    7
2019-12-30  2020     1    1
2019-12-31  2020     1    2
2020-01-01  2020     1    3

In [143]: idx.to_series().dt.isocalendar()
Out[143]: 
            year  week  day
2019-12-29  2019    52    7
2019-12-30  2020     1    1
2019-12-31  2020     1    2
2020-01-01  2020     1    3

DateOffset对象#

在前面的例子中,频率串(例如 'D' )用于指定定义以下内容的频率:

这些频率字符串映射到 DateOffset 对象及其子类。一个 DateOffset 类似于 Timedelta 表示持续时间,但遵循特定的日历持续时间规则。例如,一个 Timedelta 天数将始终递增 datetimes 到24小时,而一个 DateOffset 天数将会增加 datetimes 到第二天的同一时间,无论一天是23小时、24小时还是25小时,因为夏令时。然而,所有的 DateOffset 一个小时或更小的子类 (HourMinuteSecondMilliMicroNano )表现得像 Timedelta 尊重绝对时间。

The basic DateOffset acts similar to dateutil.relativedelta (relativedelta documentation) that shifts a date time by the corresponding calendar duration specified. The arithmetic operator (+) can be used to perform the shift.

# This particular day contains a day light savings time transition
In [144]: ts = pd.Timestamp("2016-10-30 00:00:00", tz="Europe/Helsinki")

# Respects absolute time
In [145]: ts + pd.Timedelta(days=1)
Out[145]: Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')

# Respects calendar time
In [146]: ts + pd.DateOffset(days=1)
Out[146]: Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')

In [147]: friday = pd.Timestamp("2018-01-05")

In [148]: friday.day_name()
Out[148]: 'Friday'

# Add 2 business days (Friday --> Tuesday)
In [149]: two_business_days = 2 * pd.offsets.BDay()

In [150]: friday + two_business_days
Out[150]: Timestamp('2018-01-09 00:00:00')

In [151]: (friday + two_business_days).day_name()
Out[151]: 'Tuesday'

最多 DateOffsets 具有关联的频率字符串或偏移别名,可以传入 freq 关键字参数。可以在下面找到可用的日期偏移量和相关的频率字符串:

日期偏移量

频率串

描述

DateOffset

通用偏移量类别,默认为绝对24小时

BDay or BusinessDay

'B'

工作日(工作日)

CDay or CustomBusinessDay

'C'

自定义工作日

Week

'W'

一周,可选择固定在一周中的某一天

WeekOfMonth

'WOM'

每月第y周的第x天

LastWeekOfMonth

'LWOM'

每月最后一周的第x天

MonthEnd

'M'

日历月末

MonthBegin

'MS'

历月开始

BMonthEnd or BusinessMonthEnd

'BM'

营业月末

BMonthBegin or BusinessMonthBegin

'BMS'

营业月开始

CBMonthEnd or CustomBusinessMonthEnd

'CBM'

自定义业务月末

CBMonthBegin or CustomBusinessMonthBegin

'CBMS'

自定义业务月开始

SemiMonthEnd

'SM'

15(或其他月份的某一天)和日历月份结束

SemiMonthBegin

'SMS'

15(或其他月份的某一天)和日历月份开始

QuarterEnd

'Q'

日历季度末

QuarterBegin

'QS'

日历季度开始

BQuarterEnd

'BQ

业务季度末

BQuarterBegin

'BQS'

业务季开始

FY5253Quarter

'REQ'

零售业(也就是52-53周)季度

YearEnd

'A'

日历年结束

YearBegin

'AS' or 'BYS'

历年开始

BYearEnd

'BA'

营业年度结束

BYearBegin

'BAS'

业务年度开始

FY5253

'RE'

零售年(也就是52-53周)

Easter

复活节假期

BusinessHour

'BH'

营业时间

CustomBusinessHour

'CBH'

自定义营业时间

Day

'D'

绝对的一天

Hour

'H'

一小时

Minute

'T' or 'min'

一分钟

Second

'S'

一秒钟

Milli

'L' or 'ms'

一毫秒

Micro

'U' or 'us'

1微秒

Nano

'N'

一纳秒

DateOffsets 另外还有 rollforward()rollback() 将日期分别向前或向后移动到相对于偏移量的有效偏移量日期的方法。例如,业务抵销将把在周末(星期六和星期日)到达的日期提前到星期一,因为业务抵销在工作日运行。

In [152]: ts = pd.Timestamp("2018-01-06 00:00:00")

In [153]: ts.day_name()
Out[153]: 'Saturday'

# BusinessHour's valid offset dates are Monday through Friday
In [154]: offset = pd.offsets.BusinessHour(start="09:00")

# Bring the date to the closest offset date (Monday)
In [155]: offset.rollforward(ts)
Out[155]: Timestamp('2018-01-08 09:00:00')

# Date is brought to the closest offset date first and then the hour is added
In [156]: ts + offset
Out[156]: Timestamp('2018-01-08 10:00:00')

默认情况下,这些操作保留时间(小时、分钟等)信息。要将时间重置为午夜,请使用 normalize() 在应用操作之前或之后(取决于您是否希望在操作中包含时间信息)。

In [157]: ts = pd.Timestamp("2014-01-01 09:00")

In [158]: day = pd.offsets.Day()

In [159]: day + ts
Out[159]: Timestamp('2014-01-02 09:00:00')

In [160]: (day + ts).normalize()
Out[160]: Timestamp('2014-01-02 00:00:00')

In [161]: ts = pd.Timestamp("2014-01-01 22:00")

In [162]: hour = pd.offsets.Hour()

In [163]: hour + ts
Out[163]: Timestamp('2014-01-01 23:00:00')

In [164]: (hour + ts).normalize()
Out[164]: Timestamp('2014-01-01 00:00:00')

In [165]: (hour + pd.Timestamp("2014-01-01 23:30")).normalize()
Out[165]: Timestamp('2014-01-02 00:00:00')

参数偏移#

有些偏移量可以在创建时被“参数化”,以产生不同的行为。例如, Week 用于生成每周数据的偏移量接受 weekday 参数,该参数导致生成的日期始终位于一周中的特定日期:

In [166]: d = datetime.datetime(2008, 8, 18, 9, 0)

In [167]: d
Out[167]: datetime.datetime(2008, 8, 18, 9, 0)

In [168]: d + pd.offsets.Week()
Out[168]: Timestamp('2008-08-25 09:00:00')

In [169]: d + pd.offsets.Week(weekday=4)
Out[169]: Timestamp('2008-08-22 09:00:00')

In [170]: (d + pd.offsets.Week(weekday=4)).weekday()
Out[170]: 4

In [171]: d - pd.offsets.Week()
Out[171]: Timestamp('2008-08-11 09:00:00')

这个 normalize 选项将对加法和减法有效。

In [172]: d + pd.offsets.Week(normalize=True)
Out[172]: Timestamp('2008-08-25 00:00:00')

In [173]: d - pd.offsets.Week(normalize=True)
Out[173]: Timestamp('2008-08-11 00:00:00')

另一个例子是参数化 YearEnd 以及具体的结束月份:

In [174]: d + pd.offsets.YearEnd()
Out[174]: Timestamp('2008-12-31 09:00:00')

In [175]: d + pd.offsets.YearEnd(month=6)
Out[175]: Timestamp('2009-06-30 09:00:00')

将偏移用于 Series / DatetimeIndex#

偏移量可以与 SeriesDatetimeIndex 将偏移量应用于每个元素。

In [176]: rng = pd.date_range("2012-01-01", "2012-01-03")

In [177]: s = pd.Series(rng)

In [178]: rng
Out[178]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')

In [179]: rng + pd.DateOffset(months=2)
Out[179]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq=None)

In [180]: s + pd.DateOffset(months=2)
Out[180]: 
0   2012-03-01
1   2012-03-02
2   2012-03-03
dtype: datetime64[ns]

In [181]: s - pd.DateOffset(months=2)
Out[181]: 
0   2011-11-01
1   2011-11-02
2   2011-11-03
dtype: datetime64[ns]

If the offset class maps directly to a Timedelta (Day, Hour, Minute, Second, Micro, Milli, Nano) it can be used exactly like a Timedelta - see the Timedelta section for more examples.

In [182]: s - pd.offsets.Day(2)
Out[182]: 
0   2011-12-30
1   2011-12-31
2   2012-01-01
dtype: datetime64[ns]

In [183]: td = s - pd.Series(pd.date_range("2011-12-29", "2011-12-31"))

In [184]: td
Out[184]: 
0   3 days
1   3 days
2   3 days
dtype: timedelta64[ns]

In [185]: td + pd.offsets.Minute(15)
Out[185]: 
0   3 days 00:15:00
1   3 days 00:15:00
2   3 days 00:15:00
dtype: timedelta64[ns]

请注意,某些偏移量(如 BQuarterEnd )没有矢量化的实现。它们仍然可以使用,但计算速度可能会显著变慢,并将显示 PerformanceWarning

In [186]: rng + pd.offsets.BQuarterEnd()
Out[186]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None)

自定义工作日#

这个 CDayCustomBusinessDay 类提供了一个参数 BusinessDay 类,该类可用于创建自定义工作日日历,这些日历考虑了本地假日和本地周末约定。

作为一个有趣的例子,让我们来看看埃及,那里的周末是周五到周六。

In [187]: weekmask_egypt = "Sun Mon Tue Wed Thu"

# They also observe International Workers' Day so let's
# add that for a couple of years
In [188]: holidays = [
   .....:     "2012-05-01",
   .....:     datetime.datetime(2013, 5, 1),
   .....:     np.datetime64("2014-05-01"),
   .....: ]
   .....: 

In [189]: bday_egypt = pd.offsets.CustomBusinessDay(
   .....:     holidays=holidays,
   .....:     weekmask=weekmask_egypt,
   .....: )
   .....: 

In [190]: dt = datetime.datetime(2013, 4, 30)

In [191]: dt + 2 * bday_egypt
Out[191]: Timestamp('2013-05-05 00:00:00')

让我们映射到工作日的名称:

In [192]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)

In [193]: pd.Series(dts.weekday, dts).map(pd.Series("Mon Tue Wed Thu Fri Sat Sun".split()))
Out[193]: 
2013-04-30    Tue
2013-05-02    Thu
2013-05-05    Sun
2013-05-06    Mon
2013-05-07    Tue
Freq: C, dtype: object

假日日历可以用来提供假日列表。请参阅 holiday calendar 部分了解更多信息。

In [194]: from pandas.tseries.holiday import USFederalHolidayCalendar

In [195]: bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())

# Friday before MLK Day
In [196]: dt = datetime.datetime(2014, 1, 17)

# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [197]: dt + bday_us
Out[197]: Timestamp('2014-01-21 00:00:00')

可以用通常的方式定义符合特定假日日历的每月偏移量。

In [198]: bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())

# Skip new years
In [199]: dt = datetime.datetime(2013, 12, 17)

In [200]: dt + bmth_us
Out[200]: Timestamp('2014-01-02 00:00:00')

# Define date index with custom offset
In [201]: pd.date_range(start="20100101", end="20120101", freq=bmth_us)
Out[201]: 
DatetimeIndex(['2010-01-04', '2010-02-01', '2010-03-01', '2010-04-01',
               '2010-05-03', '2010-06-01', '2010-07-01', '2010-08-02',
               '2010-09-01', '2010-10-01', '2010-11-01', '2010-12-01',
               '2011-01-03', '2011-02-01', '2011-03-01', '2011-04-01',
               '2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
               '2011-09-01', '2011-10-03', '2011-11-01', '2011-12-01'],
              dtype='datetime64[ns]', freq='CBMS')

备注

频率字符串‘C’用于指示使用了CustomBusinessDay DateOffset,重要的是要注意,由于CustomBusinessDay是一个参数化类型,CustomBusinessDay的实例可能不同,这是无法从‘C’频率字符串中检测到的。因此,用户需要确保在用户的应用程序中一致地使用‘C’频率串。

营业时间#

这个 BusinessHour 类提供营业时间表示形式。 BusinessDay ,允许使用特定的开始和结束时间。

默认情况下, BusinessHour 营业时间为9:00至17:00。添加 BusinessHour 将递增 Timestamp 按每小时频率计算。IF目标 Timestamp 不在营业时间,请移到下一个营业时间,然后递增。如果结果超过了工作时间结束时间,则剩余的工作时间将添加到下一个工作日。

In [202]: bh = pd.offsets.BusinessHour()

In [203]: bh
Out[203]: <BusinessHour: BH=09:00-17:00>

# 2014-08-01 is Friday
In [204]: pd.Timestamp("2014-08-01 10:00").weekday()
Out[204]: 4

In [205]: pd.Timestamp("2014-08-01 10:00") + bh
Out[205]: Timestamp('2014-08-01 11:00:00')

# Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh
In [206]: pd.Timestamp("2014-08-01 08:00") + bh
Out[206]: Timestamp('2014-08-01 10:00:00')

# If the results is on the end time, move to the next business day
In [207]: pd.Timestamp("2014-08-01 16:00") + bh
Out[207]: Timestamp('2014-08-04 09:00:00')

# Remainings are added to the next day
In [208]: pd.Timestamp("2014-08-01 16:30") + bh
Out[208]: Timestamp('2014-08-04 09:30:00')

# Adding 2 business hours
In [209]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(2)
Out[209]: Timestamp('2014-08-01 12:00:00')

# Subtracting 3 business hours
In [210]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(-3)
Out[210]: Timestamp('2014-07-31 15:00:00')

您还可以指定 startend 按关键字计算的时间。参数必须是 str vbl.用一种. hour:minute 代表或一个 datetime.time 举个例子。指定秒、微秒和纳秒作为营业时间将导致 ValueError

In [211]: bh = pd.offsets.BusinessHour(start="11:00", end=datetime.time(20, 0))

In [212]: bh
Out[212]: <BusinessHour: BH=11:00-20:00>

In [213]: pd.Timestamp("2014-08-01 13:00") + bh
Out[213]: Timestamp('2014-08-01 14:00:00')

In [214]: pd.Timestamp("2014-08-01 09:00") + bh
Out[214]: Timestamp('2014-08-01 12:00:00')

In [215]: pd.Timestamp("2014-08-01 18:00") + bh
Out[215]: Timestamp('2014-08-01 19:00:00')

通过 start 时间晚于 end 表示午夜营业时间。在这种情况下,营业时间超过午夜并与第二天重叠。有效营业时间以是否从有效开始区分 BusinessDay

In [216]: bh = pd.offsets.BusinessHour(start="17:00", end="09:00")

In [217]: bh
Out[217]: <BusinessHour: BH=17:00-09:00>

In [218]: pd.Timestamp("2014-08-01 17:00") + bh
Out[218]: Timestamp('2014-08-01 18:00:00')

In [219]: pd.Timestamp("2014-08-01 23:00") + bh
Out[219]: Timestamp('2014-08-02 00:00:00')

# Although 2014-08-02 is Saturday,
# it is valid because it starts from 08-01 (Friday).
In [220]: pd.Timestamp("2014-08-02 04:00") + bh
Out[220]: Timestamp('2014-08-02 05:00:00')

# Although 2014-08-04 is Monday,
# it is out of business hours because it starts from 08-03 (Sunday).
In [221]: pd.Timestamp("2014-08-04 04:00") + bh
Out[221]: Timestamp('2014-08-04 18:00:00')

施药 BusinessHour.rollforwardrollback 到非营业时间会导致下一个营业时间开始或前一天结束。与其他补偿不同的是, BusinessHour.rollforward 可能输出不同的结果 apply 顾名思义。

这是因为一天的营业时间结束等于第二天的营业时间开始。例如,在默认工作时间(9:00-17:00)下, 2014-08-01 17:002014-08-04 09:00

# This adjusts a Timestamp to business hour edge
In [222]: pd.offsets.BusinessHour().rollback(pd.Timestamp("2014-08-02 15:00"))
Out[222]: Timestamp('2014-08-01 17:00:00')

In [223]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02 15:00"))
Out[223]: Timestamp('2014-08-04 09:00:00')

# It is the same as BusinessHour() + pd.Timestamp('2014-08-01 17:00').
# And it is the same as BusinessHour() + pd.Timestamp('2014-08-04 09:00')
In [224]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02 15:00")
Out[224]: Timestamp('2014-08-04 10:00:00')

# BusinessDay results (for reference)
In [225]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02"))
Out[225]: Timestamp('2014-08-04 09:00:00')

# It is the same as BusinessDay() + pd.Timestamp('2014-08-01')
# The result is the same as rollworward because BusinessDay never overlap.
In [226]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02")
Out[226]: Timestamp('2014-08-04 10:00:00')

BusinessHour 把星期六和星期日当作节假日。要使用任意节假日,您可以使用 CustomBusinessHour 偏移量,如下小节所述。

自定义营业时间#

这个 CustomBusinessHour 是一种混合物 BusinessHourCustomBusinessDay 它允许您指定任意假期。 CustomBusinessHour 工作方式与 BusinessHour 只是它跳过了指定的自定义假日。

In [227]: from pandas.tseries.holiday import USFederalHolidayCalendar

In [228]: bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar())

# Friday before MLK Day
In [229]: dt = datetime.datetime(2014, 1, 17, 15)

In [230]: dt + bhour_us
Out[230]: Timestamp('2014-01-17 16:00:00')

# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [231]: dt + bhour_us * 2
Out[231]: Timestamp('2014-01-21 09:00:00')

您可以使用以下任一项支持的关键字参数 BusinessHourCustomBusinessDay

In [232]: bhour_mon = pd.offsets.CustomBusinessHour(start="10:00", weekmask="Tue Wed Thu Fri")

# Monday is skipped because it's a holiday, business hour starts from 10:00
In [233]: dt + bhour_mon * 2
Out[233]: Timestamp('2014-01-21 10:00:00')

偏移别名#

许多字符串别名被赋予有用的公共时间序列频率。我们将这些别名称为 偏移别名

别名

描述

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

年份开始频率

拜斯,拜斯

业务年度开始频率

BH

营业时间班次

H

每小时班次

T,最小

每分钟频率

S

二次频率

L,ms

毫秒

U,我们

微秒级

N

纳秒

备注

在使用上面的偏移别名时,应该注意的是, date_range()bdate_range() ,将仅返回位于 start_dateend_date 。如果 start_date 与频率不对应,则返回的时间戳将从下一个有效的时间戳开始,对于 end_date ,则返回的时间戳将在上一个有效的时间戳处停止。

例如,对于偏移量 MS ,如果 start_date 不是每月的第一天,则返回的时间戳将从下个月的第一天开始。如果 end_date 不是一个月的第一天,则最后返回的时间戳将是相应月份的第一天。

In [234]: dates_lst_1 = pd.date_range("2020-01-06", "2020-04-03", freq="MS")

In [235]: dates_lst_1
Out[235]: DatetimeIndex(['2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')

In [236]: dates_lst_2 = pd.date_range("2020-01-01", "2020-04-01", freq="MS")

In [237]: dates_lst_2
Out[237]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')

我们可以从上面的例子中看到 date_range()bdate_range() 事件之间的有效时间戳 start_dateend_date 。如果这些时间戳不是给定频率的有效时间戳,它将滚动到下一个值 start_date (分别为上一次 end_date )

组合别名#

正如我们前面看到的,别名和Offset实例在大多数函数中都是可替换的:

In [238]: pd.date_range(start, periods=5, freq="B")
Out[238]: 
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
               '2011-01-07'],
              dtype='datetime64[ns]', freq='B')

In [239]: pd.date_range(start, periods=5, freq=pd.offsets.BDay())
Out[239]: 
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
               '2011-01-07'],
              dtype='datetime64[ns]', freq='B')

您可以将日偏移量和日内偏移量组合在一起:

In [240]: pd.date_range(start, periods=10, freq="2h20min")
Out[240]: 
DatetimeIndex(['2011-01-01 00:00:00', '2011-01-01 02:20:00',
               '2011-01-01 04:40:00', '2011-01-01 07:00:00',
               '2011-01-01 09:20:00', '2011-01-01 11:40:00',
               '2011-01-01 14:00:00', '2011-01-01 16:20:00',
               '2011-01-01 18:40:00', '2011-01-01 21:00:00'],
              dtype='datetime64[ns]', freq='140T')

In [241]: pd.date_range(start, periods=10, freq="1D10U")
Out[241]: 
DatetimeIndex([       '2011-01-01 00:00:00', '2011-01-02 00:00:00.000010',
               '2011-01-03 00:00:00.000020', '2011-01-04 00:00:00.000030',
               '2011-01-05 00:00:00.000040', '2011-01-06 00:00:00.000050',
               '2011-01-07 00:00:00.000060', '2011-01-08 00:00:00.000070',
               '2011-01-09 00:00:00.000080', '2011-01-10 00:00:00.000090'],
              dtype='datetime64[ns]', freq='86400000010U')

定位偏移#

对于某些频率,您可以指定锚定后缀:

别名

描述

W-Sun

每周一次(星期日)。与“W”相同

W-周一

每周班次(星期一)

W-TUE

每周班次(星期二)

W-Wed

每周班次(星期三)

W-清华

每周班次(星期四)

W-星期五

每周班次(星期五)

W-SAT

每周班次(星期六)

(B)Q(S)-DEC

季度频率,一年在12月结束。与“Q”相同

(B)Q(S)-1月

季度频率,年度结束在1月份

(B)Q(S)-2月

季度频率,年度结束在2月份

(B)Q(S)-3月

季度频率,年度结束时间为3月

(B)Q(S)-APR

季度频率,年终在4月

(B)Q(S)-可

季度频率,年终在5月

(B)Q(S)-6月

季度频率,年度结束时间为6月

(B)Q(S)-七月

季度频率,年度结束在7月份

(B)Q(S)-八月

季度频率,年终在8月

(B)Q(S)-SEP

季度频率,年度结束在9月份

(B)Q(S)-十月

季度频率,年度结束在10月份

(B)Q(S)-11月

季度频率,年度结束时间为11月

(B)甲(S)-地政总署署长

年度频率,固定在12月底。与“A”相同

(乙)甲(S)-一月

年度频率,固定在1月底

(乙)甲(S)-二月

年频率,固定在2月底

(B)甲(S)-三月

年度频率,固定在3月底

(B)A(S)-APR

年度频率,固定在4月底

(B)甲(S)-可

年频率,固定在5月底

(B)甲(S)-六月

年度频率,固定在6月底

(B)甲(S)-七月

年度频率,固定在7月底

(乙)甲(S)-八月

年度频率,固定在8月底

(B)A(S)-SEP

年度频率,固定在9月底

(乙)甲(S)-十月

年度频率,固定在10月底

(B)A(S)-11月

年度频率,固定在11月底

可以将这些参数用作 date_rangebdate_range ,构造函数 DatetimeIndex ,以及大Pandas的其他各种与时间序列相关的功能。

锚定偏移语义#

对于固定到特定频率开始或结束的那些偏移量 (MonthEndMonthBeginWeekEnd 等),以下规则适用于前滚和后滚。

什么时候 n 不是0,则如果给定日期不在锚点上,则捕捉到下一个(上一个)锚点并移动 |n|-1 向前或向后的附加步骤。

In [242]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=1)
Out[242]: Timestamp('2014-02-01 00:00:00')

In [243]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=1)
Out[243]: Timestamp('2014-01-31 00:00:00')

In [244]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=1)
Out[244]: Timestamp('2014-01-01 00:00:00')

In [245]: pd.Timestamp("2014-01-02") - pd.offsets.MonthEnd(n=1)
Out[245]: Timestamp('2013-12-31 00:00:00')

In [246]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=4)
Out[246]: Timestamp('2014-05-01 00:00:00')

In [247]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=4)
Out[247]: Timestamp('2013-10-01 00:00:00')

如果给定的日期 is 在锚点上,它被移动 |n| 向前或向后的分数。

In [248]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=1)
Out[248]: Timestamp('2014-02-01 00:00:00')

In [249]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=1)
Out[249]: Timestamp('2014-02-28 00:00:00')

In [250]: pd.Timestamp("2014-01-01") - pd.offsets.MonthBegin(n=1)
Out[250]: Timestamp('2013-12-01 00:00:00')

In [251]: pd.Timestamp("2014-01-31") - pd.offsets.MonthEnd(n=1)
Out[251]: Timestamp('2013-12-31 00:00:00')

In [252]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=4)
Out[252]: Timestamp('2014-05-01 00:00:00')

In [253]: pd.Timestamp("2014-01-31") - pd.offsets.MonthBegin(n=4)
Out[253]: Timestamp('2013-10-01 00:00:00')

在以下情况下 n=0 ,则日期如果在锚点上则不会移动,否则会前滚到下一个锚点。

In [254]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=0)
Out[254]: Timestamp('2014-02-01 00:00:00')

In [255]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=0)
Out[255]: Timestamp('2014-01-31 00:00:00')

In [256]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=0)
Out[256]: Timestamp('2014-01-01 00:00:00')

In [257]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=0)
Out[257]: Timestamp('2014-01-31 00:00:00')

节假日/节假日日历#

假日和日历提供了一种简单的方式来定义要与一起使用的假日规则 CustomBusinessDay 或者在需要预定义的假日集合的其他分析中。这个 AbstractHolidayCalendar 类提供了返回假日列表所需的所有方法,并且仅 rules 需要在特定的假日日历类中定义。此外, start_dateend_date 类属性确定生成假日的日期范围。这些内容应覆盖在 AbstractHolidayCalendar 类以使该范围应用于所有日历子类。 USFederalHolidayCalendar 是唯一存在的日历,并且主要用作开发其他日历的示例。

对于在固定日期(例如,美国阵亡将士纪念日或7月4日)发生的假日,如果该假日恰好在周末或其他未庆祝的日子,则庆祝规则确定了该假日的庆祝时间。已定义的遵守规则包括:

规则

描述

nearest_workday

周六移至周五,周日移至周一

sunday_to_monday

将周日移至下周一

next_monday_or_tuesday

将星期六移至星期一及星期日/星期一移至星期二

previous_friday

将星期六和星期日移到前一个星期五“

next_monday

将周六和周日移至下周一

以下是如何定义节假日和节假日日历的示例:

In [258]: from pandas.tseries.holiday import (
   .....:     Holiday,
   .....:     USMemorialDay,
   .....:     AbstractHolidayCalendar,
   .....:     nearest_workday,
   .....:     MO,
   .....: )
   .....: 

In [259]: class ExampleCalendar(AbstractHolidayCalendar):
   .....:     rules = [
   .....:         USMemorialDay,
   .....:         Holiday("July 4th", month=7, day=4, observance=nearest_workday),
   .....:         Holiday(
   .....:             "Columbus Day",
   .....:             month=10,
   .....:             day=1,
   .....:             offset=pd.DateOffset(weekday=MO(2)),
   .....:         ),
   .....:     ]
   .....: 

In [260]: cal = ExampleCalendar()

In [261]: cal.holidays(datetime.datetime(2012, 1, 1), datetime.datetime(2012, 12, 31))
Out[261]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
提示

weekday=MO(2) 与相同 2 * Week(weekday=2)

使用此日历、创建索引或执行偏移运算将跳过周末和节假日(即阵亡将士纪念日/7月4日)。例如,下面定义了使用 ExampleCalendar 。与任何其他偏移量一样,它可用于创建 DatetimeIndex 或添加到 datetimeTimestamp 对象。

In [262]: pd.date_range(
   .....:     start="7/1/2012", end="7/10/2012", freq=pd.offsets.CDay(calendar=cal)
   .....: ).to_pydatetime()
   .....: 
Out[262]: 
array([datetime.datetime(2012, 7, 2, 0, 0),
       datetime.datetime(2012, 7, 3, 0, 0),
       datetime.datetime(2012, 7, 5, 0, 0),
       datetime.datetime(2012, 7, 6, 0, 0),
       datetime.datetime(2012, 7, 9, 0, 0),
       datetime.datetime(2012, 7, 10, 0, 0)], dtype=object)

In [263]: offset = pd.offsets.CustomBusinessDay(calendar=cal)

In [264]: datetime.datetime(2012, 5, 25) + offset
Out[264]: Timestamp('2012-05-29 00:00:00')

In [265]: datetime.datetime(2012, 7, 3) + offset
Out[265]: Timestamp('2012-07-05 00:00:00')

In [266]: datetime.datetime(2012, 7, 3) + 2 * offset
Out[266]: Timestamp('2012-07-06 00:00:00')

In [267]: datetime.datetime(2012, 7, 6) + offset
Out[267]: Timestamp('2012-07-09 00:00:00')

范围由 start_dateend_date 的类属性 AbstractHolidayCalendar 。默认设置如下所示。

In [268]: AbstractHolidayCalendar.start_date
Out[268]: Timestamp('1970-01-01 00:00:00')

In [269]: AbstractHolidayCalendar.end_date
Out[269]: Timestamp('2200-12-31 00:00:00')

通过将属性设置为日期时间/时间戳/字符串,可以覆盖这些日期。

In [270]: AbstractHolidayCalendar.start_date = datetime.datetime(2012, 1, 1)

In [271]: AbstractHolidayCalendar.end_date = datetime.datetime(2012, 12, 31)

In [272]: cal.holidays()
Out[272]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)

每个日历类都可以通过名称使用 get_calendar 函数,该函数返回Holiday类实例。此功能将自动提供任何导入的日历类。另外, HolidayCalendarFactory 提供一个轻松的界面来创建日历,这些日历是日历或带有附加规则的日历的组合。

In [273]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, USLaborDay

In [274]: cal = get_calendar("ExampleCalendar")

In [275]: cal.rules
Out[275]: 
[Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
 Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f0dc6a6a950>),
 Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]

In [276]: new_cal = HolidayCalendarFactory("NewExampleCalendar", cal, USLaborDay)

In [277]: new_cal.rules
Out[277]: 
[Holiday: Labor Day (month=9, day=1, offset=<DateOffset: weekday=MO(+1)>),
 Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
 Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f0dc6a6a950>),
 Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]

重采样#

Pandas具有简单、强大和高效的功能,用于在频率转换期间执行重采样操作(例如,将第二个数据转换为5分钟的数据)。这在金融应用程序中非常常见,但不限于此。

resample() 是一个基于时间的GROUP BY,后面是它的每个组的约简方法。看一些 cookbook examples 一些先进的策略。

这个 resample() 方法可以直接从 DataFrameGroupBy 对象,请参阅 groupby docs

基础知识#

In [289]: rng = pd.date_range("1/1/2012", periods=100, freq="S")

In [290]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)

In [291]: ts.resample("5Min").sum()
Out[291]: 
2012-01-01    25103
Freq: 5T, dtype: int64

这个 resample 功能非常灵活,允许您指定许多不同的参数来控制频率转换和重采样操作。

通过以下方式提供的任何功能 dispatching 作为返回对象的方法可用,包括 summeanstdsemmaxminmedianfirstlastohlc

In [292]: ts.resample("5Min").mean()
Out[292]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

In [293]: ts.resample("5Min").ohlc()
Out[293]: 
            open  high  low  close
2012-01-01   308   460    9    205

In [294]: ts.resample("5Min").max()
Out[294]: 
2012-01-01    460
Freq: 5T, dtype: int64

对于下采样, closed 可以设置为‘Left’或‘Right’,以指定关闭间隔的哪一端:

In [295]: ts.resample("5Min", closed="right").mean()
Out[295]: 
2011-12-31 23:55:00    308.000000
2012-01-01 00:00:00    250.454545
Freq: 5T, dtype: float64

In [296]: ts.resample("5Min", closed="left").mean()
Out[296]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

参数,如 label 用于操作生成的标签。 label 指定结果是标记为间隔的开始还是结束。

In [297]: ts.resample("5Min").mean()  # by default label='left'
Out[297]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

In [298]: ts.resample("5Min", label="left").mean()
Out[298]: 
2012-01-01    251.03
Freq: 5T, dtype: float64

警告

的缺省值 labelclosed IS‘ left ‘对于除’M‘、’A‘、’Q‘、’BM‘、’BA‘、’BQ‘和’W‘之外的所有频率偏移量,其缺省值均为’Right‘。

这可能会无意中导致向前看,在这种情况下,稍后时间的值被拉回到前一时间,如下面的示例所示 BusinessDay 频率:

In [299]: s = pd.date_range("2000-01-01", "2000-01-05").to_series()

In [300]: s.iloc[2] = pd.NaT

In [301]: s.dt.day_name()
Out[301]: 
2000-01-01     Saturday
2000-01-02       Sunday
2000-01-03          NaN
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: D, dtype: object

# default: label='left', closed='left'
In [302]: s.resample("B").last().dt.day_name()
Out[302]: 
1999-12-31       Sunday
2000-01-03          NaN
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: B, dtype: object

请注意周日的值是如何拉回到前一个周五的。要获得将周日的值推送到星期一的行为,请改用

In [303]: s.resample("B", label="right", closed="right").last().dt.day_name()
Out[303]: 
2000-01-03       Sunday
2000-01-04      Tuesday
2000-01-05    Wednesday
Freq: B, dtype: object

这个 axis 参数可以设置为0或1,并允许您对 DataFrame

kind 可以设置为‘TIMESTAMP’或‘PERIOD’,以将生成的索引与时间戳和时间跨度表示形式相互转换。默认情况下 resample 保留输入表示形式。

convention 在重新采样周期数据时可以设置为‘Start’或‘End’(详见下文)。它指定如何将低频周期转换为高频周期。

上采样#

对于上采样,您可以指定一种方法来上采样和 limit 用于在创建的间隙上进行内插的参数:

# from secondly to every 250 milliseconds
In [304]: ts[:2].resample("250L").asfreq()
Out[304]: 
2012-01-01 00:00:00.000    308.0
2012-01-01 00:00:00.250      NaN
2012-01-01 00:00:00.500      NaN
2012-01-01 00:00:00.750      NaN
2012-01-01 00:00:01.000    204.0
Freq: 250L, dtype: float64

In [305]: ts[:2].resample("250L").ffill()
Out[305]: 
2012-01-01 00:00:00.000    308
2012-01-01 00:00:00.250    308
2012-01-01 00:00:00.500    308
2012-01-01 00:00:00.750    308
2012-01-01 00:00:01.000    204
Freq: 250L, dtype: int64

In [306]: ts[:2].resample("250L").ffill(limit=2)
Out[306]: 
2012-01-01 00:00:00.000    308.0
2012-01-01 00:00:00.250    308.0
2012-01-01 00:00:00.500    308.0
2012-01-01 00:00:00.750      NaN
2012-01-01 00:00:01.000    204.0
Freq: 250L, dtype: float64

稀疏重采样#

稀疏时间序列是指相对于您希望重新采样的时间量,您拥有的点数要少得多的时间序列。如果天真地对稀疏序列进行上采样,可能会生成大量中间值。如果您不想使用一种方法来填充这些值,例如 fill_methodNone ,则中间值将填充为 NaN

因为 resample 是基于时间的分组方式,下面是一种有效地仅对不是全部的组进行重采样的方法 NaN

In [307]: rng = pd.date_range("2014-1-1", periods=100, freq="D") + pd.Timedelta("1s")

In [308]: ts = pd.Series(range(100), index=rng)

如果我们想要重新采样到该系列的全部范围:

In [309]: ts.resample("3T").sum()
Out[309]: 
2014-01-01 00:00:00     0
2014-01-01 00:03:00     0
2014-01-01 00:06:00     0
2014-01-01 00:09:00     0
2014-01-01 00:12:00     0
                       ..
2014-04-09 23:48:00     0
2014-04-09 23:51:00     0
2014-04-09 23:54:00     0
2014-04-09 23:57:00     0
2014-04-10 00:00:00    99
Freq: 3T, Length: 47521, dtype: int64

取而代之的是,我们只能重新抽样那些我们有点的组,如下所示:

In [310]: from functools import partial

In [311]: from pandas.tseries.frequencies import to_offset

In [312]: def round(t, freq):
   .....:     freq = to_offset(freq)
   .....:     return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
   .....: 

In [313]: ts.groupby(partial(round, freq="3T")).sum()
Out[313]: 
2014-01-01     0
2014-01-02     1
2014-01-03     2
2014-01-04     3
2014-01-05     4
              ..
2014-04-06    95
2014-04-07    96
2014-04-08    97
2014-04-09    98
2014-04-10    99
Length: 100, dtype: int64

聚合#

类似于 aggregating APIgroupby API ,以及 window API ,a Resampler 可以有选择地重新采样。

重新采样 DataFrame 默认情况下,将对具有相同函数的所有列执行操作。

In [314]: df = pd.DataFrame(
   .....:     np.random.randn(1000, 3),
   .....:     index=pd.date_range("1/1/2012", freq="S", periods=1000),
   .....:     columns=["A", "B", "C"],
   .....: )
   .....: 

In [315]: r = df.resample("3T")

In [316]: r.mean()
Out[316]: 
                            A         B         C
2012-01-01 00:00:00 -0.033823 -0.121514 -0.081447
2012-01-01 00:03:00  0.056909  0.146731 -0.024320
2012-01-01 00:06:00 -0.058837  0.047046 -0.052021
2012-01-01 00:09:00  0.063123 -0.026158 -0.066533
2012-01-01 00:12:00  0.186340 -0.003144  0.074752
2012-01-01 00:15:00 -0.085954 -0.016287 -0.050046

我们可以使用标准getitem选择特定的一列或多列。

In [317]: r["A"].mean()
Out[317]: 
2012-01-01 00:00:00   -0.033823
2012-01-01 00:03:00    0.056909
2012-01-01 00:06:00   -0.058837
2012-01-01 00:09:00    0.063123
2012-01-01 00:12:00    0.186340
2012-01-01 00:15:00   -0.085954
Freq: 3T, Name: A, dtype: float64

In [318]: r[["A", "B"]].mean()
Out[318]: 
                            A         B
2012-01-01 00:00:00 -0.033823 -0.121514
2012-01-01 00:03:00  0.056909  0.146731
2012-01-01 00:06:00 -0.058837  0.047046
2012-01-01 00:09:00  0.063123 -0.026158
2012-01-01 00:12:00  0.186340 -0.003144
2012-01-01 00:15:00 -0.085954 -0.016287

您可以传递用于聚合的函数列表或DICT,输出 DataFrame

In [319]: r["A"].agg([np.sum, np.mean, np.std])
Out[319]: 
                           sum      mean       std
2012-01-01 00:00:00  -6.088060 -0.033823  1.043263
2012-01-01 00:03:00  10.243678  0.056909  1.058534
2012-01-01 00:06:00 -10.590584 -0.058837  0.949264
2012-01-01 00:09:00  11.362228  0.063123  1.028096
2012-01-01 00:12:00  33.541257  0.186340  0.884586
2012-01-01 00:15:00  -8.595393 -0.085954  1.035476

在重新采样时 DataFrame 中,您可以传递要应用于每一列的函数列表,这将生成具有分层索引的聚合结果:

In [320]: r.agg([np.sum, np.mean])
Out[320]: 
                             A                    B                    C          
                           sum      mean        sum      mean        sum      mean
2012-01-01 00:00:00  -6.088060 -0.033823 -21.872530 -0.121514 -14.660515 -0.081447
2012-01-01 00:03:00  10.243678  0.056909  26.411633  0.146731  -4.377642 -0.024320
2012-01-01 00:06:00 -10.590584 -0.058837   8.468289  0.047046  -9.363825 -0.052021
2012-01-01 00:09:00  11.362228  0.063123  -4.708526 -0.026158 -11.975895 -0.066533
2012-01-01 00:12:00  33.541257  0.186340  -0.565895 -0.003144  13.455299  0.074752
2012-01-01 00:15:00  -8.595393 -0.085954  -1.628689 -0.016287  -5.004580 -0.050046

通过将判决书传递给 aggregate 可以将不同的聚合应用于 DataFrame

In [321]: r.agg({"A": np.sum, "B": lambda x: np.std(x, ddof=1)})
Out[321]: 
                             A         B
2012-01-01 00:00:00  -6.088060  1.001294
2012-01-01 00:03:00  10.243678  1.074597
2012-01-01 00:06:00 -10.590584  0.987309
2012-01-01 00:09:00  11.362228  0.944953
2012-01-01 00:12:00  33.541257  1.095025
2012-01-01 00:15:00  -8.595393  1.035312

函数名也可以是字符串。为了使字符串有效,必须在重新采样的对象上实现该字符串:

In [322]: r.agg({"A": "sum", "B": "std"})
Out[322]: 
                             A         B
2012-01-01 00:00:00  -6.088060  1.001294
2012-01-01 00:03:00  10.243678  1.074597
2012-01-01 00:06:00 -10.590584  0.987309
2012-01-01 00:09:00  11.362228  0.944953
2012-01-01 00:12:00  33.541257  1.095025
2012-01-01 00:15:00  -8.595393  1.035312

此外,您还可以分别为每列指定多个聚合函数。

In [323]: r.agg({"A": ["sum", "std"], "B": ["mean", "std"]})
Out[323]: 
                             A                   B          
                           sum       std      mean       std
2012-01-01 00:00:00  -6.088060  1.043263 -0.121514  1.001294
2012-01-01 00:03:00  10.243678  1.058534  0.146731  1.074597
2012-01-01 00:06:00 -10.590584  0.949264  0.047046  0.987309
2012-01-01 00:09:00  11.362228  1.028096 -0.026158  0.944953
2012-01-01 00:12:00  33.541257  0.884586 -0.003144  1.095025
2012-01-01 00:15:00  -8.595393  1.035476 -0.016287  1.035312

如果一个 DataFrame 没有类似日期时间的索引,但您希望基于帧中类似日期时间的列重新采样,它可以传递给 on 关键字。

In [324]: df = pd.DataFrame(
   .....:     {"date": pd.date_range("2015-01-01", freq="W", periods=5), "a": np.arange(5)},
   .....:     index=pd.MultiIndex.from_arrays(
   .....:         [[1, 2, 3, 4, 5], pd.date_range("2015-01-01", freq="W", periods=5)],
   .....:         names=["v", "d"],
   .....:     ),
   .....: )
   .....: 

In [325]: df
Out[325]: 
                   date  a
v d                       
1 2015-01-04 2015-01-04  0
2 2015-01-11 2015-01-11  1
3 2015-01-18 2015-01-18  2
4 2015-01-25 2015-01-25  3
5 2015-02-01 2015-02-01  4

In [326]: df.resample("M", on="date").sum()
Out[326]: 
            a
date         
2015-01-31  6
2015-02-28  4

同样,如果您希望按类似日期时间的级别重新采样 MultiIndex ,其名称或位置可以传递给 level 关键字。

In [327]: df.resample("M", level="d").sum()
Out[327]: 
            a
d            
2015-01-31  6
2015-02-28  4

遍历组#

使用 Resampler 对象,遍历分组的数据非常自然,其功能类似于 itertools.groupby()

In [328]: small = pd.Series(
   .....:     range(6),
   .....:     index=pd.to_datetime(
   .....:         [
   .....:             "2017-01-01T00:00:00",
   .....:             "2017-01-01T00:30:00",
   .....:             "2017-01-01T00:31:00",
   .....:             "2017-01-01T01:00:00",
   .....:             "2017-01-01T03:00:00",
   .....:             "2017-01-01T03:05:00",
   .....:         ]
   .....:     ),
   .....: )
   .....: 

In [329]: resampled = small.resample("H")

In [330]: for name, group in resampled:
   .....:     print("Group: ", name)
   .....:     print("-" * 27)
   .....:     print(group, end="\n\n")
   .....: 
Group:  2017-01-01 00:00:00
---------------------------
2017-01-01 00:00:00    0
2017-01-01 00:30:00    1
2017-01-01 00:31:00    2
dtype: int64

Group:  2017-01-01 01:00:00
---------------------------
2017-01-01 01:00:00    3
dtype: int64

Group:  2017-01-01 02:00:00
---------------------------
Series([], dtype: int64)

Group:  2017-01-01 03:00:00
---------------------------
2017-01-01 03:00:00    4
2017-01-01 03:05:00    5
dtype: int64

看见 遍历组Resampler.__iter__ 想要更多。

使用 originoffset 调整垃圾箱起点的步骤#

1.1.0 新版功能.

根据时间序列起始点的一天的开始调整分组的仓位。这在频率是一天的倍数的情况下运行良好(例如 30D )或将一天平均分配(如 90s1min )。这可能会与某些不符合此标准的频率产生不一致。要更改此行为,可以使用参数指定固定的时间戳 origin

例如:

In [331]: start, end = "2000-10-01 23:30:00", "2000-10-02 00:30:00"

In [332]: middle = "2000-10-02 00:00:00"

In [333]: rng = pd.date_range(start, end, freq="7min")

In [334]: ts = pd.Series(np.arange(len(rng)) * 3, index=rng)

In [335]: ts
Out[335]: 
2000-10-01 23:30:00     0
2000-10-01 23:37:00     3
2000-10-01 23:44:00     6
2000-10-01 23:51:00     9
2000-10-01 23:58:00    12
2000-10-02 00:05:00    15
2000-10-02 00:12:00    18
2000-10-02 00:19:00    21
2000-10-02 00:26:00    24
Freq: 7T, dtype: int64

在这里我们可以看到,当使用 origin 使用其缺省值 ('start_day' ),之后的结果 '2000-10-02 00:00:00' 根据时间序列的起始点不同而不相同:

In [336]: ts.resample("17min", origin="start_day").sum()
Out[336]: 
2000-10-01 23:14:00     0
2000-10-01 23:31:00     9
2000-10-01 23:48:00    21
2000-10-02 00:05:00    54
2000-10-02 00:22:00    24
Freq: 17T, dtype: int64

In [337]: ts[middle:end].resample("17min", origin="start_day").sum()
Out[337]: 
2000-10-02 00:00:00    33
2000-10-02 00:17:00    45
Freq: 17T, dtype: int64

在这里我们可以看到,当设置 origin'epoch' ,之后的结果 '2000-10-02 00:00:00' 根据时间序列的开始,是相同的:

In [338]: ts.resample("17min", origin="epoch").sum()
Out[338]: 
2000-10-01 23:18:00     0
2000-10-01 23:35:00    18
2000-10-01 23:52:00    27
2000-10-02 00:09:00    39
2000-10-02 00:26:00    24
Freq: 17T, dtype: int64

In [339]: ts[middle:end].resample("17min", origin="epoch").sum()
Out[339]: 
2000-10-01 23:52:00    15
2000-10-02 00:09:00    39
2000-10-02 00:26:00    24
Freq: 17T, dtype: int64

如果需要,您可以使用自定义时间戳 origin

In [340]: ts.resample("17min", origin="2001-01-01").sum()
Out[340]: 
2000-10-01 23:30:00     9
2000-10-01 23:47:00    21
2000-10-02 00:04:00    54
2000-10-02 00:21:00    24
Freq: 17T, dtype: int64

In [341]: ts[middle:end].resample("17min", origin=pd.Timestamp("2001-01-01")).sum()
Out[341]: 
2000-10-02 00:04:00    54
2000-10-02 00:21:00    24
Freq: 17T, dtype: int64

如果需要,您只需使用 offset 将添加到默认设置中的Timedelta origin 。这两个示例等同于此时间序列:

In [342]: ts.resample("17min", origin="start").sum()
Out[342]: 
2000-10-01 23:30:00     9
2000-10-01 23:47:00    21
2000-10-02 00:04:00    54
2000-10-02 00:21:00    24
Freq: 17T, dtype: int64

In [343]: ts.resample("17min", offset="23h30min").sum()
Out[343]: 
2000-10-01 23:30:00     9
2000-10-01 23:47:00    21
2000-10-02 00:04:00    54
2000-10-02 00:21:00    24
Freq: 17T, dtype: int64

请注意 'start'origin 关于最后一个例子。在这种情况下, origin 将设置为时间序列的第一个值。

向后重采样#

1.3.0 新版功能.

有时,我们不需要调整垃圾箱的开头,而是需要固定垃圾箱的末端,以使用给定的向后重新采样 freq 。向后重采样集 closed'right' 默认情况下,因为最后一个值应该被视为最后一个存储箱的边缘点。

我们可以设置 origin'end' 。特定对象的值 Timestamp INDEX代表当前 Timestamp 减号 freq 到目前为止 Timestamp 右路近距离击球。

In [344]: ts.resample('17min', origin='end').sum()
Out[344]: 
2000-10-01 23:35:00     0
2000-10-01 23:52:00    18
2000-10-02 00:09:00    27
2000-10-02 00:26:00    63
Freq: 17T, dtype: int64

此外,与 'start_day' 选项, end_day 受支持。这会将原点设置为最大午夜的天花板 Timestamp

In [345]: ts.resample('17min', origin='end_day').sum()
Out[345]: 
2000-10-01 23:38:00     3
2000-10-01 23:55:00    15
2000-10-02 00:12:00    45
2000-10-02 00:29:00    45
Freq: 17T, dtype: int64

上面的结果使用 2000-10-02 00:29:00 作为自以下计算以来的最后一个箱子的右边缘。

In [346]: ceil_mid = rng.max().ceil('D')

In [347]: freq = pd.offsets.Minute(17)

In [348]: bin_res = ceil_mid - freq * ((ceil_mid - rng.max()) // freq)

In [349]: bin_res
Out[349]: Timestamp('2000-10-02 00:29:00')

时间跨度表示#

规则的时间间隔由 Period Pandas中的物体,而序列 Period 对象被收集在 PeriodIndex ,它可以使用便利性函数创建 period_range

期间#

A Period 表示一段时间(例如,一天、一个月、一个季度等)。您可以通过指定跨度 freq 关键字使用频率别名,如下所示。因为 freq 表示跨度为 Period ,它不能像“-3D”那样是负面的。

In [350]: pd.Period("2012", freq="A-DEC")
Out[350]: Period('2012', 'A-DEC')

In [351]: pd.Period("2012-1-1", freq="D")
Out[351]: Period('2012-01-01', 'D')

In [352]: pd.Period("2012-1-1 19:00", freq="H")
Out[352]: Period('2012-01-01 19:00', 'H')

In [353]: pd.Period("2012-1-1 19:00", freq="5H")
Out[353]: Period('2012-01-01 19:00', '5H')

在周期中添加和减去整数会按周期自身的频率移动周期。之间不允许进行运算 Period 使用不同的 freq (跨距)。

In [354]: p = pd.Period("2012", freq="A-DEC")

In [355]: p + 1
Out[355]: Period('2013', 'A-DEC')

In [356]: p - 3
Out[356]: Period('2009', 'A-DEC')

In [357]: p = pd.Period("2012-01", freq="2M")

In [358]: p + 2
Out[358]: Period('2012-05', '2M')

In [359]: p - 1
Out[359]: Period('2011-11', '2M')

In [360]: p == pd.Period("2012-01", freq="3M")
Out[360]: False

如果 Period 频率为每日或更高 (DHTSLUN ), offsetstimedelta -如果结果可以具有相同的频率,则可以添加LIKE。否则, ValueError 都会被举起。

In [361]: p = pd.Period("2014-07-01 09:00", freq="H")

In [362]: p + pd.offsets.Hour(2)
Out[362]: Period('2014-07-01 11:00', 'H')

In [363]: p + datetime.timedelta(minutes=120)
Out[363]: Period('2014-07-01 11:00', 'H')

In [364]: p + np.timedelta64(7200, "s")
Out[364]: Period('2014-07-01 11:00', 'H')
In [1]: p + pd.offsets.Minute(5)
Traceback
   ...
ValueError: Input has different freq from Period(freq=H)

如果 Period 有其他频率,只是相同 offsets 可以添加。否则, ValueError 都会被举起。

In [365]: p = pd.Period("2014-07", freq="M")

In [366]: p + pd.offsets.MonthEnd(3)
Out[366]: Period('2014-10', 'M')
In [1]: p + pd.offsets.MonthBegin(3)
Traceback
   ...
ValueError: Input has different freq from Period(freq=M)

取…的不同 Period 相同频率的实例将返回它们之间的频率单位数:

In [367]: pd.Period("2012", freq="A-DEC") - pd.Period("2002", freq="A-DEC")
Out[367]: <10 * YearEnds: month=12>

PeriodIndex和Period_Range#

有规律的序列 Period 对象可以在 PeriodIndex ,它可以使用 period_range 方便功能:

In [368]: prng = pd.period_range("1/1/2011", "1/1/2012", freq="M")

In [369]: prng
Out[369]: 
PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
             '2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12',
             '2012-01'],
            dtype='period[M]')

这个 PeriodIndex 构造函数也可以直接使用:

In [370]: pd.PeriodIndex(["2011-1", "2011-2", "2011-3"], freq="M")
Out[370]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')

传递相乘的频率会输出一个序列 Period 它的跨度扩大了一倍。

In [371]: pd.period_range(start="2014-01", freq="3M", periods=4)
Out[371]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]')

如果 startendPeriod 对象,则它们将用作 PeriodIndex 与频率匹配的 PeriodIndex 构造函数。

In [372]: pd.period_range(
   .....:     start=pd.Period("2017Q1", freq="Q"), end=pd.Period("2017Q2", freq="Q"), freq="M"
   .....: )
   .....: 
Out[372]: PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]')

就像 DatetimeIndex ,a PeriodIndex 还可用于索引Pandas对象:

In [373]: ps = pd.Series(np.random.randn(len(prng)), prng)

In [374]: ps
Out[374]: 
2011-01   -2.916901
2011-02    0.514474
2011-03    1.346470
2011-04    0.816397
2011-05    2.258648
2011-06    0.494789
2011-07    0.301239
2011-08    0.464776
2011-09   -1.393581
2011-10    0.056780
2011-11    0.197035
2011-12    2.261385
2012-01   -0.329583
Freq: M, dtype: float64

PeriodIndex 支持使用相同规则的加法和减法 Period

In [375]: idx = pd.period_range("2014-07-01 09:00", periods=5, freq="H")

In [376]: idx
Out[376]: 
PeriodIndex(['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00',
             '2014-07-01 12:00', '2014-07-01 13:00'],
            dtype='period[H]')

In [377]: idx + pd.offsets.Hour(2)
Out[377]: 
PeriodIndex(['2014-07-01 11:00', '2014-07-01 12:00', '2014-07-01 13:00',
             '2014-07-01 14:00', '2014-07-01 15:00'],
            dtype='period[H]')

In [378]: idx = pd.period_range("2014-07", periods=5, freq="M")

In [379]: idx
Out[379]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]')

In [380]: idx + pd.offsets.MonthEnd(3)
Out[380]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]')

PeriodIndex 具有自己的数据类型,名为 period ,请参阅 Period Dtypes

期间数据类型#

PeriodIndex has a custom period dtype. This is a pandas extension dtype similar to the timezone aware dtype (datetime64[ns, tz]).

这个 period Dtype保存 freq 属性,并用 period[freq] 喜欢 period[D]period[M] ,使用 frequency strings

In [381]: pi = pd.period_range("2016-01-01", periods=3, freq="M")

In [382]: pi
Out[382]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]')

In [383]: pi.dtype
Out[383]: period[M]

这个 period 数据类型可用于 .astype(...) 。它允许用户更改 freq 属于 PeriodIndex 喜欢 .asfreq() 并将其转换为 DatetimeIndexPeriodIndex 喜欢 to_period()

# change monthly freq to daily freq
In [384]: pi.astype("period[D]")
Out[384]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]')

# convert to DatetimeIndex
In [385]: pi.astype("datetime64[ns]")
Out[385]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')

# convert to PeriodIndex
In [386]: dti = pd.date_range("2011-01-01", freq="M", periods=3)

In [387]: dti
Out[387]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='M')

In [388]: dti.astype("period[M]")
Out[388]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')

周期索引部分字符串索引#

PerodIndex现在支持使用非单调索引的部分字符串切片。

1.1.0 新版功能.

可以将日期和字符串传递给 SeriesDataFrame 使用 PeriodIndex ,其方式与 DatetimeIndex 。有关详细信息,请参阅 DatetimeIndex Partial String Indexing

In [389]: ps["2011-01"]
Out[389]: -2.9169013294054507

In [390]: ps[datetime.datetime(2011, 12, 25):]
Out[390]: 
2011-12    2.261385
2012-01   -0.329583
Freq: M, dtype: float64

In [391]: ps["10/31/2011":"12/31/2011"]
Out[391]: 
2011-10    0.056780
2011-11    0.197035
2011-12    2.261385
Freq: M, dtype: float64

传递表示低于的频率的字符串 PeriodIndex 返回部分切片数据。

In [392]: ps["2011"]
Out[392]: 
2011-01   -2.916901
2011-02    0.514474
2011-03    1.346470
2011-04    0.816397
2011-05    2.258648
2011-06    0.494789
2011-07    0.301239
2011-08    0.464776
2011-09   -1.393581
2011-10    0.056780
2011-11    0.197035
2011-12    2.261385
Freq: M, dtype: float64

In [393]: dfp = pd.DataFrame(
   .....:     np.random.randn(600, 1),
   .....:     columns=["A"],
   .....:     index=pd.period_range("2013-01-01 9:00", periods=600, freq="T"),
   .....: )
   .....: 

In [394]: dfp
Out[394]: 
                         A
2013-01-01 09:00 -0.538468
2013-01-01 09:01 -1.365819
2013-01-01 09:02 -0.969051
2013-01-01 09:03 -0.331152
2013-01-01 09:04 -0.245334
...                    ...
2013-01-01 18:55  0.522460
2013-01-01 18:56  0.118710
2013-01-01 18:57  0.167517
2013-01-01 18:58  0.922883
2013-01-01 18:59  1.721104

[600 rows x 1 columns]

In [395]: dfp.loc["2013-01-01 10H"]
Out[395]: 
                         A
2013-01-01 10:00 -0.308975
2013-01-01 10:01  0.542520
2013-01-01 10:02  1.061068
2013-01-01 10:03  0.754005
2013-01-01 10:04  0.352933
...                    ...
2013-01-01 10:55 -0.865621
2013-01-01 10:56 -1.167818
2013-01-01 10:57 -2.081748
2013-01-01 10:58 -0.527146
2013-01-01 10:59  0.802298

[60 rows x 1 columns]

和以前一样 DatetimeIndex ,则结果中将包括终结点。下面的示例对从10:00到11:59的数据进行切片。

In [396]: dfp["2013-01-01 10H":"2013-01-01 11H"]
Out[396]: 
                         A
2013-01-01 10:00 -0.308975
2013-01-01 10:01  0.542520
2013-01-01 10:02  1.061068
2013-01-01 10:03  0.754005
2013-01-01 10:04  0.352933
...                    ...
2013-01-01 11:55 -0.590204
2013-01-01 11:56  1.539990
2013-01-01 11:57 -1.224826
2013-01-01 11:58  0.578798
2013-01-01 11:59 -0.685496

[120 rows x 1 columns]

使用PerioIndex进行频率转换和重采样#

的频率 PeriodPeriodIndex 可以通过 asfreq 方法。让我们从截至12月的2011财年开始:

In [397]: p = pd.Period("2011", freq="A-DEC")

In [398]: p
Out[398]: Period('2011', 'A-DEC')

我们可以将其转换为每月一次。使用 how 参数,我们可以指定是返回起始月份还是返回结束月份:

In [399]: p.asfreq("M", how="start")
Out[399]: Period('2011-01', 'M')

In [400]: p.asfreq("M", how="end")
Out[400]: Period('2011-12', 'M')

为方便起见,提供了缩写‘s’和‘e’:

In [401]: p.asfreq("M", "s")
Out[401]: Period('2011-01', 'M')

In [402]: p.asfreq("M", "e")
Out[402]: Period('2011-12', 'M')

转换为“超周期”(例如,年度频率是季度频率的超周期)会自动返回包含输入周期的超周期:

In [403]: p = pd.Period("2011-12", freq="M")

In [404]: p.asfreq("A-NOV")
Out[404]: Period('2012', 'A-NOV')

请注意,由于我们转换为在11月结束一年的年度频率,因此2011年12月的月度周期实际上是在2012年的A-11月期间。

固定频率的周期转换对于处理经济、商业和其他领域常见的各种季度数据特别有用。许多组织根据其财政年度开始和结束的月份来定义季度。因此,2011年第一季度可能从2010年开始,或进入2011年的几个月。通过固定频率,Pandas在所有季度频率下都能工作 Q-JAN 通过 Q-DEC

Q-DEC 定义常规日历季度:

In [405]: p = pd.Period("2012Q1", freq="Q-DEC")

In [406]: p.asfreq("D", "s")
Out[406]: Period('2012-01-01', 'D')

In [407]: p.asfreq("D", "e")
Out[407]: Period('2012-03-31', 'D')

Q-MAR 将财政年度结束时间定义为3月:

In [408]: p = pd.Period("2011Q4", freq="Q-MAR")

In [409]: p.asfreq("D", "s")
Out[409]: Period('2011-01-01', 'D')

In [410]: p.asfreq("D", "e")
Out[410]: Period('2011-03-31', 'D')

在表示之间进行转换#

可以使用以下命令将带时间戳的数据转换为周期索引数据 to_period 反之亦然,使用 to_timestamp

In [411]: rng = pd.date_range("1/1/2012", periods=5, freq="M")

In [412]: ts = pd.Series(np.random.randn(len(rng)), index=rng)

In [413]: ts
Out[413]: 
2012-01-31    1.931253
2012-02-29   -0.184594
2012-03-31    0.249656
2012-04-30   -0.978151
2012-05-31   -0.873389
Freq: M, dtype: float64

In [414]: ps = ts.to_period()

In [415]: ps
Out[415]: 
2012-01    1.931253
2012-02   -0.184594
2012-03    0.249656
2012-04   -0.978151
2012-05   -0.873389
Freq: M, dtype: float64

In [416]: ps.to_timestamp()
Out[416]: 
2012-01-01    1.931253
2012-02-01   -0.184594
2012-03-01    0.249656
2012-04-01   -0.978151
2012-05-01   -0.873389
Freq: MS, dtype: float64

请记住,‘s’和‘e’可用于返回句点开始或结束时的时间戳:

In [417]: ps.to_timestamp("D", how="s")
Out[417]: 
2012-01-01    1.931253
2012-02-01   -0.184594
2012-03-01    0.249656
2012-04-01   -0.978151
2012-05-01   -0.873389
Freq: MS, dtype: float64

在PERIOD和TIMESTAMP之间进行转换可以使用一些方便的算术函数。在下面的示例中,我们将年度结束时间为11月的季度频率转换为季度结束后的下一个月的上午9点:

In [418]: prng = pd.period_range("1990Q1", "2000Q4", freq="Q-NOV")

In [419]: ts = pd.Series(np.random.randn(len(prng)), prng)

In [420]: ts.index = (prng.asfreq("M", "e") + 1).asfreq("H", "s") + 9

In [421]: ts.head()
Out[421]: 
1990-03-01 09:00   -0.109291
1990-06-01 09:00   -0.637235
1990-09-01 09:00   -1.735925
1990-12-01 09:00    2.096946
1991-03-01 09:00   -1.039926
Freq: H, dtype: float64

表示超出边界范围的#

如果您的数据位于 Timestamp 边界,请参见 Timestamp limitations ,则可以使用 PeriodIndex 和/或 SeriesPeriods 来做计算。

In [422]: span = pd.period_range("1215-01-01", "1381-01-01", freq="D")

In [423]: span
Out[423]: 
PeriodIndex(['1215-01-01', '1215-01-02', '1215-01-03', '1215-01-04',
             '1215-01-05', '1215-01-06', '1215-01-07', '1215-01-08',
             '1215-01-09', '1215-01-10',
             ...
             '1380-12-23', '1380-12-24', '1380-12-25', '1380-12-26',
             '1380-12-27', '1380-12-28', '1380-12-29', '1380-12-30',
             '1380-12-31', '1381-01-01'],
            dtype='period[D]', length=60632)

若要从 int64 基于YYYYMMDD表示形式。

In [424]: s = pd.Series([20121231, 20141130, 99991231])

In [425]: s
Out[425]: 
0    20121231
1    20141130
2    99991231
dtype: int64

In [426]: def conv(x):
   .....:     return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D")
   .....: 

In [427]: s.apply(conv)
Out[427]: 
0    2012-12-31
1    2014-11-30
2    9999-12-31
dtype: period[D]

In [428]: s.apply(conv)[2]
Out[428]: Period('9999-12-31', 'D')

这些都可以很容易地转换为 PeriodIndex

In [429]: span = pd.PeriodIndex(s.apply(conv))

In [430]: span
Out[430]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]')

时区处理#

Pandas为在不同时区使用时间戳提供了丰富的支持 pytzdateutil 库或 datetime.timezone 标准库中的对象。

使用时区#

默认情况下,Pandas对象不知道时区:

In [431]: rng = pd.date_range("3/6/2012 00:00", periods=15, freq="D")

In [432]: rng.tz is None
Out[432]: True

若要将这些日期本地化为时区(将特定时区分配给原始日期),可以使用 tz_localize 方法或 tz 中的关键字参数 date_range()Timestamp ,或 DatetimeIndex 。你可以选择通过 pytzdateutil 时区对象或Olson时区数据库字符串。Olson时区字符串将返回 pytz 默认情况下,时区对象。归来 dateutil 时区对象,附加 dateutil/ 在琴弦之前。

  • 在……里面 pytz 您可以使用以下命令查找常用(和不常用)时区的列表 from pytz import common_timezones, all_timezones

  • dateutil 使用操作系统时区,因此没有可用的固定列表。对于公共区域,名称与 pytz

In [433]: import dateutil

# pytz
In [434]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz="Europe/London")

In [435]: rng_pytz.tz
Out[435]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>

# dateutil
In [436]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D")

In [437]: rng_dateutil = rng_dateutil.tz_localize("dateutil/Europe/London")

In [438]: rng_dateutil.tz
Out[438]: tzfile('/usr/share/zoneinfo/Europe/London')

# dateutil - utc special case
In [439]: rng_utc = pd.date_range(
   .....:     "3/6/2012 00:00",
   .....:     periods=3,
   .....:     freq="D",
   .....:     tz=dateutil.tz.tzutc(),
   .....: )
   .....: 

In [440]: rng_utc.tz
Out[440]: tzutc()

0.25.0 新版功能.

# datetime.timezone
In [441]: rng_utc = pd.date_range(
   .....:     "3/6/2012 00:00",
   .....:     periods=3,
   .....:     freq="D",
   .....:     tz=datetime.timezone.utc,
   .....: )
   .....: 

In [442]: rng_utc.tz
Out[442]: datetime.timezone.utc

请注意, UTC 时区是中的特例 dateutil 并应显式构造为 dateutil.tz.tzutc 。您也可以首先显式构造其他时区对象。

In [443]: import pytz

# pytz
In [444]: tz_pytz = pytz.timezone("Europe/London")

In [445]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D")

In [446]: rng_pytz = rng_pytz.tz_localize(tz_pytz)

In [447]: rng_pytz.tz == tz_pytz
Out[447]: True

# dateutil
In [448]: tz_dateutil = dateutil.tz.gettz("Europe/London")

In [449]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz=tz_dateutil)

In [450]: rng_dateutil.tz == tz_dateutil
Out[450]: True

若要将支持时区的Pandas对象从一个时区转换为另一个时区,可以使用 tz_convert 方法。

In [451]: rng_pytz.tz_convert("US/Eastern")
Out[451]: 
DatetimeIndex(['2012-03-05 19:00:00-05:00', '2012-03-06 19:00:00-05:00',
               '2012-03-07 19:00:00-05:00'],
              dtype='datetime64[ns, US/Eastern]', freq=None)

备注

在使用时 pytz 时区, DatetimeIndex 将构造一个不同于 Timestamp 对于相同的时区输入。一个 DatetimeIndex 可以包含一个集合 Timestamp 对象,这些对象可能具有不同的UTC偏移量,不能用一个 pytz 时区实例,而一个 Timestamp 表示具有特定UTC偏移量的时间点。

In [452]: dti = pd.date_range("2019-01-01", periods=3, freq="D", tz="US/Pacific")

In [453]: dti.tz
Out[453]: <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>

In [454]: ts = pd.Timestamp("2019-01-01", tz="US/Pacific")

In [455]: ts.tz
Out[455]: <DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>

警告

当心库之间的转换。对于某些时区, pytzdateutil 对区域有不同的定义。对于非常规时区来说,这比像这样的“标准”时区更成问题 US/Eastern

警告

请注意,不同版本的时区库的时区定义可能不会被视为相同。在使用使用一个版本本地化并使用另一个版本操作的存储数据时,这可能会导致问题。看见 here 关于如何处理这样的情况。

警告

pytz 时区,将时区对象直接传递到 datetime.datetime 构造函数(例如, datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern')) 。相反,DateTime需要使用 localize 方法的基础上。 pytz 时区对象。

警告

请注意,对于未来的时间,任何时区库都不能保证时区(和UTC)之间的正确转换,因为时区与UTC的偏移量可能会被相应的政府更改。

警告

如果您使用的日期超过2038-01-18,由于2038年问题导致的基础库中当前的缺陷,将不会应用夏令时(DST)对时区感知日期的调整。如果基础库固定,则将应用DST过渡。

例如,对于英国夏令时的两个日期(因此通常为GMT+1),以下两个断言都评估为真:

In [456]: d_2037 = "2037-03-31T010101"

In [457]: d_2038 = "2038-03-31T010101"

In [458]: DST = "Europe/London"

In [459]: assert pd.Timestamp(d_2037, tz=DST) != pd.Timestamp(d_2037, tz="GMT")

In [460]: assert pd.Timestamp(d_2038, tz=DST) == pd.Timestamp(d_2038, tz="GMT")

在引擎盖下,所有时间戳都以UTC存储。来自时区感知的值 DatetimeIndexTimestamp 将有自己的字段(日、小时、分钟等)已本地化到时区。但是,即使时间戳位于不同的时区,具有相同UTC值的时间戳仍被视为相等:

In [461]: rng_eastern = rng_utc.tz_convert("US/Eastern")

In [462]: rng_berlin = rng_utc.tz_convert("Europe/Berlin")

In [463]: rng_eastern[2]
Out[463]: Timestamp('2012-03-07 19:00:00-0500', tz='US/Eastern', freq='D')

In [464]: rng_berlin[2]
Out[464]: Timestamp('2012-03-08 01:00:00+0100', tz='Europe/Berlin', freq='D')

In [465]: rng_eastern[2] == rng_berlin[2]
Out[465]: True

两地之间的运营 Series 在不同的时区将产生UTC Series ,对齐UTC时间戳上的数据:

In [466]: ts_utc = pd.Series(range(3), pd.date_range("20130101", periods=3, tz="UTC"))

In [467]: eastern = ts_utc.tz_convert("US/Eastern")

In [468]: berlin = ts_utc.tz_convert("Europe/Berlin")

In [469]: result = eastern + berlin

In [470]: result
Out[470]: 
2013-01-01 00:00:00+00:00    0
2013-01-02 00:00:00+00:00    2
2013-01-03 00:00:00+00:00    4
Freq: D, dtype: int64

In [471]: result.index
Out[471]: 
DatetimeIndex(['2013-01-01 00:00:00+00:00', '2013-01-02 00:00:00+00:00',
               '2013-01-03 00:00:00+00:00'],
              dtype='datetime64[ns, UTC]', freq='D')

要删除时区信息,请使用 tz_localize(None)tz_convert(None)tz_localize(None) 将删除生成本地时间表示形式的时区。 tz_convert(None) 将在转换为UTC时间后删除时区。

In [472]: didx = pd.date_range(start="2014-08-01 09:00", freq="H", periods=3, tz="US/Eastern")

In [473]: didx
Out[473]: 
DatetimeIndex(['2014-08-01 09:00:00-04:00', '2014-08-01 10:00:00-04:00',
               '2014-08-01 11:00:00-04:00'],
              dtype='datetime64[ns, US/Eastern]', freq='H')

In [474]: didx.tz_localize(None)
Out[474]: 
DatetimeIndex(['2014-08-01 09:00:00', '2014-08-01 10:00:00',
               '2014-08-01 11:00:00'],
              dtype='datetime64[ns]', freq=None)

In [475]: didx.tz_convert(None)
Out[475]: 
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
               '2014-08-01 15:00:00'],
              dtype='datetime64[ns]', freq='H')

# tz_convert(None) is identical to tz_convert('UTC').tz_localize(None)
In [476]: didx.tz_convert("UTC").tz_localize(None)
Out[476]: 
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
               '2014-08-01 15:00:00'],
              dtype='datetime64[ns]', freq=None)

褶皱#

1.1.0 新版功能.

对于不明确的时间,Pandas支持显式指定仅限关键字的Fold参数。由于夏令时,一个挂钟时间在从夏季时间转换到冬季时间时可以出现两次;Fold描述类似日期时间的时间是否对应于第一次(0)或第二次(1)挂钟命中不明确的时间。仅支持从朴素构建的折叠 datetime.datetime (see datetime documentation 有关详细信息)或 Timestamp 或用于从组件构造(见下文)。仅限 dateutil timezones are supported (see dateutil documentationdateutil methods that deal with ambiguous datetimes) as pytz timezones do not support fold (see pytz documentation 以获取有关如何 pytz 处理模棱两可的日期时间)。使用本地化不明确的DateTime pytz ,请使用 Timestamp.tz_localize() 。一般而言,我们建议依赖于 Timestamp.tz_localize() 在本地化不明确的日期时间时,如果您需要直接控制它们的处理方式。

In [477]: pd.Timestamp(
   .....:     datetime.datetime(2019, 10, 27, 1, 30, 0, 0),
   .....:     tz="dateutil/Europe/London",
   .....:     fold=0,
   .....: )
   .....: 
Out[477]: Timestamp('2019-10-27 01:30:00+0100', tz='dateutil//usr/share/zoneinfo/Europe/London')

In [478]: pd.Timestamp(
   .....:     year=2019,
   .....:     month=10,
   .....:     day=27,
   .....:     hour=1,
   .....:     minute=30,
   .....:     tz="dateutil/Europe/London",
   .....:     fold=1,
   .....: )
   .....: 
Out[478]: Timestamp('2019-10-27 01:30:00+0000', tz='dateutil//usr/share/zoneinfo/Europe/London')

本地化时的含糊时间#

tz_localize 可能无法确定时间戳的UTC偏移量,因为当地时区的夏令时(DST)会导致某些时间在一天内出现两次(“时钟倒退”)。以下选项可用:

  • 'raise' :提出一个 pytz.AmbiguousTimeError (默认行为)

  • 'infer' :尝试根据时间戳的单调性确定正确的偏移量

  • 'NaT': Replaces ambiguous times with NaT

  • boolTrue 表示DST时间, False 表示非DST时间。像阵列一样的 bool 值支持一系列时间。

In [479]: rng_hourly = pd.DatetimeIndex(
   .....:     ["11/06/2011 00:00", "11/06/2011 01:00", "11/06/2011 01:00", "11/06/2011 02:00"]
   .....: )
   .....: 

这将失败,因为存在不明确的时间 ('11/06/2011 01:00' )

In [2]: rng_hourly.tz_localize('US/Eastern')
AmbiguousTimeError: Cannot infer dst time from Timestamp('2011-11-06 01:00:00'), try using the 'ambiguous' argument

通过指定以下内容来处理这些不明确的时间。

In [480]: rng_hourly.tz_localize("US/Eastern", ambiguous="infer")
Out[480]: 
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
               '2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
              dtype='datetime64[ns, US/Eastern]', freq=None)

In [481]: rng_hourly.tz_localize("US/Eastern", ambiguous="NaT")
Out[481]: 
DatetimeIndex(['2011-11-06 00:00:00-04:00', 'NaT', 'NaT',
               '2011-11-06 02:00:00-05:00'],
              dtype='datetime64[ns, US/Eastern]', freq=None)

In [482]: rng_hourly.tz_localize("US/Eastern", ambiguous=[True, True, False, False])
Out[482]: 
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
               '2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
              dtype='datetime64[ns, US/Eastern]', freq=None)

本地化时不存在时间#

DST转换还可以将本地时间提前1小时,从而创建不存在的本地时间(“时钟向前跳动”)。本地化不存在时间的时间序列的行为可以由 nonexistent 争论。以下选项可用:

  • 'raise' :提出一个 pytz.NonExistentTimeError (默认行为)

  • 'NaT': Replaces nonexistent times with NaT

  • 'shift_forward' :将不存在的时间前移到最接近的实时

  • 'shift_backward' :将不存在的时间向后移动到最接近的实时时间

  • TimeDelta对象:将不存在的时间按时间增量持续时间移位

In [483]: dti = pd.date_range(start="2015-03-29 02:30:00", periods=3, freq="H")

# 2:30 is a nonexistent time

默认情况下,不存在的时间的本地化将引发错误。

In [2]: dti.tz_localize('Europe/Warsaw')
NonExistentTimeError: 2015-03-29 02:30:00

将不存在的时间转换为 NaT 或者改变时代。

In [484]: dti
Out[484]: 
DatetimeIndex(['2015-03-29 02:30:00', '2015-03-29 03:30:00',
               '2015-03-29 04:30:00'],
              dtype='datetime64[ns]', freq='H')

In [485]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_forward")
Out[485]: 
DatetimeIndex(['2015-03-29 03:00:00+02:00', '2015-03-29 03:30:00+02:00',
               '2015-03-29 04:30:00+02:00'],
              dtype='datetime64[ns, Europe/Warsaw]', freq=None)

In [486]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_backward")
Out[486]: 
DatetimeIndex(['2015-03-29 01:59:59.999999999+01:00',
                         '2015-03-29 03:30:00+02:00',
                         '2015-03-29 04:30:00+02:00'],
              dtype='datetime64[ns, Europe/Warsaw]', freq=None)

In [487]: dti.tz_localize("Europe/Warsaw", nonexistent=pd.Timedelta(1, unit="H"))
Out[487]: 
DatetimeIndex(['2015-03-29 03:30:00+02:00', '2015-03-29 03:30:00+02:00',
               '2015-03-29 04:30:00+02:00'],
              dtype='datetime64[ns, Europe/Warsaw]', freq=None)

In [488]: dti.tz_localize("Europe/Warsaw", nonexistent="NaT")
Out[488]: 
DatetimeIndex(['NaT', '2015-03-29 03:30:00+02:00',
               '2015-03-29 04:30:00+02:00'],
              dtype='datetime64[ns, Europe/Warsaw]', freq=None)

时区系列操作#

A Series 使用时区 幼稚 值由数据类型 datetime64[ns]

In [489]: s_naive = pd.Series(pd.date_range("20130101", periods=3))

In [490]: s_naive
Out[490]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
dtype: datetime64[ns]

A Series 有时区的 心知肚明 值由数据类型 datetime64[ns, tz] 哪里 tz 是时区吗

In [491]: s_aware = pd.Series(pd.date_range("20130101", periods=3, tz="US/Eastern"))

In [492]: s_aware
Out[492]: 
0   2013-01-01 00:00:00-05:00
1   2013-01-02 00:00:00-05:00
2   2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]

这两个都是 Series 时区信息可以通过 .dt 访问者,请参见 the dt accessor section

例如,本地化一个朴素的戳记并将其转换为时区感知。

In [493]: s_naive.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[493]: 
0   2012-12-31 19:00:00-05:00
1   2013-01-01 19:00:00-05:00
2   2013-01-02 19:00:00-05:00
dtype: datetime64[ns, US/Eastern]

时区信息也可以使用 astype 方法。此方法可以在不同的时区感知数据类型之间进行转换。

# convert to a new time zone
In [494]: s_aware.astype("datetime64[ns, CET]")
Out[494]: 
0   2013-01-01 06:00:00+01:00
1   2013-01-02 06:00:00+01:00
2   2013-01-03 06:00:00+01:00
dtype: datetime64[ns, CET]

备注

使用 Series.to_numpy() 在一个 Series 返回数据的NumPy数组。NumPy目前不支持时区(即使它是 打印 在本地时区!)中,因此为时区感知数据返回时间戳的对象数组:

In [495]: s_naive.to_numpy()
Out[495]: 
array(['2013-01-01T00:00:00.000000000', '2013-01-02T00:00:00.000000000',
       '2013-01-03T00:00:00.000000000'], dtype='datetime64[ns]')

In [496]: s_aware.to_numpy()
Out[496]: 
array([Timestamp('2013-01-01 00:00:00-0500', tz='US/Eastern'),
       Timestamp('2013-01-02 00:00:00-0500', tz='US/Eastern'),
       Timestamp('2013-01-03 00:00:00-0500', tz='US/Eastern')],
      dtype=object)

通过转换为时间戳的对象数组,它保留了时区信息。例如,在转换回系列时:

In [497]: pd.Series(s_aware.to_numpy())
Out[497]: 
0   2013-01-01 00:00:00-05:00
1   2013-01-02 00:00:00-05:00
2   2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]

但是,如果您想要一个实际的NumPy datetime64[ns] 数组(值转换为UTC)而不是对象数组,您可以指定 dtype 论点:

In [498]: s_aware.to_numpy(dtype="datetime64[ns]")
Out[498]: 
array(['2013-01-01T05:00:00.000000000', '2013-01-02T05:00:00.000000000',
       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')