时间序列/日期功能#
Pandas包含处理所有域的时间序列数据的广泛功能和特性。使用数字复制 datetime64
和 timedelta64
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个与时间相关的概念:
日期和时间:支持时区的特定日期和时间。类似于
datetime.datetime
来自标准库的。时间增量:绝对持续时间。类似于
datetime.timedelta
来自标准库的。时间跨度:由时间点及其相关频率定义的时间跨度。
日期偏移量:尊重日历算法的相对持续时间。类似于
dateutil.relativedelta.relativedelta
从dateutil
包裹。
概念 |
标量类 |
数组类 |
Pandas数据类型 |
初级创作方法 |
---|---|---|---|---|
日期和时间 |
|
|
|
|
时间增量 |
|
|
|
|
时间跨度 |
|
|
|
|
日期偏移 |
|
|
|
|
对于时间序列数据,传统上用指数表示时间分量 Series
或 DataFrame
因此,可以相对于时间元素执行操作。
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
然而, Series
和 DataFrame
还可以直接支持时间分量作为数据本身。
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]
Series
和 DataFrame
具有扩展的数据类型支持和功能,用于 datetime
, timedelta
和 Period
数据传递到这些构造函数时。 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')
Timestamp
和 Period
可以作为索引。一览表 Timestamp
和 Period
被自动强制到 DatetimeIndex
和 PeriodIndex
分别为。
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
,则它返回单个 Timestamp
。 Timestamp
也可以接受字符串输入,但不接受字符串解析选项,如 dayfirst
或 format
,所以使用 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
要组合成一个 Series
的 Timestamps
。
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组件的标准名称,包括:
要求:
year
,month
,day
可选:
hour
,minute
,second
,millisecond
,microsecond
,nanosecond
无效数据#
默认行为是 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支持将整型或浮点型纪元时间转换为 Timestamp
和 DatetimeIndex
。默认单位是纳秒,因为这就是 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 版更改.
构建一个 Timestamp
或 DatetimeIndex
带有纪元时间戳的 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)
生成时间戳的范围#
若要生成带有时间戳的索引,可以使用 DatetimeIndex
或 Index
构造函数并传入一组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_range
和 bdate_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_range
和 bdate_range
使用各种参数组合轻松生成一系列日期,例如 start
, end
, periods
,以及 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')
指定 start
, end
,以及 periods
将生成一系列均匀分布的日期 start
至 end
包括,与 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
属性生成一系列自定义频率日期。 weekmask
和 holidays
参数。只有在传递自定义频率字符串时才会使用这些参数。
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
具有相同频率的对象非常快(对于快速数据对齐很重要)。通过以下属性快速访问日期字段
year
,month
等。正则化函数,如
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
部分字符串取决于时间段的“准确性”,换句话说,时间间隔与索引分辨率的关系有多具体。相比之下,使用 Timestamp
或 datetime
对象是精确的,因为对象具有确切的含义。这些也遵循了 包括两个端点 。
这些 Timestamp
和 datetime
对象具有精确的 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'
)用于指定定义以下内容的频率:
日期是如何计时的
DatetimeIndex
使用时被隔开date_range()
一次爆炸的频率
Period
或PeriodIndex
这些频率字符串映射到 DateOffset
对象及其子类。一个 DateOffset
类似于 Timedelta
表示持续时间,但遵循特定的日历持续时间规则。例如,一个 Timedelta
天数将始终递增 datetimes
到24小时,而一个 DateOffset
天数将会增加 datetimes
到第二天的同一时间,无论一天是23小时、24小时还是25小时,因为夏令时。然而,所有的 DateOffset
一个小时或更小的子类 (Hour
, Minute
, Second
, Milli
, Micro
, Nano
)表现得像 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
关键字参数。可以在下面找到可用的日期偏移量和相关的频率字符串:
日期偏移量 |
频率串 |
描述 |
---|---|---|
无 |
通用偏移量类别,默认为绝对24小时 |
|
|
|
工作日(工作日) |
|
自定义工作日 |
|
|
一周,可选择固定在一周中的某一天 |
|
|
每月第y周的第x天 |
|
|
每月最后一周的第x天 |
|
|
日历月末 |
|
|
历月开始 |
|
|
营业月末 |
|
|
营业月开始 |
|
|
自定义业务月末 |
|
|
自定义业务月开始 |
|
|
15(或其他月份的某一天)和日历月份结束 |
|
|
15(或其他月份的某一天)和日历月份开始 |
|
|
日历季度末 |
|
|
日历季度开始 |
|
|
业务季度末 |
|
|
业务季开始 |
|
|
零售业(也就是52-53周)季度 |
|
|
日历年结束 |
|
|
历年开始 |
|
|
营业年度结束 |
|
|
业务年度开始 |
|
|
零售年(也就是52-53周) |
|
无 |
复活节假期 |
|
|
营业时间 |
|
|
自定义营业时间 |
|
|
绝对的一天 |
|
|
一小时 |
|
|
一分钟 |
|
|
一秒钟 |
|
|
一毫秒 |
|
|
1微秒 |
|
|
一纳秒 |
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
#
偏移量可以与 Series
或 DatetimeIndex
将偏移量应用于每个元素。
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)
自定义工作日#
这个 CDay
或 CustomBusinessDay
类提供了一个参数 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')
您还可以指定 start
和 end
按关键字计算的时间。参数必须是 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.rollforward
和 rollback
到非营业时间会导致下一个营业时间开始或前一天结束。与其他补偿不同的是, BusinessHour.rollforward
可能输出不同的结果 apply
顾名思义。
这是因为一天的营业时间结束等于第二天的营业时间开始。例如,在默认工作时间(9:00-17:00)下, 2014-08-01 17:00
和 2014-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
是一种混合物 BusinessHour
和 CustomBusinessDay
它允许您指定任意假期。 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')
您可以使用以下任一项支持的关键字参数 BusinessHour
和 CustomBusinessDay
。
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_date
和end_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_date
和 end_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_range
, bdate_range
,构造函数 DatetimeIndex
,以及大Pandas的其他各种与时间序列相关的功能。
锚定偏移语义#
对于固定到特定频率开始或结束的那些偏移量 (MonthEnd
, MonthBegin
, WeekEnd
等),以下规则适用于前滚和后滚。
什么时候 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_date
和 end_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
或添加到 datetime
或 Timestamp
对象。
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_date
和 end_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 作为返回对象的方法可用,包括 sum
, mean
, std
, sem
, max
, min
, median
, first
, last
, ohlc
:
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
警告
的缺省值 label
和 closed
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_method
是 None
,则中间值将填充为 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 API , groupby 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__
想要更多。
使用 origin
或 offset
调整垃圾箱起点的步骤#
1.1.0 新版功能.
根据时间序列起始点的一天的开始调整分组的仓位。这在频率是一天的倍数的情况下运行良好(例如 30D
)或将一天平均分配(如 90s
或 1min
)。这可能会与某些不符合此标准的频率产生不一致。要更改此行为,可以使用参数指定固定的时间戳 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
频率为每日或更高 (D
, H
, T
, S
, L
, U
, N
), offsets
和 timedelta
-如果结果可以具有相同的频率,则可以添加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]')
如果 start
或 end
是 Period
对象,则它们将用作 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()
并将其转换为 DatetimeIndex
至 PeriodIndex
喜欢 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 新版功能.
可以将日期和字符串传递给 Series
和 DataFrame
使用 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进行频率转换和重采样#
的频率 Period
和 PeriodIndex
可以通过 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
和/或 Series
的 Periods
来做计算。
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为在不同时区使用时间戳提供了丰富的支持 pytz
和 dateutil
库或 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
。你可以选择通过 pytz
或 dateutil
时区对象或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>
警告
当心库之间的转换。对于某些时区, pytz
和 dateutil
对区域有不同的定义。对于非常规时区来说,这比像这样的“标准”时区更成问题 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存储。来自时区感知的值 DatetimeIndex
或 Timestamp
将有自己的字段(日、小时、分钟等)已本地化到时区。但是,即使时间戳位于不同的时区,具有相同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 documentation 为 dateutil
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 withNaT
bool
:True
表示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 withNaT
'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]')