时间序列/日期功能# pandas 包含用于处理所有领域的时间序列数据的广泛功能和特性。使用 NumPydatetime64和timedelta64dtypes,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。 概念 标量类 数组类 熊猫数据类型 主要创作方法 日期时间 Timestamp DatetimeIndex datetime64[ns]或者datetime64[ns, tz] to_datetime或者date_range 时间增量 Timedelta TimedeltaIndex timedelta64[ns] to_timedelta或者timedelta_range 时间跨度 Period PeriodIndex period[freq] Period或者period_range 日期偏移量 DateOffset None None DateOffset 对于时间序列数据,通常在索引中表示时间分量,可以对时间元素执行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提供扩展的数据类型支持和功能。 但是数据将被存储为数据。datetimetimedeltaPeriodDateOffsetobject 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="ME", 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]: import datetime In [29]: pd.Timestamp(datetime.datetime(2012, 5, 1)) Out[29]: Timestamp('2012-05-01 00:00:00') In [30]: pd.Timestamp("2012-05-01") Out[30]: Timestamp('2012-05-01 00:00:00') In [31]: pd.Timestamp(2012, 5, 1) Out[31]: Timestamp('2012-05-01 00:00:00') 然而,在许多情况下,将变化变量等内容与时间跨度相关联更为自然。表示的跨度Period可以显式指定,也可以从日期时间字符串格式推断。 例如: In [32]: pd.Period("2011-01") Out[32]: Period('2011-01', 'M') In [33]: pd.Period("2012-05", freq="D") Out[33]: Period('2012-05-01', 'D') Timestamp并且Period可以作为索引。Timestamp和的列表 分别Period自动强制转换为DatetimeIndex 和PeriodIndex。 In [34]: dates = [ ....: pd.Timestamp("2012-05-01"), ....: pd.Timestamp("2012-05-02"), ....: pd.Timestamp("2012-05-03"), ....: ] ....: In [35]: ts = pd.Series(np.random.randn(3), dates) In [36]: type(ts.index) Out[36]: pandas.core.indexes.datetimes.DatetimeIndex In [37]: ts.index Out[37]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None) In [38]: ts Out[38]: 2012-05-01 0.469112 2012-05-02 -0.282863 2012-05-03 -1.509059 dtype: float64 In [39]: periods = [pd.Period("2012-01"), pd.Period("2012-02"), pd.Period("2012-03")] In [40]: ts = pd.Series(np.random.randn(3), periods) In [41]: type(ts.index) Out[41]: pandas.core.indexes.period.PeriodIndex In [42]: ts.index Out[42]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='period[M]') In [43]: ts Out[43]: 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函数。当传递 a 时Series,这将返回 a Series(具有相同的索引),而类似列表将转换为 a DatetimeIndex: In [44]: pd.to_datetime(pd.Series(["Jul 31, 2009", "Jan 10, 2010", None])) Out[44]: 0 2009-07-31 1 2010-01-10 2 NaT dtype: datetime64[ns] In [45]: pd.to_datetime(["2005/11/23", "2010/12/31"]) Out[45]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None) 如果您使用以日开头的日期(即欧洲风格),则可以传递该dayfirst标志: In [46]: pd.to_datetime(["04-01-2012 10:00"], dayfirst=True) Out[46]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None) In [47]: pd.to_datetime(["04-14-2012 10:00"], dayfirst=True) Out[47]: DatetimeIndex(['2012-04-14 10:00:00'], dtype='datetime64[ns]', freq=None) 警告 您在上面的示例中看到,这dayfirst并不严格。如果无法以日期为第一来解析日期,则会像第一天那样解析日期, dayfirst并且False还会引发警告。 如果将单个字符串传递给to_datetime,它将返回单个Timestamp。 Timestamp也可以接受字符串输入,但它不接受字符串解析选项,如dayfirst或format,因此to_datetime如果需要的话请使用。 In [48]: pd.to_datetime("2010/11/12") Out[48]: Timestamp('2010-11-12 00:00:00') In [49]: pd.Timestamp("2010/11/12") Out[49]: Timestamp('2010-11-12 00:00:00') 您也可以DatetimeIndex直接使用构造函数: In [50]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"]) Out[50]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq=None) 可以传递字符串 'infer' 以便将索引的频率设置为创建时推断的频率: In [51]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"], freq="infer") Out[51]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq='2D') 提供格式参数# 除了所需的日期时间字符串之外,format还可以传递一个参数以确保特定的解析。这也可能会大大加快转换速度。 In [52]: pd.to_datetime("2010/11/12", format="%Y/%m/%d") Out[52]: Timestamp('2010-11-12 00:00:00') In [53]: pd.to_datetime("12-11-2010 00:00", format="%d-%m-%Y %H:%M") Out[53]: Timestamp('2010-11-12 00:00:00') 有关指定选项时可用选项的更多信息format ,请参阅 Python日期时间文档。 从多个 DataFrame 列组装日期时间# 您还可以传递一个DataFrame整数或字符串列来组装成一个Seriesof Timestamps。 In [54]: df = pd.DataFrame( ....: {"year": [2015, 2016], "month": [2, 3], "day": [4, 5], "hour": [2, 3]} ....: ) ....: In [55]: pd.to_datetime(df) Out[55]: 0 2015-02-04 02:00:00 1 2016-03-05 03:00:00 dtype: datetime64[ns] 您只能传递需要组装的列。 In [56]: pd.to_datetime(df[["year", "month", "day"]]) Out[56]: 0 2015-02-04 1 2016-03-05 dtype: datetime64[ns] pd.to_datetime查找列名称中日期时间组件的标准名称,包括: 必需的:year,month,day 选修的:hour,minute,second,millisecond,microsecond,nanosecond 无效数据# 默认行为errors='raise'是在无法解析时引发: In [57]: pd.to_datetime(['2009/07/31', 'asd'], errors='raise') --------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[57], line 1 ----> 1 pd.to_datetime(['2009/07/31', 'asd'], errors='raise') File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:1099, in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache) 1097 result = _convert_and_box_cache(argc, cache_array) 1098 else: -> 1099 result = convert_listlike(argc, format) 1100 else: 1101 result = convert_listlike(np.array([arg]), format)[0] File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:433, in _convert_listlike_datetimes(arg, format, name, utc, unit, errors, dayfirst, yearfirst, exact) 431 # `format` could be inferred, or user didn't ask for mixed-format parsing. 432 if format is not None and format != "mixed": --> 433 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors) 435 result, tz_parsed = objects_to_datetime64( 436 arg, 437 dayfirst=dayfirst, (...) 441 allow_object=True, 442 ) 444 if tz_parsed is not None: 445 # We can take a shortcut since the datetime64 numpy array 446 # is in UTC File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:467, in _array_strptime_with_fallback(arg, name, utc, fmt, exact, errors) 456 def _array_strptime_with_fallback( 457 arg, 458 name, (...) 462 errors: str, 463 ) -> Index: 464 """ 465 Call array_strptime, with fallback behavior depending on 'errors'. 466 """ --> 467 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc) 468 if tz_out is not None: 469 unit = np.datetime_data(result.dtype)[0] File strptime.pyx:501, in pandas._libs.tslibs.strptime.array_strptime() File strptime.pyx:451, in pandas._libs.tslibs.strptime.array_strptime() File strptime.pyx:583, in pandas._libs.tslibs.strptime._parse_with_format() ValueError: time data "asd" doesn't match format "%Y/%m/%d", at position 1. You might want to try: - passing `format` if your strings have a consistent format; - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format; - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this. 将errors='coerce'无法解析的数据转换为NaT(不是时间): In [58]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce") Out[58]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None) 纪元时间戳# pandas 支持将整数或浮点纪元时间转换为Timestamp和 DatetimeIndex。默认单位是纳秒,因为这是Timestamp 对象内部存储的方式。然而,纪元通常存储在另一个unit 可以指定的纪元中。这些是从参数指定的起点计算的 origin。 In [59]: pd.to_datetime( ....: [1349720105, 1349806505, 1349892905, 1349979305, 1350065705], unit="s" ....: ) ....: Out[59]: 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 [60]: pd.to_datetime( ....: [1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500], ....: unit="ms", ....: ) ....: Out[60]: 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讨论的参数相同的字符串)。可用的单位列在 的文档中。pandas.to_datetime() 使用指定参数构造一个带有纪元时间戳的Timestampor将引发 ValueError。如果您在另一个时区的墙上时间中有纪元,则可以将纪元读取为时区朴素时间戳,然后本地化到适当的时区:DatetimeIndextz In [61]: pd.Timestamp(1262347200000000000).tz_localize("US/Pacific") Out[61]: Timestamp('2010-01-01 12:00:00-0800', tz='US/Pacific') In [62]: pd.DatetimeIndex([1262347200000000000]).tz_localize("US/Pacific") Out[62]: DatetimeIndex(['2010-01-01 12:00:00-08:00'], dtype='datetime64[ns, US/Pacific]', freq=None) 笔记 纪元时间将四舍五入到最接近的纳秒。 警告 浮点纪元时间的转换可能会导致不准确和意外的结果。 Python 浮点数的十进制精度约为 15 位。从浮点型到高精度型的转换过程中,舍入Timestamp是不可避免的。实现精确精度的唯一方法是使用固定宽度类型(例如 int64)。 In [63]: pd.to_datetime([1490195805.433, 1490195805.433502912], unit="s") Out[63]: DatetimeIndex(['2017-03-22 15:16:45.433000088', '2017-03-22 15:16:45.433502913'], dtype='datetime64[ns]', freq=None) In [64]: pd.to_datetime(1490195805433502912, unit="ns") Out[64]: Timestamp('2017-03-22 15:16:45.433502912') 也可以看看 使用原点参数 从时间戳到纪元# 反转上面的操作,即从 a 转换Timestamp为 'unix' 纪元: In [65]: stamps = pd.date_range("2012-10-08 18:15:05", periods=4, freq="D") In [66]: stamps Out[66]: 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') 我们减go纪元(UTC 时间 1970 年 1 月 1 日午夜),然后除以“单位”(1 秒)。 In [67]: (stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s") Out[67]: Index([1349720105, 1349806505, 1349892905, 1349979305], dtype='int64') 使用origin参数# 使用该origin参数,可以指定创建DatetimeIndex.例如,使用 1960-01-01 作为开始日期: In [68]: pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01")) Out[68]: DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], dtype='datetime64[ns]', freq=None) 默认设置为origin='unix',默认为。通常称为“unix 纪元”或 POSIX 时间。1970-01-01 00:00:00 In [69]: pd.to_datetime([1, 2, 3], unit="D") Out[69]: DatetimeIndex(['1970-01-02', '1970-01-03', '1970-01-04'], dtype='datetime64[ns]', freq=None) 生成时间戳范围# 要生成带有时间戳的索引,您可以使用DatetimeIndex或 Index构造函数并传入日期时间对象列表: In [70]: dates = [ ....: datetime.datetime(2012, 5, 1), ....: datetime.datetime(2012, 5, 2), ....: datetime.datetime(2012, 5, 3), ....: ] ....: # Note the frequency information In [71]: index = pd.DatetimeIndex(dates) In [72]: index Out[72]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None) # Automatically converted to DatetimeIndex In [73]: index = pd.Index(dates) In [74]: index Out[74]: 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 [75]: start = datetime.datetime(2011, 1, 1) In [76]: end = datetime.datetime(2012, 1, 1) In [77]: index = pd.date_range(start, end) In [78]: index Out[78]: 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 [79]: index = pd.bdate_range(start, end) In [80]: index Out[80]: 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可以利用各种频率别名: In [81]: pd.date_range(start, periods=1000, freq="ME") Out[81]: 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='ME') In [82]: pd.bdate_range(start, periods=250, freq="BQS") Out[82]: 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等参数的各种组合轻松生成一系列日期。严格包含开始日期和结束日期,因此不会生成指定日期之外的日期:startendperiodsfreq In [83]: pd.date_range(start, end, freq="BME") Out[83]: 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='BME') In [84]: pd.date_range(start, end, freq="W") Out[84]: 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 [85]: pd.bdate_range(end=end, periods=20) Out[85]: 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 [86]: pd.bdate_range(start=start, periods=20) Out[86]: 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 [87]: pd.date_range("2018-01-01", "2018-01-05", periods=5) Out[87]: DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05'], dtype='datetime64[ns]', freq=None) In [88]: pd.date_range("2018-01-01", "2018-01-05", periods=10) Out[88]: 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_rangeweekmask还可以使用和参数生成一系列自定义频率日期holidays。仅当传递自定义频率字符串时才会使用这些参数。 In [89]: weekmask = "Mon Wed Fri" In [90]: holidays = [datetime.datetime(2011, 1, 5), datetime.datetime(2011, 3, 14)] In [91]: pd.bdate_range(start, end, freq="C", weekmask=weekmask, holidays=holidays) Out[91]: 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 [92]: pd.bdate_range(start, end, freq="CBMS", weekmask=weekmask) Out[92]: 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') 也可以看看 定制工作日 时间戳限制# 时间戳表示的限制取决于所选的分辨率。对于纳秒分辨率,可以使用 64 位整数表示的时间跨度限制为大约 584 年: In [93]: pd.Timestamp.min Out[93]: Timestamp('1677-09-21 00:12:43.145224193') In [94]: pd.Timestamp.max Out[94]: Timestamp('2262-04-11 23:47:16.854775807') 当选择第二分辨率时,可用范围将增长至.不同分辨率可以通过 进行相互转换。+/- 2.9e11 yearsas_unit 也可以看看 代表越界范围 索引# 主要用途之一DatetimeIndex是作为 pandas 对象的索引。该类DatetimeIndex包含许多与时间序列相关的优化: 各种偏移量的大范围日期被预先计算并缓存在引擎盖下,以便非常快速地生成后续日期范围(只需抓取一个切片)。 shift使用pandas 对象上的方法进行快速移位。 具有相同频率的重叠DatetimeIndex对象的联合非常快(对于快速数据对齐很重要)。 year通过、month等属性快速访问日期字段。 正则化功能类似于snap非常快的asof逻辑。 DatetimeIndex对象具有常规对象的所有基本功能Index ,以及用于轻松进行频率处理的高级时间序列特定方法的大杂烩。 也可以看看 重新索引方法 笔记 虽然 pandas 不会强制您拥有排序的日期索引,但如果日期未排序,其中一些方法可能会出现意外或不正确的行为。 DatetimeIndex可以像常规索引一样使用,并提供所有智能功能,如选择、切片等。 In [95]: rng = pd.date_range(start, end, freq="BME") In [96]: ts = pd.Series(np.random.randn(len(rng)), index=rng) In [97]: ts.index Out[97]: 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='BME') In [98]: ts[:5].index Out[98]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29', '2011-05-31'], dtype='datetime64[ns]', freq='BME') In [99]: ts[::2].index Out[99]: DatetimeIndex(['2011-01-31', '2011-03-31', '2011-05-31', '2011-07-29', '2011-09-30', '2011-11-30'], dtype='datetime64[ns]', freq='2BME') 部分字符串索引# 解析为时间戳的日期和字符串可以作为索引参数传递: In [100]: ts["1/31/2011"] Out[100]: 0.11920871129693428 In [101]: ts[datetime.datetime(2011, 12, 25):] Out[101]: 2011-12-30 0.56702 Freq: BME, dtype: float64 In [102]: ts["10/31/2011":"12/31/2011"] Out[102]: 2011-10-31 0.271860 2011-11-30 -0.424972 2011-12-30 0.567020 Freq: BME, dtype: float64 为了方便访问更长的时间序列,您还可以将年份或年月作为字符串传递: In [103]: ts["2011"] Out[103]: 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: BME, dtype: float64 In [104]: ts["2011-6"] Out[104]: 2011-06-30 1.071804 Freq: BME, dtype: float64 这种类型的切片也适用于 aDataFrame和 a 。由于部分字符串选择是标签切片的一种形式,因此将DatetimeIndex包括端点。这将包括所包含日期的匹配时间: 警告 从 pandas 1.2.0 开始,不推荐使用getitem (例如)使用单个DataFrame字符串对行进行索引(考虑到它是索引行还是选择列),并将在未来版本中删除。仍然支持(eg )的等效项。frame[dtstring].locframe.loc[dtstring] In [105]: dft = pd.DataFrame( .....: np.random.randn(100000, 1), .....: columns=["A"], .....: index=pd.date_range("20130101", periods=100000, freq="min"), .....: ) .....: In [106]: dft 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.loc["2013"] 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-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 [108]: dft["2013-1":"2013-2"] 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"] 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-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 [110]: dft["2013-1":"2013-2-28 00:00:00"] Out[110]: 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 [111]: dft["2013-1-15":"2013-1-15 12:30:00"] Out[111]: 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部分字符串索引也适用于 aDataFrame和 a MultiIndex: In [112]: 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 [113]: dft2 Out[113]: 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 [114]: dft2.loc["2013-01-05"] Out[114]: 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 [115]: idx = pd.IndexSlice In [116]: dft2 = dft2.swaplevel(0, 1).sort_index() In [117]: dft2.loc[idx[:, "2013-01-05"], :] Out[117]: 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 使用字符串索引进行切片也遵循 UTC 偏移量。 In [118]: df = pd.DataFrame([0], index=pd.DatetimeIndex(["2019-01-01"], tz="US/Pacific")) In [119]: df Out[119]: 0 2019-01-01 00:00:00-08:00 0 In [120]: df["2019-01-01 12:00:00+04:00":"2019-01-01 13:00:00+04:00"] Out[120]: 0 2019-01-01 00:00:00-08:00 0 切片与精确匹配# 用作索引参数的同一字符串可以被视为切片或完全匹配,具体取决于索引的分辨率。如果字符串不如索引准确,它将被视为切片,否则被视为完全匹配。 考虑一个Series具有微小分辨率索引的对象: In [121]: 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 [122]: series_minute.index.resolution Out[122]: 'minute' 精确度低于一分钟的时间戳字符串会给出一个Series对象。 In [123]: series_minute["2011-12-31 23"] Out[123]: 2011-12-31 23:59:00 1 dtype: int64 具有分钟分辨率(或更准确)的时间戳字符串给出一个标量,即它不会转换为切片。 In [124]: series_minute["2011-12-31 23:59"] Out[124]: 1 In [125]: series_minute["2011-12-31 23:59:00"] Out[125]: 1 如果索引分辨率为秒,则精确到分钟的时间戳给出 Series. In [126]: 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 [127]: series_second.index.resolution Out[127]: 'second' In [128]: series_second["2011-12-31 23:59"] Out[128]: 2011-12-31 23:59:59 1 dtype: int64 如果时间戳字符串被视为一个切片,它也可以DataFrame用于索引。.loc[] In [129]: dft_minute = pd.DataFrame( .....: {"a": [1, 2, 3], "b": [4, 5, 6]}, index=series_minute.index .....: ) .....: In [130]: dft_minute.loc["2011-12-31 23"] Out[130]: a b 2011-12-31 23:59:00 1 4 警告 但是,如果字符串被视为完全匹配,则DataFrames中的选择[]将按列而不是按行,请参阅索引基础知识。例如,将引发as具有与索引相同的分辨率,并且不存在具有此类名称的列:dft_minute['2011-12-31 23:59']KeyError'2012-12-31 23:59' 为了始终具有明确的选择,无论该行被视为切片还是单个选择,请使用.loc。 In [131]: dft_minute.loc["2011-12-31 23:59"] Out[131]: a 1 b 4 Name: 2011-12-31 23:59:00, dtype: int64 另请注意,DatetimeIndex分辨率不能低于天的精确度。 In [132]: series_monthly = pd.Series( .....: [1, 2, 3], pd.DatetimeIndex(["2011-12", "2012-01", "2012-02"]) .....: ) .....: In [133]: series_monthly.index.resolution Out[133]: 'day' In [134]: series_monthly["2011-12"] # returns Series Out[134]: 2011-12-01 1 dtype: int64 精确索引# 正如上一节所讨论的,使用DatetimeIndex部分字符串对 a 进行索引取决于周期的“准确性”,换句话说,间隔与索引分辨率的关系有多具体。相反,使用Timestamp或datetime对象进行索引是精确的,因为对象具有精确的含义。这些也遵循包括两个端点的语义。 这些Timestamp和datetime对象具有精确的和,即使它们没有明确指定(它们是)。hours, minutes,seconds0 In [135]: dft[datetime.datetime(2013, 1, 1): datetime.datetime(2013, 2, 28)] Out[135]: 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 [136]: dft[ .....: datetime.datetime(2013, 1, 1, 10, 12, 0): datetime.datetime( .....: 2013, 2, 28, 10, 12, 0 .....: ) .....: ] .....: Out[136]: 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] 截断和花式索引# truncate()提供了类似于切片的便利功能。请注意,truncate假设 a 中任何未指定的日期组件的值为 0,DatetimeIndex这与返回任何部分匹配日期的切片相反: In [137]: rng2 = pd.date_range("2011-01-01", "2012-01-01", freq="W") In [138]: ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2) In [139]: ts2.truncate(before="2011-11", after="2011-12") Out[139]: 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 [140]: ts2["2011-11":"2011-12"] Out[140]: 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 [141]: ts2.iloc[[0, 2, 6]].index Out[141]: DatetimeIndex(['2011-01-02', '2011-01-16', '2011-02-13'], dtype='datetime64[ns]', freq=None) 时间/日期组件# 有多个时间/日期属性可供访问,Timestamp或者可以从时间戳集合(例如DatetimeIndex. 财产 描述 年 日期时间的年份 月 日期时间的月份 天 日期时间的天数 小时 日期时间的小时 分钟 日期时间的分钟数 第二 日期时间的秒数 微秒 日期时间的微秒 纳秒 日期时间的纳秒 日期 返回datetime.date(不包含时区信息) 时间 返回datetime.time(不包含时区信息) 泰梅茨 返回 datetime.time 作为带有时区信息的本地时间 一年中的某一天 一年中的第几天 年中的某一天 一年中的第几天 一年中的一周 一年中的第几周 星期 一年中的第几周 星期几 一周中的第几天,星期一=0,星期日=6 星期几 一周中的第几天,星期一=0,星期日=6 工作日 一周中的第几天,星期一=0,星期日=6 四分之一 日期的季度:一月至三月 = 1,四月至六月 = 2,依此类推。 月内天数 日期时间所在月份的天数 is_month_start 逻辑指示是否为每月的第一天(由频率定义) is_month_end 逻辑指示是否为每月的最后一天(由频率定义) is_quarter_start 逻辑指示是否为季度的第一天(由频率定义) is_quarter_end 逻辑指示是否为季度的最后一天(由频率定义) is_year_start 逻辑指示是否为一年的第一天(由频率定义) is_year_end 逻辑指示是否是一年的最后一天(由频率定义) is_leap_year 逻辑指示日期是否属于闰年 此外,如果您有Series类似日期时间的值,那么您可以通过访问器访问这些属性,如.dt 访问器.dt部分中详细介绍的。 您可以从 ISO 8601 标准获取 ISO 年份的年、周和日组成部分: In [142]: idx = pd.date_range(start="2019-12-29", freq="D", periods=4) In [143]: idx.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 In [144]: idx.to_series().dt.isocalendar() Out[144]: 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对象及其子类。 A与表示时间持续时间的DateOffset a 类似,但遵循特定的日历持续时间规则。Timedelta例如,Timedelta一天将始终增加datetimes24 小时,而由于夏令时,无论一天表示 23、24 还是 25 小时,DateOffset一天都会增加到第二天的同一时间。datetimes然而,所有DateOffset一小时或更小的子类(Hour, Minute, Second, Milli, Micro, Nano)都表现得像 Timedelta并尊重绝对时间。 基本DateOffset行为类似于dateutil.relativedelta(relativedelta 文档),将日期时间移动指定的相应日历持续时间。可以使用算术运算符 ( +) 来执行移位。 # This particular day contains a day light savings time transition In [145]: ts = pd.Timestamp("2016-10-30 00:00:00", tz="Europe/Helsinki") # Respects absolute time In [146]: ts + pd.Timedelta(days=1) Out[146]: Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki') # Respects calendar time In [147]: ts + pd.DateOffset(days=1) Out[147]: Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki') In [148]: friday = pd.Timestamp("2018-01-05") In [149]: friday.day_name() Out[149]: 'Friday' # Add 2 business days (Friday --> Tuesday) In [150]: two_business_days = 2 * pd.offsets.BDay() In [151]: friday + two_business_days Out[151]: Timestamp('2018-01-09 00:00:00') In [152]: (friday + two_business_days).day_name() Out[152]: 'Tuesday' 大多数DateOffsets都有关联的频率字符串或偏移别名,可以传递到freq关键字参数中。可用的日期偏移量和相关的频率字符串可以在下面找到: 日期偏移量 频率串 描述 DateOffset 没有任何 通用偏移量类,默认为绝对 24 小时 BDay或者BusinessDay 'B' 工作日(工作日) CDay或者CustomBusinessDay 'C' 定制工作日 Week 'W' 一周,可选择固定在一周中的某一天 WeekOfMonth 'WOM' 每月第 y 周的第 x 天 LastWeekOfMonth 'LWOM' 每月最后一周的第 x 天 MonthEnd 'ME' 日历月末 MonthBegin 'MS' 日历月开始 BMonthEnd或者BusinessMonthEnd 'BME' 营业月末 BMonthBegin或者BusinessMonthBegin 'BMS' 营业月开始 CBMonthEnd或者CustomBusinessMonthEnd 'CBME' 自定义业务月末 CBMonthBegin或者CustomBusinessMonthBegin 'CBMS' 定制业务月开始 SemiMonthEnd 'SME' 15 日(或其他日期)和日历月末 SemiMonthBegin 'SMS' 15 日(或其他日期)和日历月开始 QuarterEnd 'QE' 日历季度末 QuarterBegin 'QS' 日历季度开始 BQuarterEnd 'BQE 业务季度末 BQuarterBegin 'BQS' 业务季度开始 FY5253Quarter 'REQ' 零售(又名 52-53 周)季度 YearEnd 'YE' 日历年末 YearBegin 'YS'或者'BYS' 日历年开始 BYearEnd 'BYE' 营业年终 BYearBegin 'BYS' 营业年度开始 FY5253 'RE' 零售(又名 52-53 周)年 Easter 没有任何 复活节假期 BusinessHour 'bh' 营业时间 CustomBusinessHour 'cbh' 自定义营业时间 Day 'D' 绝对的一天 Hour 'h' 一小时 Minute 'min' 等一下 Second 's' 一秒 Milli 'ms' 一毫秒 Micro 'us' 一微秒 Nano 'ns' 一纳秒 DateOffsets另外还具有rollforward()用于rollback() 将日期分别向前或向后移动到相对于偏移量的有效偏移日期的方法。例如,业务抵消会将周末(周六和周日)的日期提前至周一,因为业务抵消在工作日运行。 In [153]: ts = pd.Timestamp("2018-01-06 00:00:00") In [154]: ts.day_name() Out[154]: 'Saturday' # BusinessHour's valid offset dates are Monday through Friday In [155]: offset = pd.offsets.BusinessHour(start="09:00") # Bring the date to the closest offset date (Monday) In [156]: offset.rollforward(ts) Out[156]: Timestamp('2018-01-08 09:00:00') # Date is brought to the closest offset date first and then the hour is added In [157]: ts + offset Out[157]: Timestamp('2018-01-08 10:00:00') 这些操作默认保存时间(小时、分钟等)信息。要将时间重置为午夜,请normalize()在应用操作之前或之后使用(取决于您是否希望在操作中包含时间信息)。 In [158]: ts = pd.Timestamp("2014-01-01 09:00") In [159]: day = pd.offsets.Day() In [160]: day + ts Out[160]: Timestamp('2014-01-02 09:00:00') In [161]: (day + ts).normalize() Out[161]: Timestamp('2014-01-02 00:00:00') In [162]: ts = pd.Timestamp("2014-01-01 22:00") In [163]: hour = pd.offsets.Hour() In [164]: hour + ts Out[164]: Timestamp('2014-01-01 23:00:00') In [165]: (hour + ts).normalize() Out[165]: Timestamp('2014-01-01 00:00:00') In [166]: (hour + pd.Timestamp("2014-01-01 23:30")).normalize() Out[166]: Timestamp('2014-01-02 00:00:00') 参数偏移# 某些偏移在创建时可以“参数化”以产生不同的行为。例如,Week生成每周数据的偏移量接受一个 weekday参数,该参数导致生成的日期始终位于一周中的特定日期: In [167]: d = datetime.datetime(2008, 8, 18, 9, 0) In [168]: d Out[168]: datetime.datetime(2008, 8, 18, 9, 0) In [169]: d + pd.offsets.Week() Out[169]: Timestamp('2008-08-25 09:00:00') In [170]: d + pd.offsets.Week(weekday=4) Out[170]: Timestamp('2008-08-22 09:00:00') In [171]: (d + pd.offsets.Week(weekday=4)).weekday() Out[171]: 4 In [172]: d - pd.offsets.Week() Out[172]: Timestamp('2008-08-11 09:00:00') 该normalize选项对加法和减法有效。 In [173]: d + pd.offsets.Week(normalize=True) Out[173]: Timestamp('2008-08-25 00:00:00') In [174]: d - pd.offsets.Week(normalize=True) Out[174]: Timestamp('2008-08-11 00:00:00') 另一个例子是YearEnd用特定的结束月份进行参数化: In [175]: d + pd.offsets.YearEnd() Out[175]: Timestamp('2008-12-31 09:00:00') In [176]: d + pd.offsets.YearEnd(month=6) Out[176]: Timestamp('2009-06-30 09:00:00') 使用带有Series/ DatetimeIndex# 的偏移量 Series偏移量可以与 a或一起使用DatetimeIndex,将偏移量应用于每个元素。 In [177]: rng = pd.date_range("2012-01-01", "2012-01-03") In [178]: s = pd.Series(rng) In [179]: rng Out[179]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D') In [180]: rng + pd.DateOffset(months=2) Out[180]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq=None) In [181]: s + pd.DateOffset(months=2) Out[181]: 0 2012-03-01 1 2012-03-02 2 2012-03-03 dtype: datetime64[ns] In [182]: s - pd.DateOffset(months=2) Out[182]: 0 2011-11-01 1 2011-11-02 2 2011-11-03 dtype: datetime64[ns] 如果偏移类直接映射到Timedelta( Day, Hour, Minute, Second, Micro, Milli, Nano) 它可以像 a 一样使用Timedelta- 有关更多示例,请参阅Timedelta 部分。 In [183]: s - pd.offsets.Day(2) Out[183]: 0 2011-12-30 1 2011-12-31 2 2012-01-01 dtype: datetime64[ns] In [184]: td = s - pd.Series(pd.date_range("2011-12-29", "2011-12-31")) In [185]: td Out[185]: 0 3 days 1 3 days 2 3 days dtype: timedelta64[ns] In [186]: td + pd.offsets.Minute(15) Out[186]: 0 3 days 00:15:00 1 3 days 00:15:00 2 3 days 00:15:00 dtype: timedelta64[ns] 请注意,某些偏移量(例如BQuarterEnd)没有矢量化实现。它们仍然可以使用,但计算速度可能会明显变慢,并且会显示PerformanceWarning In [187]: rng + pd.offsets.BQuarterEnd() Out[187]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None) 自定义工作日# CDayor类CustomBusinessDay提供了一个参数 BusinessDay类,可用于创建考虑当地节假日和当地周末惯例的自定义工作日日历。 作为一个有趣的例子,让我们看看埃及,那里有周五至周六的周末。 In [188]: weekmask_egypt = "Sun Mon Tue Wed Thu" # They also observe International Workers' Day so let's # add that for a couple of years In [189]: holidays = [ .....: "2012-05-01", .....: datetime.datetime(2013, 5, 1), .....: np.datetime64("2014-05-01"), .....: ] .....: In [190]: bday_egypt = pd.offsets.CustomBusinessDay( .....: holidays=holidays, .....: weekmask=weekmask_egypt, .....: ) .....: In [191]: dt = datetime.datetime(2013, 4, 30) In [192]: dt + 2 * bday_egypt Out[192]: Timestamp('2013-05-05 00:00:00') 让我们映射到工作日名称: In [193]: dts = pd.date_range(dt, periods=5, freq=bday_egypt) In [194]: pd.Series(dts.weekday, dts).map(pd.Series("Mon Tue Wed Thu Fri Sat Sun".split())) Out[194]: 2013-04-30 Tue 2013-05-02 Thu 2013-05-05 Sun 2013-05-06 Mon 2013-05-07 Tue Freq: C, dtype: object 假日日历可用于提供假日列表。有关详细信息,请参阅 节假日日历部分。 In [195]: from pandas.tseries.holiday import USFederalHolidayCalendar In [196]: bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar()) # Friday before MLK Day In [197]: dt = datetime.datetime(2014, 1, 17) # Tuesday after MLK Day (Monday is skipped because it's a holiday) In [198]: dt + bday_us Out[198]: Timestamp('2014-01-21 00:00:00') 可以按照通常的方式定义遵守特定假期日历的每月偏移量。 In [199]: bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar()) # Skip new years In [200]: dt = datetime.datetime(2013, 12, 17) In [201]: dt + bmth_us Out[201]: Timestamp('2014-01-02 00:00:00') # Define date index with custom offset In [202]: pd.date_range(start="20100101", end="20120101", freq=bmth_us) Out[202]: 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如果目标Timestamp不在工作时间,请移至下一个工作时间,然后增加它。如果结果超过营业时间结束,则剩余时间将添加到下一个工作日。 In [203]: bh = pd.offsets.BusinessHour() In [204]: bh Out[204]: <BusinessHour: bh=09:00-17:00> # 2014-08-01 is Friday In [205]: pd.Timestamp("2014-08-01 10:00").weekday() Out[205]: 4 In [206]: pd.Timestamp("2014-08-01 10:00") + bh Out[206]: Timestamp('2014-08-01 11:00:00') # Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh In [207]: pd.Timestamp("2014-08-01 08:00") + bh Out[207]: Timestamp('2014-08-01 10:00:00') # If the results is on the end time, move to the next business day In [208]: pd.Timestamp("2014-08-01 16:00") + bh Out[208]: Timestamp('2014-08-04 09:00:00') # Remainings are added to the next day In [209]: pd.Timestamp("2014-08-01 16:30") + bh Out[209]: Timestamp('2014-08-04 09:30:00') # Adding 2 business hours In [210]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(2) Out[210]: Timestamp('2014-08-01 12:00:00') # Subtracting 3 business hours In [211]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(-3) Out[211]: Timestamp('2014-07-31 15:00:00') 您还可以通过关键字指定时间start。end参数必须是str带有hour:minute表示或datetime.time 实例的。指定秒、微秒和纳秒作为营业时间会产生ValueError. In [212]: bh = pd.offsets.BusinessHour(start="11:00", end=datetime.time(20, 0)) In [213]: bh Out[213]: <BusinessHour: bh=11:00-20:00> In [214]: pd.Timestamp("2014-08-01 13:00") + bh Out[214]: Timestamp('2014-08-01 14:00:00') In [215]: pd.Timestamp("2014-08-01 09:00") + bh Out[215]: Timestamp('2014-08-01 12:00:00') In [216]: pd.Timestamp("2014-08-01 18:00") + bh Out[216]: Timestamp('2014-08-01 19:00:00') 经过的start时间晚于end午夜营业时间。在这种情况下,营业时间超过午夜并重叠到第二天。有效营业时间是根据是否从 valid 开始来区分的BusinessDay。 In [217]: bh = pd.offsets.BusinessHour(start="17:00", end="09:00") In [218]: bh Out[218]: <BusinessHour: bh=17:00-09:00> In [219]: pd.Timestamp("2014-08-01 17:00") + bh Out[219]: Timestamp('2014-08-01 18:00:00') In [220]: pd.Timestamp("2014-08-01 23:00") + bh Out[220]: Timestamp('2014-08-02 00:00:00') # Although 2014-08-02 is Saturday, # it is valid because it starts from 08-01 (Friday). In [221]: pd.Timestamp("2014-08-02 04:00") + bh Out[221]: 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 [222]: pd.Timestamp("2014-08-04 04:00") + bh Out[222]: Timestamp('2014-08-04 18:00:00') 应用BusinessHour.rollforward和rollback到非工作时间会导致下一个工作时间开始或前一天结束。与其他偏移不同,根据定义BusinessHour.rollforward 可能会输出不同的结果。apply 这是因为一天的营业时间结束等于第二天的营业时间开始。例如,在默认营业时间(9:00 - 17:00)下,和 之间没有间隙(0 分钟)。2014-08-01 17:002014-08-04 09:00 # This adjusts a Timestamp to business hour edge In [223]: pd.offsets.BusinessHour().rollback(pd.Timestamp("2014-08-02 15:00")) Out[223]: Timestamp('2014-08-01 17:00:00') In [224]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02 15:00")) Out[224]: 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 [225]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02 15:00") Out[225]: Timestamp('2014-08-04 10:00:00') # BusinessDay results (for reference) In [226]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02")) Out[226]: 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 [227]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02") Out[227]: Timestamp('2014-08-04 10:00:00') BusinessHour将星期六和星期日视为假期。要使用任意假期,您可以使用CustomBusinessHour偏移量,如下小节所述。 自定义营业时间# 它是和CustomBusinessHour的混合,它允许您指定任意假期。工作原理与 相同,只是它会跳过指定的自定义假期。BusinessHourCustomBusinessDayCustomBusinessHourBusinessHour In [228]: from pandas.tseries.holiday import USFederalHolidayCalendar In [229]: bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar()) # Friday before MLK Day In [230]: dt = datetime.datetime(2014, 1, 17, 15) In [231]: dt + bhour_us Out[231]: Timestamp('2014-01-17 16:00:00') # Tuesday after MLK Day (Monday is skipped because it's a holiday) In [232]: dt + bhour_us * 2 Out[232]: Timestamp('2014-01-21 09:00:00') BusinessHour您可以使用和支持的关键字参数CustomBusinessDay。 In [233]: 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 [234]: dt + bhour_mon * 2 Out[234]: Timestamp('2014-01-21 10:00:00') 偏移别名# 为有用的常见时间序列频率提供了许多字符串别名。我们将这些别名称为偏移别名。 别名 描述 乙 工作日频率 C 自定义工作日频率 D 日历日频率 瓦 每周频率 我 月末频率 中小企业 半月结束频率(15 日和月底) 生物医学工程 营业月结束频率 CBME 自定义业务月结束频率 多发性硬化症 月份开始频率 短信 半月开始频率(1 日和 15 日) 电池管理系统 营业月开始频率 CBMS 自定义业务月开始频率 量化宽松 季末频率 商业质量评估 业务季末频率 质量标准 季度开始频率 BQS 业务季度开始频率 叶 年终频率 再见 业务年终频率 YS 年开始频率 拜斯 营业年度开始频率 H 每小时频率 乙肝 营业时间频率 CBH 自定义营业时间频率 分钟 每分钟频率 s 其次频率 多发性硬化症 毫秒 我们 微秒 纳秒 纳秒 自版本 2.2.0 起已弃用:别名H, BH, CBH, T, S, L, U, 和N 已弃用,取而代之的是别名h, bh, cbh, min, s, ms, us, 和ns。 笔记 使用上面的偏移量别名时,应该注意的是,date_range()、bdate_range()、 等函数只会返回start_date和 定义的时间间隔内的时间戳end_date。如果start_date与频率不对应,则返回的时间戳将从下一个有效时间戳开始,同样,对于 end_date,返回的时间戳将停止在上一个有效时间戳。 例如,对于 offset MS,如果start_date不是该月的第一天,则返回的时间戳将从下个月的第一天开始。如果end_date不是某月的第一天,则最后返回的时间戳将为相应月份的第一天。 In [235]: dates_lst_1 = pd.date_range("2020-01-06", "2020-04-03", freq="MS") In [236]: dates_lst_1 Out[236]: DatetimeIndex(['2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS') In [237]: dates_lst_2 = pd.date_range("2020-01-01", "2020-04-01", freq="MS") In [238]: dates_lst_2 Out[238]: 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) 句号别名# 为有用的常见时间序列频率提供了许多字符串别名。我们将这些别名称为句点别名。 别名 描述 乙 工作日频率 D 日历日频率 瓦 每周频率 中号 每月频率 问 每季度频率 是 每年频率 H 每小时频率 分钟 每分钟频率 s 其次频率 多发性硬化症 毫秒 我们 微秒 纳秒 纳秒 自版本 2.2.0 起已弃用:别名A, H, T, S, , L,U和N已弃用,取而代之的是别名 Y, h, min, s, ms, us, 和ns。 组合别名# 正如我们之前所看到的,别名和偏移实例在大多数函数中都是可替换的: In [239]: pd.date_range(start, periods=5, freq="B") 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=5, freq=pd.offsets.BDay()) Out[240]: DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06', '2011-01-07'], dtype='datetime64[ns]', freq='B') 您可以将日内和日内偏移量组合在一起: In [241]: pd.date_range(start, periods=10, freq="2h20min") Out[241]: 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='140min') In [242]: pd.date_range(start, periods=10, freq="1D10us") Out[242]: 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='86400000010us') 锚定偏移# 对于某些频率,您可以指定锚定后缀: 别名 描述 威盛 每周频率(星期日)。与“W”相同 W-MON 每周频率(周一) 周末TUE 每周频率(周二) W-WED 每周频率(周三) 周四 每周频率(周四) W-FRI 每周频率(周五) 卫星卫星 每周频率(周六) (B)Q(E)(S)-DEC 季度频率,年度结束于 12 月。与“量化宽松”相同 (B)Q(E)(S)-JAN 季度频率,年度结束于一月 (B)Q(E)(S)-FEB 季度频率,年度结束于二月 (B)Q(E)(S)-三月 季度频率,年度结束于三月 (B)Q(E)(S)-APR 季度频率,年度结束于四月 (B)Q(E)(S)-五月 季度频率,年度结束于五月 (B)Q(E)(S)-JUN 季度频率,年度结束于六月 (B)Q(E)(S)-7 月 季度频率,年度结束于七月 (B)Q(E)(S)-8 月 季度频率,年度结束于八月 (B)Q(E)(S)-SEP 季度频率,年度结束于九月 (B)Q(E)(S)-OCT 季度频率,年度结束于 10 月 (B)Q(E)(S)-NOV 季度频率,年度结束于 11 月 (B)Y(E)(S)-DEC 年度频率,锚定于 12 月底。与“YE”相同 (B)Y(E)(S)-JAN 年度频率,固定在一月底 (B)Y(E)(S)-FEB 年度频率,固定于二月底 (B)Y(E)(S)-MAR 年度频率,固定于三月底 (B)Y(E)(S)-APR 年度频率,固定于四月底 (B)Y(E)(S)-五月 年度频率,固定于五月底 (B)Y(E)(S)-JUN 年度频率,固定于六月底 (B)Y(E)(S)-7 月 年度频率,固定于七月底 (B)Y(E)(S)-8 月 年度频率,锚定于八月底 (B)Y(E)(S)-SEP 年度频率,固定于九月底 (B)Y(E)(S)-OCT 年度频率,固定于 10 月底 (B)Y(E)(S)-NOV 年度频率,固定于 11 月底 这些可以用作date_range、bdate_range、 的构造函数DatetimeIndex以及 pandas 中各种其他与时间序列相关的函数的参数。 锚定偏移语义# 对于那些锚定到特定频率的开始或结束的偏移量(MonthEnd、MonthBegin、WeekEnd等),以下规则适用于向前和向后滚动。 当n不为0时,如果给定日期不在锚点上,它将捕捉到下一个(上一个)锚点,并|n|-1向前或向后移动额外的步骤。 In [243]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=1) Out[243]: Timestamp('2014-02-01 00:00:00') In [244]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=1) Out[244]: Timestamp('2014-01-31 00:00:00') In [245]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=1) Out[245]: Timestamp('2014-01-01 00:00:00') In [246]: pd.Timestamp("2014-01-02") - pd.offsets.MonthEnd(n=1) Out[246]: Timestamp('2013-12-31 00:00:00') In [247]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=4) Out[247]: Timestamp('2014-05-01 00:00:00') In [248]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=4) Out[248]: Timestamp('2013-10-01 00:00:00') 如果给定日期位于锚点上,则会|n|向前或向后移动点。 In [249]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=1) Out[249]: Timestamp('2014-02-01 00:00:00') In [250]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=1) Out[250]: Timestamp('2014-02-28 00:00:00') In [251]: pd.Timestamp("2014-01-01") - pd.offsets.MonthBegin(n=1) Out[251]: Timestamp('2013-12-01 00:00:00') In [252]: pd.Timestamp("2014-01-31") - pd.offsets.MonthEnd(n=1) Out[252]: Timestamp('2013-12-31 00:00:00') In [253]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=4) Out[253]: Timestamp('2014-05-01 00:00:00') In [254]: pd.Timestamp("2014-01-31") - pd.offsets.MonthBegin(n=4) Out[254]: Timestamp('2013-10-01 00:00:00') 对于 的情况n=0,如果日期在锚点上,则不会移动,否则向前滚动到下一个锚点。 In [255]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=0) Out[255]: Timestamp('2014-02-01 00:00:00') In [256]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=0) Out[256]: Timestamp('2014-01-31 00:00:00') In [257]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=0) Out[257]: Timestamp('2014-01-01 00:00:00') In [258]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=0) Out[258]: Timestamp('2014-01-31 00:00:00') 假期/假期日历# CustomBusinessDay假期和日历提供了一种简单的方法来定义假期规则,以便与需要一组预定义假期的其他分析一起使用或在其他分析中使用。该类AbstractHolidayCalendar提供了返回假期列表所需的所有方法,并且只rules需要在特定的假期日历类中定义即可。此外,start_date和end_date class 属性确定生成假期的日期范围。这些应该在AbstractHolidayCalendar类上覆盖,以使范围适用于所有日历子类。USFederalHolidayCalendar是唯一存在的日历,主要用作开发其他日历的示例。 对于在固定日期(例如,美国阵亡将士纪念日或 7 月 4 日)发生的假期,如果该假期是周末或其他不遵守的日期,则遵守规则会确定该假期的遵守时间。定义的遵守规则是: 规则 描述 最近的工作日 周六移至周五,周日移至周一 星期日至星期一 将周日移至下周一 下周一或周二 周六移至周一和周日/周一至周二 上周五 将周六和周日移至上周五” 下个星期一 将周六和周日移至下周一 如何定义假期和假期日历的示例: In [259]: from pandas.tseries.holiday import ( .....: Holiday, .....: USMemorialDay, .....: AbstractHolidayCalendar, .....: nearest_workday, .....: MO, .....: ) .....: In [260]: 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 [261]: cal = ExampleCalendar() In [262]: cal.holidays(datetime.datetime(2012, 1, 1), datetime.datetime(2012, 12, 31)) Out[262]: 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 [263]: pd.date_range( .....: start="7/1/2012", end="7/10/2012", freq=pd.offsets.CDay(calendar=cal) .....: ).to_pydatetime() .....: Out[263]: 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 [264]: offset = pd.offsets.CustomBusinessDay(calendar=cal) In [265]: datetime.datetime(2012, 5, 25) + offset Out[265]: Timestamp('2012-05-29 00:00:00') In [266]: datetime.datetime(2012, 7, 3) + offset Out[266]: Timestamp('2012-07-05 00:00:00') In [267]: datetime.datetime(2012, 7, 3) + 2 * offset Out[267]: Timestamp('2012-07-06 00:00:00') In [268]: datetime.datetime(2012, 7, 6) + offset Out[268]: Timestamp('2012-07-09 00:00:00') start_date范围由 的和end_date类属性定义AbstractHolidayCalendar。默认值如下所示。 In [269]: AbstractHolidayCalendar.start_date Out[269]: Timestamp('1970-01-01 00:00:00') In [270]: AbstractHolidayCalendar.end_date Out[270]: Timestamp('2200-12-31 00:00:00') 可以通过将属性设置为日期时间/时间戳/字符串来覆盖这些日期。 In [271]: AbstractHolidayCalendar.start_date = datetime.datetime(2012, 1, 1) In [272]: AbstractHolidayCalendar.end_date = datetime.datetime(2012, 12, 31) In [273]: cal.holidays() Out[273]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None) get_calendar每个日历类都可以使用返回假日类实例的函数通过名称进行访问。任何导入的日历类都将自动通过此功能可用。此外,还HolidayCalendarFactory 提供了一个简单的界面来创建日历或日历与附加规则的组合。 In [274]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, USLaborDay In [275]: cal = get_calendar("ExampleCalendar") In [276]: cal.rules Out[276]: [Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>), Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7ff27fdb0b80>), Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)] In [277]: new_cal = HolidayCalendarFactory("NewExampleCalendar", cal, USLaborDay) In [278]: new_cal.rules Out[278]: [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 0x7ff27fdb0b80>), Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)] 时间序列相关的实例方法# 移动/滞后# 人们可能希望在时间上前后移动或滞后时间序列中的值。其方法是shift(),它可用于所有 pandas 对象。 In [279]: ts = pd.Series(range(len(rng)), index=rng) In [280]: ts = ts[:5] In [281]: ts.shift(1) Out[281]: 2012-01-01 NaN 2012-01-02 0.0 2012-01-03 1.0 Freq: D, dtype: float64 该shift方法接受一个freq参数,该参数可以接受 DateOffset类或其他timedelta类似对象,也可以 接受偏移量别名。 当freq指定时,shift方法会更改索引中的所有日期,而不是更改数据和索引的对齐方式: In [282]: ts.shift(5, freq="D") Out[282]: 2012-01-06 0 2012-01-07 1 2012-01-08 2 Freq: D, dtype: int64 In [283]: ts.shift(5, freq=pd.offsets.BDay()) Out[283]: 2012-01-06 0 2012-01-09 1 2012-01-10 2 dtype: int64 In [284]: ts.shift(5, freq="BME") Out[284]: 2012-05-31 0 2012-05-31 1 2012-05-31 2 dtype: int64 请注意,当freq指定了when 时,前导条目不再是NaN,因为数据没有被重新对齐。 变频# 改变频率的主要功能是asfreq() 方法。对于 a 来说DatetimeIndex,这基本上只是一个薄而方便的包装器,围绕reindex()它生成 adate_range并调用reindex。 In [285]: dr = pd.date_range("1/1/2010", periods=3, freq=3 * pd.offsets.BDay()) In [286]: ts = pd.Series(np.random.randn(3), index=dr) In [287]: ts Out[287]: 2010-01-01 1.494522 2010-01-06 -0.778425 2010-01-11 -0.253355 Freq: 3B, dtype: float64 In [288]: ts.asfreq(pd.offsets.BDay()) Out[288]: 2010-01-01 1.494522 2010-01-04 NaN 2010-01-05 NaN 2010-01-06 -0.778425 2010-01-07 NaN 2010-01-08 NaN 2010-01-11 -0.253355 Freq: B, dtype: float64 asfreq提供了进一步的便利,因此您可以为频率转换后可能出现的任何间隙指定插值方法。 In [289]: ts.asfreq(pd.offsets.BDay(), method="pad") Out[289]: 2010-01-01 1.494522 2010-01-04 1.494522 2010-01-05 1.494522 2010-01-06 -0.778425 2010-01-07 -0.778425 2010-01-08 -0.778425 2010-01-11 -0.253355 Freq: B, dtype: float64 向前/向后填充# 与asfreq和reindexis相关fillna(),这记录在缺失数据部分中。 转换为 Python 日期时间# DatetimeIndexdatetime.datetime可以使用该方法转换为Python本机对象的数组 to_pydatetime。 重新采样# pandas具有简单、强大、高效的功能,可以在变频过程中执行重采样操作(例如将秒数数据转换为5分钟数据)。这在(但不限于)金融应用中极为常见。 resample()是一个基于时间的 groupby,后跟每个组的归约方法。有关一些高级策略,请参阅一些食谱示例。 该resample()方法可以直接从DataFrameGroupBy对象中使用,请参阅groupby 文档。 基本# In [290]: rng = pd.date_range("1/1/2012", periods=100, freq="s") In [291]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng) In [292]: ts.resample("5Min").sum() Out[292]: 2012-01-01 25103 Freq: 5min, dtype: int64 该resample功能非常灵活,允许您指定许多不同的参数来控制变频和重采样操作。 通过GroupBy提供的任何内置方法都可以用作返回对象的方法,包括sum, mean, std, sem, max, min, median, first, last, ohlc: In [293]: ts.resample("5Min").mean() Out[293]: 2012-01-01 251.03 Freq: 5min, dtype: float64 In [294]: ts.resample("5Min").ohlc() Out[294]: open high low close 2012-01-01 308 460 9 205 In [295]: ts.resample("5Min").max() Out[295]: 2012-01-01 460 Freq: 5min, dtype: int64 对于下采样,closed可以设置为“左”或“右”来指定间隔的哪一端是封闭的: In [296]: ts.resample("5Min", closed="right").mean() Out[296]: 2011-12-31 23:55:00 308.000000 2012-01-01 00:00:00 250.454545 Freq: 5min, dtype: float64 In [297]: ts.resample("5Min", closed="left").mean() Out[297]: 2012-01-01 251.03 Freq: 5min, dtype: float64 诸如此类的参数label用于操作结果标签。 label指定结果是用间隔的开始还是结束进行标记。 In [298]: ts.resample("5Min").mean() # by default label='left' Out[298]: 2012-01-01 251.03 Freq: 5min, dtype: float64 In [299]: ts.resample("5Min", label="left").mean() Out[299]: 2012-01-01 251.03 Freq: 5min, dtype: float64 警告 除“ME”、“YE”、“QE”、“BME”、“BYE”、“BQE”和“W”外,所有频率偏移的默认值为label“ closedleft ”,它们的默认值为“正确的'。 这可能会无意中导致向前查看,其中稍后时间的值被拉回到之前的时间,如以下频率示例所示BusinessDay: In [300]: s = pd.date_range("2000-01-01", "2000-01-05").to_series() In [301]: s.iloc[2] = pd.NaT In [302]: s.dt.day_name() Out[302]: 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 [303]: s.resample("B").last().dt.day_name() Out[303]: 1999-12-31 Sunday 2000-01-03 NaN 2000-01-04 Tuesday 2000-01-05 Wednesday Freq: B, dtype: object 请注意周日的值是如何拉回到上周五的。要获得将星期日的值推送到星期一的行为,请改用 In [304]: s.resample("B", label="right", closed="right").last().dt.day_name() Out[304]: 2000-01-03 Sunday 2000-01-04 Tuesday 2000-01-05 Wednesday 2000-01-06 NaN Freq: B, dtype: object 该axis参数可以设置为 0 或 1,并允许您对 的指定轴重新采样DataFrame。 kind可以设置为“timestamp”或“period”以将结果索引转换为时间戳和时间跨度表示形式。默认情况下resample 保留输入表示。 convention重采样周期数据时可以设置为“开始”或“结束”(详细信息如下)。它指定如何将低频周期转换为高频周期。 上采样# 对于上采样,您可以指定上采样的方式以及limit在创建的间隙上进行插值的参数: # from secondly to every 250 milliseconds In [305]: ts[:2].resample("250ms").asfreq() Out[305]: 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: 250ms, dtype: float64 In [306]: ts[:2].resample("250ms").ffill() Out[306]: 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: 250ms, dtype: int64 In [307]: ts[:2].resample("250ms").ffill(limit=2) Out[307]: 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: 250ms, dtype: float64 稀疏重采样# 稀疏时间序列是指相对于您要重新采样的时间量而言,您拥有的点要少得多的时间序列。单纯地对稀疏序列进行上采样可能会生成大量中间值。当您不想使用方法来填充这些值时,例如fill_methodis None,那么中间值将被填充NaN。 由于resample是基于时间的分组,因此以下是一种仅对不全部为 的组进行有效重新采样的方法NaN。 In [308]: rng = pd.date_range("2014-1-1", periods=100, freq="D") + pd.Timedelta("1s") In [309]: ts = pd.Series(range(100), index=rng) 如果我们想重新采样到该系列的整个范围: In [310]: ts.resample("3min").sum() Out[310]: 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: 3min, Length: 47521, dtype: int64 相反,我们只能对那些有点的组进行重新采样,如下所示: In [311]: from functools import partial In [312]: from pandas.tseries.frequencies import to_offset In [313]: def round(t, freq): .....: freq = to_offset(freq) .....: td = pd.Timedelta(freq) .....: return pd.Timestamp((t.value // td.value) * td.value) .....: In [314]: ts.groupby(partial(round, freq="3min")).sum() Out[314]: 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 聚合# 该resample()方法返回一个pandas.api.typing.Resampler实例。与聚合 API、groupby API和窗口 API类似, aResampler可以有选择地重新采样。 重新采样 a DataFrame,默认情况下将对具有相同函数的所有列进行操作。 In [315]: df = pd.DataFrame( .....: np.random.randn(1000, 3), .....: index=pd.date_range("1/1/2012", freq="s", periods=1000), .....: columns=["A", "B", "C"], .....: ) .....: In [316]: r = df.resample("3min") In [317]: r.mean() Out[317]: 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 [318]: r["A"].mean() Out[318]: 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: 3min, Name: A, dtype: float64 In [319]: r[["A", "B"]].mean() Out[319]: 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 您可以传递函数列表或字典来进行聚合,输出DataFrame: In [320]: r["A"].agg(["sum", "mean", "std"]) Out[320]: 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 在 resampled 上DataFrame,您可以传递要应用于每一列的函数列表,这会生成具有分层索引的聚合结果: In [321]: r.agg(["sum", "mean"]) Out[321]: A ... C sum mean ... sum mean 2012-01-01 00:00:00 -6.088060 -0.033823 ... -14.660515 -0.081447 2012-01-01 00:03:00 10.243678 0.056909 ... -4.377642 -0.024320 2012-01-01 00:06:00 -10.590584 -0.058837 ... -9.363825 -0.052021 2012-01-01 00:09:00 11.362228 0.063123 ... -11.975895 -0.066533 2012-01-01 00:12:00 33.541257 0.186340 ... 13.455299 0.074752 2012-01-01 00:15:00 -8.595393 -0.085954 ... -5.004580 -0.050046 [6 rows x 6 columns] 通过传递一个字典,aggregate您可以对 a 的列应用不同的聚合DataFrame: In [322]: r.agg({"A": "sum", "B": lambda x: np.std(x, ddof=1)}) 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", "B": "std"}) Out[323]: 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 [324]: r.agg({"A": ["sum", "std"], "B": ["mean", "std"]}) Out[324]: 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 如果 aDataFrame没有 datetimelike 索引,但您想根据框架中的 datetimelike 列重新采样,则可以将其传递给关键字 on。 In [325]: 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 [326]: df Out[326]: 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 [327]: df.resample("ME", on="date")[["a"]].sum() Out[327]: a date 2015-01-31 6 2015-02-28 4 同样,如果您想按 datetimelike 级别重新采样MultiIndex,则可以将其名称或位置传递给 level关键字。 In [328]: df.resample("ME", level="d")[["a"]].sum() Out[328]: a d 2015-01-31 6 2015-02-28 4 遍历组# 有了对象Resampler,遍历分组数据就非常自然,其功能类似于itertools.groupby(): In [329]: 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 [330]: resampled = small.resample("h") In [331]: 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调整 bin 的开始# 分组的箱根据时间序列起点当天的开始进行调整。这对于一天的倍数(如30D)或平均划分一天的频率(如90s或1min)非常有效。这可能会与某些不符合此标准的频率产生不一致。要更改此行为,您可以使用参数指定固定时间戳origin。 例如: In [332]: start, end = "2000-10-01 23:30:00", "2000-10-02 00:30:00" In [333]: middle = "2000-10-02 00:00:00" In [334]: rng = pd.date_range(start, end, freq="7min") In [335]: ts = pd.Series(np.arange(len(rng)) * 3, index=rng) In [336]: ts Out[336]: 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: 7min, dtype: int64 在这里我们可以看到,当使用origin默认值( )时,根据时间序列的开始,'start_day'之后的结果并不相同:'2000-10-02 00:00:00' In [337]: ts.resample("17min", origin="start_day").sum() Out[337]: 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: 17min, dtype: int64 In [338]: ts[middle:end].resample("17min", origin="start_day").sum() Out[338]: 2000-10-02 00:00:00 33 2000-10-02 00:17:00 45 Freq: 17min, dtype: int64 在这里我们可以看到,当设置origin为 时,根据时间序列的开始,'epoch'之后的结果是相同的:'2000-10-02 00:00:00' In [339]: ts.resample("17min", origin="epoch").sum() Out[339]: 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: 17min, dtype: int64 In [340]: ts[middle:end].resample("17min", origin="epoch").sum() Out[340]: 2000-10-01 23:52:00 15 2000-10-02 00:09:00 39 2000-10-02 00:26:00 24 Freq: 17min, dtype: int64 如果需要,您可以使用自定义时间戳origin: In [341]: ts.resample("17min", origin="2001-01-01").sum() Out[341]: 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: 17min, dtype: int64 In [342]: ts[middle:end].resample("17min", origin=pd.Timestamp("2001-01-01")).sum() Out[342]: 2000-10-02 00:04:00 54 2000-10-02 00:21:00 24 Freq: 17min, dtype: int64 offset如果需要,您可以使用将添加到默认值的 Timedelta来调整 bin origin。这两个例子对于这个时间序列是等效的: In [343]: ts.resample("17min", origin="start").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: 17min, dtype: int64 In [344]: ts.resample("17min", offset="23h30min").sum() Out[344]: 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: 17min, dtype: int64 请注意最后一个示例中'start'for的使用。origin在这种情况下,origin将设置为时间序列的第一个值。 向后重采样# 1.3.0 版本中的新增功能。 有时我们不需要调整 bin 的开头,而是需要修复 bin 的末尾以使用给定的 进行向后重新采样freq。默认情况下,向后重采样设置closed为'right',因为最后一个值应被视为最后一个 bin 的边缘点。 我们可以设置origin为'end'.特定索引的值Timestamp代表从当前Timestamp负值freq到当前Timestamp右收盘值的重新采样结果。 In [345]: ts.resample('17min', origin='end').sum() Out[345]: 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: 17min, dtype: int64 此外,与'start_day'选项相反,end_day是支持的。这会将原点设置为最大午夜的上限Timestamp。 In [346]: ts.resample('17min', origin='end_day').sum() Out[346]: 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: 17min, dtype: int64 自以下计算以来,上述结果用作最后一个 bin 的右边缘。2000-10-02 00:29:00 In [347]: ceil_mid = rng.max().ceil('D') In [348]: freq = pd.offsets.Minute(17) In [349]: bin_res = ceil_mid - freq * ((ceil_mid - rng.max()) // freq) In [350]: bin_res Out[350]: Timestamp('2000-10-02 00:29:00') 时间跨度表示# Period规则的时间间隔由pandas 中的对象表示,而Period对象序列则收集在 a 中PeriodIndex,可以使用便利函数创建period_range。 时期# APeriod代表一段时间(例如,一天、一个月、一个季度等)。您可以freq使用频率别名通过关键字指定跨度,如下所示。因为freq代表 的跨度Period,所以不能像“-3D”那样为负数。 In [351]: pd.Period("2012", freq="Y-DEC") Out[351]: Period('2012', 'Y-DEC') In [352]: pd.Period("2012-1-1", freq="D") Out[352]: Period('2012-01-01', 'D') In [353]: pd.Period("2012-1-1 19:00", freq="h") Out[353]: Period('2012-01-01 19:00', 'h') In [354]: pd.Period("2012-1-1 19:00", freq="5h") Out[354]: Period('2012-01-01 19:00', '5h') 从周期中添加和减go整数会将周期移动其自身的频率。Period不同freq(跨度)之间不允许进行算术运算。 In [355]: p = pd.Period("2012", freq="Y-DEC") In [356]: p + 1 Out[356]: Period('2013', 'Y-DEC') In [357]: p - 3 Out[357]: Period('2009', 'Y-DEC') In [358]: p = pd.Period("2012-01", freq="2M") In [359]: p + 2 Out[359]: Period('2012-05', '2M') In [360]: p - 1 Out[360]: Period('2011-11', '2M') In [361]: p == pd.Period("2012-01", freq="3M") Out[361]: False 如果Period频率为每日或更高(D、h、min、s、ms、us和ns),offsets并且timedelta如果结果可以具有相同的频率,则可以添加 -like 。否则,ValueError将会被提高。 In [362]: p = pd.Period("2014-07-01 09:00", freq="h") In [363]: p + pd.offsets.Hour(2) Out[363]: Period('2014-07-01 11:00', 'h') In [364]: p + datetime.timedelta(minutes=120) Out[364]: Period('2014-07-01 11:00', 'h') In [365]: p + np.timedelta64(7200, "s") Out[365]: Period('2014-07-01 11:00', 'h') In [366]: p + pd.offsets.Minute(5) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) File period.pyx:1824, in pandas._libs.tslibs.period._Period._add_timedeltalike_scalar() File timedeltas.pyx:278, in pandas._libs.tslibs.timedeltas.delta_to_nanoseconds() File np_datetime.pyx:661, in pandas._libs.tslibs.np_datetime.convert_reso() ValueError: Cannot losslessly convert units The above exception was the direct cause of the following exception: IncompatibleFrequency Traceback (most recent call last) Cell In[366], line 1 ----> 1 p + pd.offsets.Minute(5) File period.pyx:1845, in pandas._libs.tslibs.period._Period.__add__() File period.pyx:1826, in pandas._libs.tslibs.period._Period._add_timedeltalike_scalar() IncompatibleFrequency: Input cannot be converted to Period(freq=h) 如果Period有其他频率,则offsets只能添加相同的频率。否则,ValueError将会被提高。 In [367]: p = pd.Period("2014-07", freq="M") In [368]: p + pd.offsets.MonthEnd(3) Out[368]: Period('2014-10', 'M') In [369]: p + pd.offsets.MonthBegin(3) --------------------------------------------------------------------------- IncompatibleFrequency Traceback (most recent call last) Cell In[369], line 1 ----> 1 p + pd.offsets.MonthBegin(3) File period.pyx:1847, in pandas._libs.tslibs.period._Period.__add__() File period.pyx:1837, in pandas._libs.tslibs.period._Period._add_offset() File period.pyx:1732, in pandas._libs.tslibs.period.PeriodMixin._require_matching_freq() IncompatibleFrequency: Input has different freq=3M from Period(freq=M) 对具有相同频率的实例进行求差Period将返回它们之间的频率单位数: In [370]: pd.Period("2012", freq="Y-DEC") - pd.Period("2002", freq="Y-DEC") Out[370]: <10 * YearEnds: month=12> periodIndex 和 period_range # 规则的对象序列Period可以收集在 a 中PeriodIndex,可以使用period_range便利函数构造: In [371]: prng = pd.period_range("1/1/2011", "1/1/2012", freq="M") In [372]: prng Out[372]: 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 [373]: pd.PeriodIndex(["2011-1", "2011-2", "2011-3"], freq="M") Out[373]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]') 通过倍频输出一个Period具有倍频跨度的序列。 In [374]: pd.period_range(start="2014-01", freq="3M", periods=4) Out[374]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]') 如果start或end是Period对象,则它们将用作PeriodIndex频率与构造函数匹配的 锚点端点PeriodIndex。 In [375]: pd.period_range( .....: start=pd.Period("2017Q1", freq="Q"), end=pd.Period("2017Q2", freq="Q"), freq="M" .....: ) .....: Out[375]: PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]') 就像 一样DatetimeIndex,aPeriodIndex也可以用于索引 pandas 对象: In [376]: ps = pd.Series(np.random.randn(len(prng)), prng) In [377]: ps Out[377]: 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 [378]: idx = pd.period_range("2014-07-01 09:00", periods=5, freq="h") In [379]: idx Out[379]: 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 [380]: idx + pd.offsets.Hour(2) Out[380]: 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 [381]: idx = pd.period_range("2014-07", periods=5, freq="M") In [382]: idx Out[382]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]') In [383]: idx + pd.offsets.MonthEnd(3) Out[383]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]') PeriodIndex有自己的数据类型,名为period,请参阅period Dtypes。 周期 dtypes # PeriodIndex有一个自定义的period数据类型。这是一个 pandas 扩展数据类型,类似于时区感知数据类型( )。datetime64[ns, tz] dtypeperiod保存属性并使用频率字符串用类似或freq来表示 。period[freq]period[D]period[M] In [384]: pi = pd.period_range("2016-01-01", periods=3, freq="M") In [385]: pi Out[385]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]') In [386]: pi.dtype Out[386]: period[M] dtypeperiod可用于.astype(...).它允许人们改变 freqa 的PeriodIndexa.asfreq()并将 a 转换 DatetimeIndex为PeriodIndexlike to_period(): # change monthly freq to daily freq In [387]: pi.astype("period[D]") Out[387]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]') # convert to DatetimeIndex In [388]: pi.astype("datetime64[ns]") Out[388]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS') # convert to PeriodIndex In [389]: dti = pd.date_range("2011-01-01", freq="ME", periods=3) In [390]: dti Out[390]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='ME') In [391]: dti.astype("period[M]") Out[391]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]') periodIndex 部分字符串索引# periodIndex 现在支持使用非单调索引进行部分字符串切片。 您可以将日期和字符串传递给Series并DataFrame使用PeriodIndex,其方式与 相同DatetimeIndex。有关详细信息,请参阅DatetimeIndex 部分字符串索引。 In [392]: ps["2011-01"] Out[392]: -2.9169013294054507 In [393]: ps[datetime.datetime(2011, 12, 25):] Out[393]: 2011-12 2.261385 2012-01 -0.329583 Freq: M, dtype: float64 In [394]: ps["10/31/2011":"12/31/2011"] Out[394]: 2011-10 0.056780 2011-11 0.197035 2011-12 2.261385 Freq: M, dtype: float64 传递一个表示频率低于PeriodIndex返回部分切片数据的字符串。 In [395]: ps["2011"] Out[395]: 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 [396]: dfp = pd.DataFrame( .....: np.random.randn(600, 1), .....: columns=["A"], .....: index=pd.period_range("2013-01-01 9:00", periods=600, freq="min"), .....: ) .....: In [397]: dfp Out[397]: 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 [398]: dfp.loc["2013-01-01 10h"] Out[398]: 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 [399]: dfp["2013-01-01 10h":"2013-01-01 11h"] Out[399]: 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] 使用PeriodIndex进行频率转换和重采样# Period和的频率PeriodIndex可以通过该asfreq 方法进行转换。让我们从截至 12 月的 2011 财年开始: In [400]: p = pd.Period("2011", freq="Y-DEC") In [401]: p Out[401]: Period('2011', 'Y-DEC') 我们可以将其转换为每月的频率。使用该how参数,我们可以指定是否返回开始或结束月份: In [402]: p.asfreq("M", how="start") Out[402]: Period('2011-01', 'M') In [403]: p.asfreq("M", how="end") Out[403]: Period('2011-12', 'M') 为了方便起见,提供了简写 's' 和 'e': In [404]: p.asfreq("M", "s") Out[404]: Period('2011-01', 'M') In [405]: p.asfreq("M", "e") Out[405]: Period('2011-12', 'M') 转换为“超级周期”(例如,年度频率是季度频率的超级周期)会自动返回包含输入周期的超级周期: In [406]: p = pd.Period("2011-12", freq="M") In [407]: p.asfreq("Y-NOV") Out[407]: Period('2012', 'Y-NOV') 请注意,由于我们转换为 11 月结束的年度频率,因此 2011 年 12 月的月度周期实际上属于 2012 年 Y-NOV 周期。 具有锚定频率的周期转换对于处理经济、商业和其他领域常见的各种季度数据特别有用。许多组织根据其会计年度开始和结束的月份来定义季度。因此,2011 年第一季度可能会在 2010 年或 2011 年几个月后开始。通过固定频率,pandas 适用于所有季度Q-JAN频率Q-DEC。 Q-DEC定义常规日历季度: In [408]: p = pd.Period("2012Q1", freq="Q-DEC") In [409]: p.asfreq("D", "s") Out[409]: Period('2012-01-01', 'D') In [410]: p.asfreq("D", "e") Out[410]: Period('2012-03-31', 'D') Q-MAR定义财政年度结束于三月: In [411]: p = pd.Period("2011Q4", freq="Q-MAR") In [412]: p.asfreq("D", "s") Out[412]: Period('2011-01-01', 'D') In [413]: p.asfreq("D", "e") Out[413]: Period('2011-03-31', 'D') 表示之间的转换# 时间戳数据可以使用以下命令转换为PeriodIndex-ed数据to_period ,反之亦然to_timestamp: In [414]: rng = pd.date_range("1/1/2012", periods=5, freq="ME") In [415]: ts = pd.Series(np.random.randn(len(rng)), index=rng) In [416]: ts Out[416]: 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: ME, dtype: float64 In [417]: ps = ts.to_period() In [418]: ps Out[418]: 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 [419]: ps.to_timestamp() Out[419]: 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 [420]: ps.to_timestamp("D", how="s") Out[420]: 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 在周期和时间戳之间进行转换可以使用一些方便的算术函数。在以下示例中,我们将年份结束于 11 月的季度频率转换为季度末后下个月月底的上午 9 点: In [421]: prng = pd.period_range("1990Q1", "2000Q4", freq="Q-NOV") In [422]: ts = pd.Series(np.random.randn(len(prng)), prng) In [423]: ts.index = (prng.asfreq("M", "e") + 1).asfreq("h", "s") + 9 In [424]: ts.head() Out[424]: 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,请参阅时间戳限制,然后您可以使用 aPeriodIndex和/或SeriesofPeriods进行计算。 In [425]: span = pd.period_range("1215-01-01", "1381-01-01", freq="D") In [426]: span Out[426]: 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) 从基于 YYYYMMDD 的表示形式进行转换int64。 In [427]: s = pd.Series([20121231, 20141130, 99991231]) In [428]: s Out[428]: 0 20121231 1 20141130 2 99991231 dtype: int64 In [429]: def conv(x): .....: return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D") .....: In [430]: s.apply(conv) Out[430]: 0 2012-12-31 1 2014-11-30 2 9999-12-31 dtype: period[D] In [431]: s.apply(conv)[2] Out[431]: Period('9999-12-31', 'D') 这些可以很容易地转换为PeriodIndex: In [432]: span = pd.PeriodIndex(s.apply(conv)) In [433]: span Out[433]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]') 时区处理# pandas 为使用标准库中的库或对象pytz处理dateutil不同时区的时间戳提供了丰富的支持。datetime.timezone 使用时区# 默认情况下,pandas 对象不知道时区: In [434]: rng = pd.date_range("3/6/2012 00:00", periods=15, freq="D") In [435]: rng.tz is None Out[435]: True 要将这些日期本地化为时区(将特定时区分配给原始日期),您可以使用、或中的 tz_localize方法或关键字参数。您可以传递时区对象或 Olson 时区数据库字符串。 Olson 时区字符串默认返回时区对象。要返回时区对象,请附加在字符串之前。tzdate_range()TimestampDatetimeIndexpytzdateutilpytzdateutildateutil/ 您pytz可以使用 找到常见(和不太常见)时区的列表 。from pytz import common_timezones, all_timezones dateutil使用操作系统时区,因此没有可用的固定列表。对于公共区域,名称与 相同pytz。 In [436]: import dateutil # pytz In [437]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz="Europe/London") In [438]: rng_pytz.tz Out[438]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> # dateutil In [439]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D") In [440]: rng_dateutil = rng_dateutil.tz_localize("dateutil/Europe/London") In [441]: rng_dateutil.tz Out[441]: tzfile('/usr/share/zoneinfo/Europe/London') # dateutil - utc special case In [442]: rng_utc = pd.date_range( .....: "3/6/2012 00:00", .....: periods=3, .....: freq="D", .....: tz=dateutil.tz.tzutc(), .....: ) .....: In [443]: rng_utc.tz Out[443]: tzutc() # datetime.timezone In [444]: rng_utc = pd.date_range( .....: "3/6/2012 00:00", .....: periods=3, .....: freq="D", .....: tz=datetime.timezone.utc, .....: ) .....: In [445]: rng_utc.tz Out[445]: datetime.timezone.utc 请注意,UTC时区是 中的特殊情况dateutil,应显式构造为 的实例dateutil.tz.tzutc。您还可以首先显式构造其他时区对象。 In [446]: import pytz # pytz In [447]: tz_pytz = pytz.timezone("Europe/London") In [448]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D") In [449]: rng_pytz = rng_pytz.tz_localize(tz_pytz) In [450]: rng_pytz.tz == tz_pytz Out[450]: True # dateutil In [451]: tz_dateutil = dateutil.tz.gettz("Europe/London") In [452]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz=tz_dateutil) In [453]: rng_dateutil.tz == tz_dateutil Out[453]: True 要将时区感知的 pandas 对象从一个时区转换为另一个时区,您可以使用该tz_convert方法。 In [454]: rng_pytz.tz_convert("US/Eastern") Out[454]: 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不同的时区对象。 TimestampADatetimeIndex 可以保存一组对象,这些对象可能具有不同的 UTC 偏移量,并且不能用一个时区实例Timestamp简洁地表示,而一个则 表示具有特定 UTC 偏移量的一个时间点。pytzTimestamp In [455]: dti = pd.date_range("2019-01-01", periods=3, freq="D", tz="US/Pacific") In [456]: dti.tz Out[456]: <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD> In [457]: ts = pd.Timestamp("2019-01-01", tz="US/Pacific") In [458]: ts.tz Out[458]: <DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD> 警告 警惕库之间的转换。对于某些时区,pytz并且dateutil有不同的时区定义。对于不寻常的时区来说,这比对于“标准”时区(例如US/Eastern. 警告 请注意,不同版本的时区库的时区定义可能不被视为相同。当处理使用一种版本本地化并使用不同版本操作的存储数据时,这可能会导致问题。请参阅此处了解如何处理此类情况。 警告 对于pytz时区,将时区对象直接传递到datetime.datetime构造函数中是不正确的(例如,)。相反,需要使用时区对象上的方法来本地化日期时间。datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern'))localizepytz 警告 请注意,对于未来的时间,任何时区库都无法保证时区(和 UTC)之间的正确转换,因为时区与 UTC 的偏移量可能会被相应政府更改。 警告 如果您使用 2038 年 1 月 18 日之后的日期,由于 2038 年问题导致底层库当前存在缺陷,因此将不会应用对时区感知日期的夏令时 (DST) 调整。如果底层库已修复,则将应用 DST 转换。 例如,对于英国夏令时的两个日期(通常为 GMT+1),以下两个断言均评估为 true: In [459]: d_2037 = "2037-03-31T010101" In [460]: d_2038 = "2038-03-31T010101" In [461]: DST = "Europe/London" In [462]: assert pd.Timestamp(d_2037, tz=DST) != pd.Timestamp(d_2037, tz="GMT") In [463]: assert pd.Timestamp(d_2038, tz=DST) == pd.Timestamp(d_2038, tz="GMT") 在底层,所有时间戳都存储在 UTC 中。来自时区的值可感知 DatetimeIndex或Timestamp将其字段(日、小时、分钟等)本地化为时区。然而,具有相同 UTC 值的时间戳即使位于不同时区,仍被认为是相等的: In [464]: rng_eastern = rng_utc.tz_convert("US/Eastern") In [465]: rng_berlin = rng_utc.tz_convert("Europe/Berlin") In [466]: rng_eastern[2] Out[466]: Timestamp('2012-03-07 19:00:00-0500', tz='US/Eastern') In [467]: rng_berlin[2] Out[467]: Timestamp('2012-03-08 01:00:00+0100', tz='Europe/Berlin') In [468]: rng_eastern[2] == rng_berlin[2] Out[468]: True Series不同时区 之间的操作将产生 UTC Series,从而在 UTC 时间戳上对齐数据: In [469]: ts_utc = pd.Series(range(3), pd.date_range("20130101", periods=3, tz="UTC")) In [470]: eastern = ts_utc.tz_convert("US/Eastern") In [471]: berlin = ts_utc.tz_convert("Europe/Berlin") In [472]: result = eastern + berlin In [473]: result Out[473]: 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 [474]: result.index Out[474]: 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 [475]: didx = pd.date_range(start="2014-08-01 09:00", freq="h", periods=3, tz="US/Eastern") In [476]: didx Out[476]: 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 [477]: didx.tz_localize(None) Out[477]: 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 [478]: didx.tz_convert(None) Out[478]: 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 [479]: didx.tz_convert("UTC").tz_localize(None) Out[479]: DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00', '2014-08-01 15:00:00'], dtype='datetime64[ns]', freq=None) 折叠# 对于不明确的情况,pandas 支持显式指定仅关键字折叠参数。由于夏令时的原因,从夏季时间转换到冬季时间时,一个挂钟时间可能会出现两次; Fold 描述了 datetime-like 是否对应于挂钟到达模糊时间的第一次 (0) 还是第二次 (1)。仅支持从朴素构建datetime.datetime (有关详细信息,请参阅日期时间文档)或从Timestamp 组件构建(参见下文)。仅dateutil支持时区(有关处理不明确日期时间的方法,请参阅 dateutil文档),因为时 区不支持折叠( 有关如何处理不明确日期时间的详细信息,请参阅pytz 文档)。要使用 本地化不明确的日期时间,请使用。一般来说,如果您需要直接控制日期时间的处理方式,我们建议在本地化不明确的日期时间时使用 。dateutilpytzpytzpytzTimestamp.tz_localize()Timestamp.tz_localize() In [480]: pd.Timestamp( .....: datetime.datetime(2019, 10, 27, 1, 30, 0, 0), .....: tz="dateutil/Europe/London", .....: fold=0, .....: ) .....: Out[480]: Timestamp('2019-10-27 01:30:00+0100', tz='dateutil//usr/share/zoneinfo/Europe/London') In [481]: pd.Timestamp( .....: year=2019, .....: month=10, .....: day=27, .....: hour=1, .....: minute=30, .....: tz="dateutil/Europe/London", .....: fold=1, .....: ) .....: Out[481]: Timestamp('2019-10-27 01:30:00+0000', tz='dateutil//usr/share/zoneinfo/Europe/London') 本地化时出现歧义# tz_localize可能无法确定时间戳的 UTC 偏移量,因为本地时区的夏令时 (DST) 会导致某些时间在一天内出现两次(“时钟回退”)。可以使用以下选项: 'raise':引发 a pytz.AmbiguousTimeError(默认行为) 'infer':尝试根据时间戳的单调性确定正确的偏移量 'NaT': 将不明确的时间替换为NaT bool:True代表夏令时,False代表非夏令时。bool支持一系列时间序列的类似数组的值。 In [482]: 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 [483]: rng_hourly.tz_localize('US/Eastern') --------------------------------------------------------------------------- AmbiguousTimeError Traceback (most recent call last) Cell In[483], line 1 ----> 1 rng_hourly.tz_localize('US/Eastern') File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:293, in DatetimeIndex.tz_localize(self, tz, ambiguous, nonexistent) 286 @doc(DatetimeArray.tz_localize) 287 def tz_localize( 288 self, (...) 291 nonexistent: TimeNonexistent = "raise", 292 ) -> Self: --> 293 arr = self._data.tz_localize(tz, ambiguous, nonexistent) 294 return type(self)._simple_new(arr, name=self.name) File ~/work/pandas/pandas/pandas/core/arrays/_mixins.py:81, in ravel_compat.<locals>.method(self, *args, **kwargs) 78 @wraps(meth) 79 def method(self, *args, **kwargs): 80 if self.ndim == 1: ---> 81 return meth(self, *args, **kwargs) 83 flags = self._ndarray.flags 84 flat = self.ravel("K") File ~/work/pandas/pandas/pandas/core/arrays/datetimes.py:1088, in DatetimeArray.tz_localize(self, tz, ambiguous, nonexistent) 1085 tz = timezones.maybe_get_tz(tz) 1086 # Convert to UTC -> 1088 new_dates = tzconversion.tz_localize_to_utc( 1089 self.asi8, 1090 tz, 1091 ambiguous=ambiguous, 1092 nonexistent=nonexistent, 1093 creso=self._creso, 1094 ) 1095 new_dates_dt64 = new_dates.view(f"M8[{self.unit}]") 1096 dtype = tz_to_dtype(tz, unit=self.unit) File tzconversion.pyx:371, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc() AmbiguousTimeError: Cannot infer dst time from 2011-11-06 01:00:00, try using the 'ambiguous' argument 通过指定以下内容来处理这些不明确的时间。 In [484]: rng_hourly.tz_localize("US/Eastern", ambiguous="infer") Out[484]: 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 [485]: rng_hourly.tz_localize("US/Eastern", ambiguous="NaT") Out[485]: 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 [486]: rng_hourly.tz_localize("US/Eastern", ambiguous=[True, True, False, False]) Out[486]: 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':引发 a pytz.NonExistentTimeError(默认行为) 'NaT': 将不存在的时间替换为NaT 'shift_forward':将不存在的时间向前移动到最接近的实时时间 'shift_backward':将不存在的时间向后移动到最接近的实时时间 timedelta 对象:将不存在的时间移动 timedelta 持续时间 In [487]: dti = pd.date_range(start="2015-03-29 02:30:00", periods=3, freq="h") # 2:30 is a nonexistent time 默认情况下,不存在的时间的本地化会引发错误。 In [488]: dti.tz_localize('Europe/Warsaw') --------------------------------------------------------------------------- NonExistentTimeError Traceback (most recent call last) Cell In[488], line 1 ----> 1 dti.tz_localize('Europe/Warsaw') File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:293, in DatetimeIndex.tz_localize(self, tz, ambiguous, nonexistent) 286 @doc(DatetimeArray.tz_localize) 287 def tz_localize( 288 self, (...) 291 nonexistent: TimeNonexistent = "raise", 292 ) -> Self: --> 293 arr = self._data.tz_localize(tz, ambiguous, nonexistent) 294 return type(self)._simple_new(arr, name=self.name) File ~/work/pandas/pandas/pandas/core/arrays/_mixins.py:81, in ravel_compat.<locals>.method(self, *args, **kwargs) 78 @wraps(meth) 79 def method(self, *args, **kwargs): 80 if self.ndim == 1: ---> 81 return meth(self, *args, **kwargs) 83 flags = self._ndarray.flags 84 flat = self.ravel("K") File ~/work/pandas/pandas/pandas/core/arrays/datetimes.py:1088, in DatetimeArray.tz_localize(self, tz, ambiguous, nonexistent) 1085 tz = timezones.maybe_get_tz(tz) 1086 # Convert to UTC -> 1088 new_dates = tzconversion.tz_localize_to_utc( 1089 self.asi8, 1090 tz, 1091 ambiguous=ambiguous, 1092 nonexistent=nonexistent, 1093 creso=self._creso, 1094 ) 1095 new_dates_dt64 = new_dates.view(f"M8[{self.unit}]") 1096 dtype = tz_to_dtype(tz, unit=self.unit) File tzconversion.pyx:431, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc() NonExistentTimeError: 2015-03-29 02:30:00 将不存在的时间转化为NaT或移动时间。 In [489]: dti Out[489]: 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 [490]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_forward") Out[490]: 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 [491]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_backward") Out[491]: 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 [492]: dti.tz_localize("Europe/Warsaw", nonexistent=pd.Timedelta(1, unit="h")) Out[492]: 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 [493]: dti.tz_localize("Europe/Warsaw", nonexistent="NaT") Out[493]: 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) 时区系列操作# 具有Series时区朴素值的 dtype 表示为datetime64[ns]。 In [494]: s_naive = pd.Series(pd.date_range("20130101", periods=3)) In [495]: s_naive Out[495]: 0 2013-01-01 1 2013-01-02 2 2013-01-03 dtype: datetime64[ns] Series具有时区感知值的数据类型表示时区datetime64[ns, tz]tz In [496]: s_aware = pd.Series(pd.date_range("20130101", periods=3, tz="US/Eastern")) In [497]: s_aware 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] 这两个Series时区信息都可以通过访问器进行操作.dt,请参阅dt 访问器部分。 例如,将原始时间戳本地化并转换为时区感知。 In [498]: s_naive.dt.tz_localize("UTC").dt.tz_convert("US/Eastern") Out[498]: 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 [499]: s_aware.astype("datetime64[ns, CET]") Out[499]: 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()在 a 上使用Series,返回数据的 NumPy 数组。 NumPy 目前不支持时区(即使它在本地时区打印!),因此为时区感知数据返回时间戳对象数组: In [500]: s_naive.to_numpy() Out[500]: array(['2013-01-01T00:00:00.000000000', '2013-01-02T00:00:00.000000000', '2013-01-03T00:00:00.000000000'], dtype='datetime64[ns]') In [501]: s_aware.to_numpy() Out[501]: 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 [502]: pd.Series(s_aware.to_numpy()) Out[502]: 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] 但是,如果您想要一个实际的 NumPydatetime64[ns]数组(值转换为 UTC)而不是对象数组,您可以指定参数 dtype: In [503]: s_aware.to_numpy(dtype="datetime64[ns]") Out[503]: array(['2013-01-01T05:00:00.000000000', '2013-01-02T05:00:00.000000000', '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')