Pandas 2.2 中文官方教程和指南(二十一·二)

2024-05-24 16:23:30 浏览数 (2)

向后重新采样

版本 1.3.0 中的新功能。

有时,我们需要调整箱子的开始而不是结束,以便使用给定的freq进行向后重新采样。向后重新采样默认将closed设置为'right',因为最后一个值应被视为最后一个箱子的边缘点。

我们可以将origin设置为'end'。特定Timestamp索引的值表示从当前Timestamp减去freq到当前Timestamp的右闭区间的重新采样结果。

代码语言:javascript复制
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。这将把origin设置为最大Timestamp的午夜。

代码语言:javascript复制
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 

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

代码语言:javascript复制
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') 
```## 时间跨度表示

在 pandas 中,时间的常规间隔由`Period`对象表示,而`Period`对象的序列被收集在`PeriodIndex`中,可以使用便利函数`period_range`创建。

### 期间

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

```py
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') 

从期间中添加和减去整数会按照其自身频率移动期间。不允许在具有不同freq(跨度)的Period之间进行算术运算。

代码语言:javascript复制
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频率是每天或更高(Dhminsmsusns),则可以添加offsets和类似于timedelta的内容,如果结果具有相同的频率,则可以添加。否则,将引发ValueError

代码语言:javascript复制
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') 
代码语言:javascript复制
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

代码语言:javascript复制
In [367]: p = pd.Period("2014-07", freq="M")

In [368]: p   pd.offsets.MonthEnd(3)
Out[368]: Period('2014-10', 'M') 
代码语言:javascript复制
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实例之间的差异将返回它们之间的频率单位数:

代码语言:javascript复制
In [370]: pd.Period("2012", freq="Y-DEC") - pd.Period("2002", freq="Y-DEC")
Out[370]: <10 * YearEnds: month=12> 
PeriodIndex 和 period_range

Period对象的常规序列可以收集在PeriodIndex中,可以使用period_range便利函数构建:

代码语言:javascript复制
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构造函数也可以直接使用:

代码语言:javascript复制
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序列。

代码语言:javascript复制
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]') 

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

代码语言:javascript复制
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一样,PeriodIndex也可以用于索引 pandas 对象:

代码语言:javascript复制
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相同规则的加法和减法。

代码语言:javascript复制
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的 dtype,请参考 Period Dtypes。

期间 dtype

PeriodIndex具有自定义的period dtype。这是类似于时区感知 dtype(datetime64[ns, tz])的 pandas 扩展 dtype。

period dtype 保存freq属性,并且用period[freq]表示,如period[D]period[M],使用频率字符串。

代码语言:javascript复制
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] 

period dtype 可以在.astype(...)中使用。它允许更改PeriodIndexfreq,如.asfreq(),并将DatetimeIndex转换为PeriodIndex,如to_period()

代码语言:javascript复制
# 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 现在支持具有非单调索引的部分字符串切片。

您可以像DatetimeIndex一样向SeriesDataFrame传递日期和字符串,具有PeriodIndex,有关详细信息,请参考 DatetimeIndex 部分字符串索引。

代码语言:javascript复制
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的频率的字符串将返回部分切片数据。

代码语言:javascript复制
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。

代码语言:javascript复制
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 进行频率转换和重采样

PeriodPeriodIndex的频率可以通过asfreq方法进行转换。让我们从 2011 财政年度开始,截至 12 月:

代码语言:javascript复制
In [400]: p = pd.Period("2011", freq="Y-DEC")

In [401]: p
Out[401]: Period('2011', 'Y-DEC') 

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

代码语言:javascript复制
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’的简写形式以方便使用:

代码语言:javascript复制
In [404]: p.asfreq("M", "s")
Out[404]: Period('2011-01', 'M')

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

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

代码语言:javascript复制
In [406]: p = pd.Period("2011-12", freq="M")

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

请注意,由于我们转换为年度频率,年终在十一月,因此 2011 年 12 月的月度期间实际上在 2012 年 Y-NOV 期间。

具有锚定频率的期间转换对于处理经济学、商业和其他领域常见的各种季度数据特别有用。许多组织将季度定义为其财政年度开始和结束的月份。因此,2011 年第一季度可能从 2010 年开始,或者在 2011 年的几个月内开始。通过锚定频率,pandas 适用于所有季度频率 Q-JANQ-DEC

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

代码语言:javascript复制
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 定义了年度财政年度结束于三月:

代码语言:javascript复制
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') 
```## 转换表示方式

时间戳数据可以使用 `to_period` 转换为 PeriodIndex 数据,反之亦然使用 `to_timestamp`:

```py
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’ 可以用于返回期间开始或结束的时间戳:

代码语言:javascript复制
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 

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

代码语言:javascript复制
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` 的范围,请参阅时间戳限制,然后您可以使用 `PeriodIndex` 和/或 `Periods` 的 `Series` 进行计算。

```py
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) 

要从基于 int64 的 YYYYMMDD 表示形式转换。

代码语言:javascript复制
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

代码语言:javascript复制
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`](https://docs.python.org/3/library/datetime.html#datetime.timezone "(在 Python v3.12 中)") 对象,可以处理不同时区的时间戳。

### 处理时区

默认情况下,pandas 对象不考虑时区:

```py
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 方法或 date_range() 中的 tz 关键字参数,TimestampDatetimeIndex。您可以传递 pytzdateutil 时区对象或 Olson 时区数据库字符串。Olson 时区字符串将默认返回 pytz 时区对象。要返回 dateutil 时区对象,请在字符串之前添加 dateutil/

  • pytz 中,您可以使用 from pytz import common_timezones, all_timezones 找到常见(以及不太常见)的时区列表。
  • dateutil 使用操作系统时区,因此没有固定的列表可用。对于常见时区,名称与 pytz 相同。
代码语言:javascript复制
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() 
代码语言:javascript复制
# 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的实例。您也可以首先显式构造其他时区对象。

代码语言:javascript复制
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方法。

代码语言:javascript复制
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将构造一个不同的时区对象,而对于相同的时区输入,Timestamp将构造一个不同的时区对象。一个DatetimeIndex可以保存一组具有不同 UTC 偏移的Timestamp对象,而不能简洁地由一个pytz时区实例表示,而一个Timestamp代表一个具有特定 UTC 偏移的时间点。

代码语言:javascript复制
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> 

警告

要谨慎处理库之间的转换。对于一些时区,pytzdateutil对时区的定义不同。这对于不寻常的时区比‘标准’时区如US/Eastern更有问题。

警告

请注意,跨时间区库版本的时区定义可能不被视为相等。这可能会在使用一个版本本地化的存储数据并在不同版本上操作时出现问题。请参见这里如何处理这种情况。

警告

对于pytz时区,直接将一个时区对象传递给datetime.datetime构造函数是不正确的(例如,datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern')))。相反,需要使用pytz时区对象上的localize方法对日期时间进行本地化。

警告

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

警告

如果您使用的日期超过 2038-01-18,由于底层库中当前存在的年 2038 问题导致的缺陷,时区感知日期的夏令时(DST)调整将不会被应用。如果底层库被修复,DST 转换将会被应用。

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

代码语言:javascript复制
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 存储。来自时区感知的DatetimeIndexTimestamp的值将被本地化到时区。然而,具有相同 UTC 值的时间戳即使在不同时区中仍被视为相等:

代码语言:javascript复制
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 时间戳上:

代码语言:javascript复制
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 时间后删除时区。

代码语言:javascript复制
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) 
Fold

对于模糊时间,pandas 支持显式指定仅关键字 fold 参数。由于夏令时,当从夏季时间转换到冬季时间时,一个挂钟时间可能发生两次;fold 描述 datetime-like 是否对应于挂钟第一次(0)或第二次(1)命中模糊时间。仅支持从 naive datetime.datetime(有关详细信息,请参阅datetime 文档)或从Timestamp构造或从组件构造(见下文)。仅支持dateutil时区(请参阅dateutil 文档以了解处理模糊日期时间的dateutil方法),因为pytz时区不支持 fold(请参阅pytz 文档以了解pytz如何处理模糊日期时间的详细信息)。要使用pytz本地化模糊日期时间,请使用Timestamp.tz_localize()。一般来说,如果需要直接控制处理模糊日期时间的方式,我们建议在本地化模糊日期时间时依赖于Timestamp.tz_localize()

代码语言:javascript复制
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 偏移量,因为本地时区的夏令时导致某些时间在一天内发生两次(“时钟回拨”)。以下选项可用:

    `'raise'`:引发`pytz.AmbiguousTimeError`(默认行为)

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

    `'NaT'`:用`NaT`替换模糊时间

    `bool`:`True`表示 DST 时间,`False`表示非 DST 时间。支持用于时间序列的`bool`值的类似数组。

```py
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'

代码语言:javascript复制
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 

通过指定以下内容来处理这些模糊的时间。

代码语言:javascript复制
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'`:引发`pytz.NonExistentTimeError`(默认行为)

    `'NaT'`:用`NaT`替换不存在的时间

    `'shift_forward'`:将不存在的时间向前移动到最近的真实时间

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

    timedelta 对象:通过 timedelta 持续时间移动不存在的时间

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

# 2:30 is a nonexistent time 

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

代码语言:javascript复制
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或移动时间。

代码语言:javascript复制
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) 
```### 时区系列操作

具有**naive**值的`Series`以`datetime64[ns]`的 dtype 表示。

```py
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] 

具有aware值的Seriesdatetime64[ns, tz]的 dtype 表示,其中tz是时区

代码语言:javascript复制
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 访问器部分。

例如,将 naive 时间戳本地化和转换为时区感知。

代码语言:javascript复制
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方法进行操作。该方法可以在不同的时区感知 dtype 之间进行转换。

代码语言:javascript复制
# 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上使用Series.to_numpy(),返回数据的 NumPy 数组。 NumPy 当前不支持时区(即使在本地时区打印!),因此对于时区感知数据,将返回时间戳的对象数组:

代码语言:javascript复制
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) 

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

代码语言:javascript复制
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参数:

代码语言:javascript复制
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]') 
```## 概述

pandas 捕获了 4 个通用的与时间相关的概念:

1.  日期时间:具有时区支持的特定日期和时间。类似于标准库中的`datetime.datetime`。  

1.  时间增量:绝对时间持续时间。类似于标准库中的`datetime.timedelta`。

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

1.  日期偏移量:一种尊重日历算术的相对时间持续。类似于`dateutil`包中的`dateutil.relativedelta.relativedelta`。

| 概念 | 标量类 | 数组类 | pandas 数据类型 | 主要创建方法 |
| --- | --- | --- | --- | --- |
| 日期时间 | `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`的索引,以便可以针对时间元素进行操作。

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

然而,SeriesDataFrame也可以直接支持时间组件作为数据本身。

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

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

代码语言:javascript复制
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对于浮点数据的行为类似。

代码语言:javascript复制
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 对象,这意味着使用时间点。

代码语言:javascript复制
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表示的跨度可以明确指定,也可以从日期时间字符串格式中推断出来。

例如:

代码语言:javascript复制
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') 

TimestampPeriod可以用作索引。TimestampPeriod的列表将自动强制转换为DatetimeIndexPeriodIndex

代码语言:javascript复制
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函数。当传递一个Series时,它会返回一个相同索引的Series,而列表则会被转换为DatetimeIndex

代码语言:javascript复制
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标志:

代码语言:javascript复制
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不是严格的。如果日期无法解析为以天为首的日期,它将被解析为dayfirstFalse,同时还会引发警告。

如果将单个字符串传递给to_datetime,它将返回单个TimestampTimestamp也可以接受字符串输入,但它不接受像dayfirstformat这样的字符串解析选项,因此如果需要这些选项,请使用to_datetime

代码语言:javascript复制
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构造函数:

代码语言:javascript复制
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”以设置索引的频率为创建时的推断频率:

代码语言:javascript复制
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参数以确保特定的解析。这也可能显著加快转换速度。

代码语言:javascript复制
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 datetime 文档。

从多个 DataFrame 列组装日期时间

您还可以传递一个整数或字符串列的DataFrame以组装为TimestampsSeries

代码语言:javascript复制
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] 

您可以只传递您需要组装的列。

代码语言:javascript复制
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会查找列名中 datetime 组件的标准标识,包括:

  • 必需的:yearmonthday
  • 可选的:hourminutesecondmillisecondmicrosecondnanosecond
无效的数据

默认行为errors='raise'是在无法解析时引发异常:

代码语言:javascript复制
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(不是时间):

代码语言:javascript复制
In [58]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce")
Out[58]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None) 
纪元时间戳

pandas 支持将整数或浮点数纪元时间转换为TimestampDatetimeIndex。默认单位是纳秒,因为Timestamp对象在内部存储时是以纳秒为单位的。然而,纪元时间通常以另一个单位存储,可以指定。这些是从origin参数指定的起始点计算出来的。

代码语言:javascript复制
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()的文档中列出。

使用tz参数指定了 epoch 时间戳的TimestampDatetimeIndex构造会引发 ValueError。如果你有另一个时区中的墙上时间的 epoch,你可以将 epoch 读取为时区不敏感的时间戳,然后本地化到适当的时区:

代码语言:javascript复制
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) 

注意

Epoch 时间将四舍五入到最近的纳秒。

警告

将 float 型 epoch 时间转换可能导致不准确和意外的结果。Python floats在十进制中有约 15 位数字精度。在从浮点数到高精度Timestamp的转换过程中进行舍入是不可避免的。实现精确精度的唯一方法是使用固定宽度的类型(例如 int64)。

代码语言:javascript复制
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') 

另请参阅

使用 origin 参数 ### 从时间戳到 epoch

要反转上述操作,即从Timestamp转换为‘unix’ epoch:

代码语言:javascript复制
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') 

我们减去纪元(1970 年 1 月 1 日 UTC 午夜),然后进行“单位”(1 秒)的地板除法。

代码语言:javascript复制
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 作为起始日期:

```py
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',默认为1970-01-01 00:00:00。通常称为“unix 纪元”或 POSIX 时间。

代码语言:javascript复制
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) 
```### 提供 format 参数

除了必需的 datetime 字符串之外,还可以传递一个`format`参数以确保特定的解析。这也可能显著加快转换速度。

```py
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 datetime 文档。

从多个 DataFrame 列中组装 datetime

你还可以传递一个整数或字符串列的DataFrame以组装成TimestampsSeries

代码语言:javascript复制
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] 

你只需要传递你需要组装的列。

代码语言:javascript复制
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查找列名中 datetime 组件的标准设计,包括:

  • 必需:yearmonthday
  • 可选:hourminutesecondmillisecondmicrosecondnanosecond
无效数据

默认行为,errors='raise',是在不可解析时引发异常:

代码语言:javascript复制
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(不是时间):

代码语言:javascript复制
In [58]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce")
Out[58]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None) 
Epoch 时间戳

pandas 支持将整数或浮点时代转换为TimestampDatetimeIndex。默认单位为纳秒,因为这是Timestamp对象在内部存储的方式。但是,时代通常以另一个可以指定的unit存储。这些是从由origin参数指定的起始点计算得出的。

代码语言:javascript复制
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() 的文档中找到可用的单位。

使用指定了tz参数的时代时间戳构造TimestampDatetimeIndex 将引发 ValueError。如果您在另一个时区的壁钟时间中有时代,您可以将时代读取为时区不可知的时间戳,然后本地化到适当的时区:

代码语言:javascript复制
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)。

代码语言:javascript复制
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') 

另请参阅

使用起始参数

从时间戳到时代

要反转上述操作,即从Timestamp转换为‘unix’时代:

代码语言:javascript复制
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') 

我们减去时代(1970 年 1 月 1 日 UTC 的午夜),然后除以“unit”(1 秒)。

代码语言:javascript复制
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 作为起始日期:

代码语言:javascript复制
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',默认为1970-01-01 00:00:00。通常称为“unix 时代”或 POSIX 时间。

代码语言:javascript复制
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) 

生成时间戳范围

要生成带有时间戳的索引,您可以使用DatetimeIndexIndex构造函数,并传递一个日期时间对象列表:

代码语言:javascript复制
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() 函数来创建DatetimeIndexdate_range的默认频率是日历日,而bdate_range的默认频率是工作日

代码语言:javascript复制
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_rangebdate_range 这样的便利函数可以利用各种频率别名:

代码语言:javascript复制
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_rangebdate_range 可以轻松生成一系列日期范围,使用各种参数组合如 startendperiodsfreq。开始和结束日期是严格包含的,因此不会生成指定范围之外的日期:

代码语言:javascript复制
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') 

指定 startendperiods 将生成一系列从 startend 的均匀间隔日期,结果为 DatetimeIndex 中的 periods 个元素:

代码语言:javascript复制
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_range 还可以通过使用 weekmaskholidays 参数生成一系列自定义频率日期。只有在传递自定义频率字符串时才会使用这些参数。

代码语言:javascript复制
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') 

另请参阅

自定义工作日 ### 自定义频率范围

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

代码语言:javascript复制
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 年:

代码语言:javascript复制
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 年。不同分辨率���以通过 as_unit 相互转换。

另请参阅

表示超出范围的跨度

索引

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

  • 大量各种偏移量的日期范围在内部预先计算并缓存,以便快速生成后续日期范围(只需抓取一个片段)。
  • 在 pandas 对象上使用 shift 方法进行快速移位。
  • 具有相同频率的重叠 DatetimeIndex 对象的并集非常快速(对于快速数据对齐很重要)。
  • 通过属性(如 yearmonth 等)快速访问日期字段。
  • snap 等正规化函数和非常快速的 asof 逻辑。

DatetimeIndex 对象具有常规 Index 对象的所有基本功能,以及一系列用于简化频率处理的高级时间序列特定方法。

另请参阅

重新索引方法

注意

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

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

代码语言:javascript复制
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') 

0 人点赞