部分字符串索引
可以将日期和解析为时间戳的字符串作为索引参数传递:
代码语言:javascript复制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
为了方便访问更长的时间序列,也可以将年份或年份和月份作为字符串传递:
代码语言:javascript复制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
这种切片方式也适用于具有DatetimeIndex
的DataFrame
。由于部分字符串选择是一种标签切片的形式,端点将被包括在内。这将包括在包含日期上匹配时间:
警告
使用单个字符串对DataFrame
行进行索引(例如frame[dtstring]
)已在 pandas 1.2.0 中弃用(由于不确定是索引行还是选择列而存在歧义),并将在将来的版本中删除。相应的.loc
(例如`frame.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]
这从月初开始,包括月底的日期和时间:
代码语言:javascript复制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]
这指定了一个包括最后一天所有时间的停止时间:
代码语言:javascript复制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]
这指定了一个精确的停止时间(与上述不同):
代码语言:javascript复制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]
我们在包含的端点上停止,因为它是索引的一部分:
代码语言:javascript复制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
部分字符串索引也适用于具有MultiIndex
的DataFrame
:
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 偏移。
代码语言:javascript复制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
```### 切片 vs. 精确匹配
使用作为索引参数的相同字符串,根据索引的分辨率,可以将其视为切片或精确匹配。如果字符串比索引不准确,则将其视为切片,否则视为精确匹配。
考虑一个具有分钟分辨率索引的`Series`对象:
```py
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
具有分钟分辨率(或更精确)的时间戳字符串会给出一个标量,即不会转换为切片。
代码语言:javascript复制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
如果将时间戳字符串视为切片,它也可以用于使用.loc[]
索引DataFrame
。
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
警告
但是,如果将字符串视为精确匹配,DataFrame
的[]
中的选择将按列而不是按行进行,参见索引基础知识。例如,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
取决于周期的“准确性”,换句话说,与索引的分辨率相比间隔的具体性。相比之下,使用Timestamp
或datetime
对象进行索引是精确的,因为这些对象具有确切的含义。这些也遵循包括两个端点的语义。
这些Timestamp
和datetime
对象具有精确的小时,分钟
和秒
,即使它们没有明确指定(它们为0
)。
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]
没有默认值。
代码语言:javascript复制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
假设 DatetimeIndex
中未指定日期组件的任何部分的值为 0,而切片则返回任何部分匹配的日期:
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)
部分字符串索引
可以将日期和解析为时间戳的字符串作为索引参数传递:
代码语言:javascript复制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
为了方便访问更长的时间序列,你也可以将年份或年份和月份作为字符串传递:
代码语言:javascript复制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
这种类型的切片也适用于具有 DatetimeIndex
的 DataFrame
。由于部分字符串选择是一种标签切片形式,因此端点将被包括在内。这将包括在包含日期的匹配时间:
警告
使用单个字符串通过 getitem(例如 frame[dtstring]
)对 DataFrame
行进行索引在 pandas 1.2.0 中已弃用(因为它存在将行索引与列选择混淆的歧义),并将在将来的版本中删除。使用 .loc
相当于(例如 frame.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]
这从月份的第一天开始,并包括月份的最后日期和时间:
代码语言:javascript复制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]
这指定了一个包含最后一天所有时间的停止时间:
代码语言:javascript复制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]
这指定了一个确切的停止时间(与上述不同):
代码语言:javascript复制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]
我们在包含的结束点停止,因为它是索引的一部分:
代码语言:javascript复制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
部分字符串索引也适用于具有 MultiIndex
的 DataFrame
:
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 偏移量。
代码语言:javascript复制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
切片 vs. 精确匹配
使用相同的字符串作为索引参数时,根据索引的分辨率,它可以被视为切片或精确匹配。如果字符串的精度低于索引,则将其视为切片,否则视为精确匹配。
考虑一个具有分钟分辨率索引的 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
具有分钟分辨率(或更高)的时间戳字符串会给出一个标量,即不会转换为切片。
代码语言:javascript复制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
如果时间戳字符串被视为切片,则也可以使用 .loc[]
索引 DataFrame
。
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
警告
但是,如果字符串被视为精确匹配,则 DataFrame
的 []
中的选择将以列而不是行为基础,参见 索引基础知识。例如,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
取决于“精确度”(即间隔相对于索引分辨率的特定程度)。相比之下,使用Timestamp
或datetime
对象进行索引是精确的,因为这些对象具有确切的含义。这些也遵循包含两个端点的语义。
这些Timestamp
和datetime
对象具有确切的小时、分钟
和秒
,即使它们没有明确指定(它们为0
)。
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]
没有默认值。
代码语言:javascript复制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
假定在DatetimeIndex
中的任何未指定的日期组件中为 0 值,与切片不同,后者返回任何部分匹配的日期:
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
等时间戳集合中访问。
属性 | 描述 |
---|---|
year | 日期时间的年份 |
month | 日期时间的月份 |
day | 日期时间的天数 |
hour | 日期时间的小时数 |
minute | 日期时间的分钟数 |
second | 日期时间的秒数 |
microsecond | 日期时间的微秒数 |
nanosecond | 日期时间的纳秒数 |
date | 返回 datetime.date(不包含时区信息) |
time | 返回 datetime.time(不包含时区信息) |
timetz | 返回带有时区信息的本地时间 datetime.time |
dayofyear | 年份中的日序数 |
day_of_year | 年份中的日序数 |
weekofyear | 年份中的周序数 |
week | 年份中的周序数 |
dayofweek | 一周中的星期几,星期一=0,星期日=6 的编号 |
day_of_week | 一周中的星期几,星期一=0,星期日=6 的编号 |
weekday | 一周中的星期几,星期一=0,星期日=6 的编号 |
quarter | 日期的季度:1 表示 1 月至 3 月,2 表示 4 月至 6 月,依此类推 |
days_in_month | 日期所在月份的天数 |
is_month_start | 逻辑值,指示是否月份的第一天(由频率定义) |
is_month_end | 逻辑值,指示是否月份的最后一天(由频率定义) |
is_quarter_start | 逻辑值,指示是否季度的第一天(由频率定义) |
is_quarter_end | 逻辑值,指示是否季度的最后一天(由频率定义) |
is_year_start | 逻辑值,指示是否年份的第一天(由频率定义) |
is_year_end | 逻辑值,指示是否年份的最后一天(由频率定义) |
is_leap_year | 逻辑值,指示日期是否属于闰年 |
此外,如果您有一个具有日期时间值的 Series
,则可以通过 .dt
访问器访问这些属性,详细信息请参见 .dt 访问器 部分。
您可以从 ISO 8601 标准获得 ISO 年份的年、周和日组件:
代码语言:javascript复制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'
)用于指定定义的频率:
- 当使用
date_range()
时,DatetimeIndex
中的日期时间间隔。 -
Period
或PeriodIndex
的频率。
这些频率字符串映射到一个 DateOffset
对象及其子类。DateOffset
类似于 Timedelta
,表示一段时间的持续时间,但遵循特定的日历持续时间规则。例如,Timedelta
的一天始终会将 datetimes
增加 24 小时,而 DateOffset
的一天将会将 datetimes
增加到次日的同一时间,无论一天是否由于夏令时而表示为 23、24 或 25 小时。但是,所有表示小时或更小单位(Hour
、Minute
、Second
、Milli
、Micro
、Nano
)的 DateOffset
子类都像 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 or CustomBusinessDay | 'C' | 自定义工作日 |
Week | 'W' | 一周,可选择以一周中的某一天为锚点 |
WeekOfMonth | 'WOM' | 每月第 x 周的第 y 天 |
LastWeekOfMonth | 'LWOM' | 每月最后一周的第 x 天 |
MonthEnd | 'ME' | 日历月末 |
MonthBegin | 'MS' | 日历月开始 |
BMonthEnd or BusinessMonthEnd | 'BME' | 工作月末 |
BMonthBegin or BusinessMonthBegin | 'BMS' | 工作月开始 |
CBMonthEnd or CustomBusinessMonthEnd | 'CBME' | 自定义工作月末 |
CBMonthBegin or CustomBusinessMonthBegin | 'CBMS' | 自定义工作月开始 |
SemiMonthEnd | 'SME' | 每月 15 日(或其他日子)和日历月末 |
SemiMonthBegin | 'SMS' | 15 号(或其他日期)和月初 |
QuarterEnd | 'QE' | 季度末 |
QuarterBegin | 'QS' | 季度初 |
BQuarterEnd | 'BQE | 业务季度末 |
BQuarterBegin | 'BQS' | 业务季度初 |
FY5253Quarter | 'REQ' | 零售(又称 52-53 周)季度 |
YearEnd | 'YE' | 年末 |
YearBegin | 'YS' or 'BYS' | 年初 |
BYearEnd | 'BYE' | 业务年末 |
BYearBegin | 'BYS' | 业务年初 |
FY5253 | 'RE' | 零售(又称 52-53 周)年 |
Easter | None | 复活节假期 |
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
或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
),则可以像使用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)
```### 自定义工作日
`CDay`或`CustomBusinessDay`类提供了一个参数化的`BusinessDay`类,可以用于创建自定义的工作日日历,考虑到当地的假日和当地的周末惯例。
作为一个有趣的例子,让我们看看埃及,那里遵循星期五到星期六的周末。
```py
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')
让我们映射到星期几的名称:
代码语言:javascript复制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
假日日历可用于提供假日列表。有关更多信息,请参阅假日日历部分。
代码语言:javascript复制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')
可以像通常一样定义尊重某个假日日历的月度偏移量。
代码语言:javascript复制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
时间。参数必须是具有hour:minute
表示或datetime.time
实例的str
。将秒、微秒和纳秒指定为营业时间会导致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
表示午夜营业时间。在这种情况下,营业时间超过午夜并延伸到第二天。有效的营业时间由是否从有效的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)下,2014-08-01 17:00
和2014-08-04 09:00
之间没有间隙(0 分钟)。
# 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
是BusinessHour
和CustomBusinessDay
的混合体,允许您指定任意假期。CustomBusinessHour
的工作方式与BusinessHour
相同,只是跳过指定的自定义假期。
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')
```### 偏移别名
给出了一些常见时间序列频率的字符串别名。我们将这些别名称为*偏移别名*。
| Alias | 描述 |
| --- | --- |
| B | 营业日频率 |
| C | 自定义营业日频率 |
| D | 日历日频率 |
| W | 每周频率 |
| ME | 月末频率 |
| SME | 半月末频率(15 号和月末) |
| BME | 营业月末频率 |
| CBME | 自定义营业月末频率 |
| MS | 月初频率 |
| SMS | 半月初频率(1 号和 15 号) |
| BMS | 营业月初频率 |
| CBMS | 自定义业务月开始频率 |
| QE | 季度结束频率 |
| BQE | 业务季度结束频率 |
| QS | 季度开始频率 |
| BQS | 业务季度开始频率 |
| YE | 年结束频率 |
| BYE | 营业年结束频率 |
| YS | 年开始频率 |
| BYS | 营业年开始频率 |
| h | 每小时频率 |
| bh | 营业时间频率 |
| cbh | 自定义营业小时频率 |
| min | 每分钟频率 |
| s | 每秒频率 |
| ms | 毫秒 |
| us | 微秒 |
| ns | 纳秒 |
自版本 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`,返回的时间戳将停止在上一个有效时间戳处。
例如,对于偏移`MS`,如果`start_date`不是月份的第一天,则返回的时间戳将从下个月的第一天开始。如果`end_date`不是月份的第一天,则最后返回的时间戳将是对应月份的第一天。
```py
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
的上一个值) ### 期别别名
一些常见的时间序列频率都有一些字符串别名。我们将这些别名称为周期别名。
别名 | 描述 |
---|---|
B | 工作日频率 |
D | 日历日频率 |
W | 每周频率 |
M | 每月频率 |
Q | 每季频率 |
Y | 每年频率 |
h | 每小时频率 |
min | 每分钟频率 |
s | 每秒频率 |
ms | 毫秒 |
us | 微秒 |
ns | 纳秒 |
自版本 2.2.0 开始已弃用:别名A
,H
,T
,S
,L
,U
和N
已弃用,转而使用别名Y
,h
,min
,s
,ms
,us
和ns
。
组合别名
正如我们之前所见,别名和偏移实例在大多数函数中是可以互换的:
代码语言:javascript复制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')
您可以组合日和日内偏移量:
代码语言:javascript复制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-SUN | 每周频率(星期日)。与‘W’相同 |
W-MON | 每周频率(星期一) |
W-TUE | 每周频率(星期二) |
W-WED | 每周频率(星期三) |
W-THU | 每周频率(星期四) |
W-FRI | 每周频率(星期五) |
W-SAT | 每周频率(星期六) |
(B)Q(E)(S)-DEC | 季度频率,年份以十二月结束。与‘QE’相同 |
(B)Q(E)(S)-JAN | 季度频率,年份以一月结束 |
(B)Q(E)(S)-FEB | 季度频率,年份以二月结束 |
(B)Q(E)(S)-MAR | 季度频率,年份以三月结束 |
(B)Q(E)(S)-APR | 季度频率,年份以四月结束 |
(B)Q(E)(S)-MAY | 季度频率,年份以五月结束 |
(B)Q(E)(S)-JUN | 季度频率,年份以六月结束 |
(B)Q(E)(S)-JUL | 季度频率,年份以七月结束 |
(B)Q(E)(S)-AUG | 季度频率,年份以八月结束 |
(B)Q(E)(S)-SEP | 季度频率,年份以九月结束 |
(B)Q(E)(S)-OCT | 季度频率,年份以十月结束 |
(B)Q(E)(S)-NOV | 季度频率,年份以十一月结束 |
(B)Y(E)(S)-DEC | 每年频率,锚定在十二月底。与‘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)-MAY | 每年频率,锚定在五月底 |
(B)Y(E)(S)-JUN | 每年频率,锚定在六月底 |
(B)Y(E)(S)-JUL | 每年频率,锚定在七月底 |
(B)Y(E)(S)-AUG | 每年频率,锚定在八月底 |
(B)Y(E)(S)-SEP | 每年频率,锚定在九月底 |
(B)Y(E)(S)-OCT | 每年频率,锚定在十月底 |
(B)Y(E)(S)-NOV | 每年频率,锚定在十一月底 |
这些可以用作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
类属性确定生成假期的日期范围。应该在AbstractHolidayCalendar
类上进行覆盖,以使该范围适用于所有日历子类。USFederalHolidayCalendar
是唯一存在的日历,主要用作开发其他日历的示例。
对于固定日期(例如美国阵亡将士纪念日或 7 月 4 日)的假期,观察规则确定了如果假期落在周末或其他非观察日时,该假期何时被观察。定义的观察规则有:
Rule | Description |
---|---|
nearest_workday | 将周六移至周五,周日移至周一 |
sunday_to_monday | 将周日移至下一个周一 |
next_monday_or_tuesday | 将周六移至周一,周日/周一移至周二 |
previous_friday | 将周六和周日移至上一个星期五” |
next_monday | 将周六和周日移至下一个周一 |
假期和假期日历如何定义的示例:
代码语言:javascript复制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')
范围由AbstractHolidayCalendar
的start_date
和end_date
类属性定义。默认值如下所示。
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')
可以通过将属性设置为 datetime/Timestamp/string 来覆盖这些日期。
代码语言:javascript复制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)>)]
参数化偏移
创建时,某些偏移可以“参数化”以产生不同的行为。例如,用于生成每周数据的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
或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
),则可以像 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)
自定义工作日
CDay
或 CustomBusinessDay
类提供了一个参数化的 BusinessDay
类,可用于创建考虑到本地节假日和本地周末惯例的定制工作日日历。
作为一个有趣的例子,让我们看一下埃及,那里观察到周五至周六的周末。
代码语言:javascript复制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')
让我们映射到工作日名称:
代码语言:javascript复制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
节假日日历可用于提供节假日列表。有关更多信息,请参阅节假日日历部分。
代码语言:javascript复制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')
可以以通常的方式定义尊重某个节假日日历的月度偏移量。
代码语言:javascript复制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
时间。参数必须是具有 hour:minute
表示或 datetime.time
实例的 str
。将秒、微秒和纳秒指定为营业时间会导致 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
表示午夜营业时间。在这种情况下,营业时间超过午夜并且重叠到第二天。有效的营业时间由是否从有效的 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)下,2014-08-01 17:00
和 2014-08-04 09:00
之间没有间隙(0 分钟)。
# 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
是BusinessHour
和CustomBusinessDay
的混合体,允许您指定任意假期。CustomBusinessHour
的工作方式与BusinessHour
相同,只是它跳过指定的自定义假期。
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')
偏移别名
一些常见的时间序列频率有一些字符串别名。我们将这些别名称为偏移别名。
别名 | 描述 |
---|---|
B | 工作日频率 |
C | 自定义工作日频率 |
D | 日历日频率 |
W | 每周频率 |
ME | 月结束频率 |
SME | 半月结束频率(15 号和月底) |
BME | 业务月结束频率 |
CBME | 自定义业务月结束频率 |
MS | 月初频率 |
SMS | 半月初频率(1 号和 15 号) |
BMS | 业务月初频率 |
CBMS | 自定义业务月初频率 |
QE | 季度结束频率 |
BQE | 业务季度结束频率 |
QS | 季度开始频率 |
BQS | 业务季度开始频率 |
YE | 年结束频率 |
BYE | 业务年结束频率 |
YS | 年开始频率 |
BYS | 业务年开始频率 |
h | 小时频率 |
bh | 工作小时频率 |
cbh | 自定义工作小时频率 |
min | 分钟频率 |
s | 每秒频率 |
ms | 毫秒 |
us | 微秒 |
ns | 纳秒 |
自 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
也是一样,返回的时间戳将在前一个有效时间戳停止。
例如,对于偏移量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
的前一个值)
周期别名
一些常见时间序列频率的字符串别名被赋予了。我们将这些别名称为周期别名。
别名 | 描述 |
---|---|
B | 工作日频率 |
D | 日历日频率 |
W | 每周频率 |
M | 每月频率 |
Q | 季度频率 |
Y | 每年频率 |
h | 每小时频率 |
min | 每分钟频率 |
s | 每秒频率 |
ms | 毫秒 |
us | 微秒 |
ns | 纳秒 |
自版本 2.2.0 起弃用:别名A
、H
、T
、S
、L
、U
和N
已被弃用,取而代之的是别名Y
、h
、min
、s
、ms
、us
和ns
。
合并别名
正如我们之前所看到的,别名和偏移实例在大多数函数中是可互换的:
代码语言:javascript复制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')
您可以组合一起日和日内偏移:
代码语言:javascript复制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-SUN | 每周频率(周日)。与‘W’相同 |
W-MON | 每周频率(周一) |
W-TUE | 每周频率(周二) |
W-WED | 每周频率(周三) |
W-THU | 每周频率(周四) |
W-FRI | 每周频率(周五) |
W-SAT | 每周频率(周六) |
(B)Q(E)(S)-DEC | 季度频率,年底在十二月。与‘QE’相同 |
(B)Q(E)(S)-JAN | 季度频率,年底在一月 |
(B)Q(E)(S)-FEB | 季度频率,年底在二月 |
(B)Q(E)(S)-MAR | 季度频率,年底在三月 |
(B)Q(E)(S)-APR | 季度频率,年底在四月 |
(B)Q(E)(S)-MAY | 季度频率,年底在五月 |
(B)Q(E)(S)-JUN | 季度频率,年底在六月 |
(B)Q(E)(S)-JUL | 季度频率,年底在七月 |
(B)Q(E)(S)-AUG | 季度频率,年底在八月 |
(B)Q(E)(S)-SEP | 季度频率,年底在九月 |
(B)Q(E)(S)-OCT | 季度频率,年底在十月 |
(B)Q(E)(S)-NOV | 季度频率,年底在十一月 |
(B)Y(E)(S)-DEC | 年度频率,锚定在十二月底。与‘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)-MAY | 年度频率,锚定在五月底 |
(B)Y(E)(S)-JUN | 年度频率,锚定在六月底 |
(B)Y(E)(S)-JUL | 年度频率,锚定在七月底 |
(B)Y(E)(S)-AUG | 年度频率,8 月底锚定 |
(B)Y(E)(S)-SEP | 年度频率,9 月底锚定 |
(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
类属性确定生成假期的日期范围。这些应该在AbstractHolidayCalendar
类上被覆盖,以使范围适用于所有日历子类。USFederalHolidayCalendar
是唯一存在的日历,主要用作开发其他日历的示例。
对于在固定日期发生的假期(例如,美国阵亡将士纪念日或 7 月 4 日),一个遵守规则确定了如果假期落在周末或其他非观察日时如何观察。定义的遵守规则有:
Rule | Description |
---|---|
nearest_workday | 将周六移动到周五,周日移动到周一 |
sunday_to_monday | 将周日移动到下一个周一 |
next_monday_or_tuesday | 将周六移动到周一,周日/周一移动到周二 |
previous_friday | 将周六和周日移动到上一个星期五” |
next_monday | 将周六和周日移动到下一个周一 |
定义假期和假日日历的示例:
代码语言:javascript复制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')
范围由AbstractHolidayCalendar
的start_date
和end_date
类属性定义。默认值如下所示。
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')
这些日期可以通过将属性设置为 datetime/Timestamp/string 来覆盖。
代码语言:javascript复制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)>)]
与时间序列相关的实例方法
Shifting / lagging
有时可能需要将时间序列中的值向前或向后移动。用于此操作的方法是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
的对象,也可以是一个 offset alias。
当指定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
时,前导条目不再是 NaN,因为数据没有被重新对齐。
频率转换
更改频率的主要函数是asfreq()
方法。对于DatetimeIndex
,这基本上只是reindex()
的一个薄包装,它生成一个date_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
和reindex
相关的是fillna()
,该方法在 missing data section 中有文档记录。
转换为 Python 日期时间
DatetimeIndex
可以使用to_pydatetime
方法转换为 Python 本机datetime.datetime
对象的数组。
Shifting / lagging
有时可能需要将时间序列中的值向前或向后移动。用于此操作的方法是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
的对象,也可以是一个 offset alias。
当指定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
时,前导条目不再是 NaN,因为数据没有被重新对齐。
频率转换
更改频率的主要函数是 asfreq()
方法。对于 DatetimeIndex
,这基本上只是一个薄的但方便的包装器,围绕 reindex()
生成一个 date_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
和 reindex
相关的是 fillna()
,该方法在 缺失数据部分有详细说明。
转换为 Python 日期时间
DatetimeIndex
可以使用 to_pydatetime
方法转换为 Python 原生的 datetime.datetime
对象数组。
重采样
pandas 在频率转换期间执行重采样操作(例如,将秒数据转换为 5 分钟数据)具有简单、强大和高效的功能。这在金融应用中非常常见,但不限于此。
resample()
是基于时间的分组,后跟对每个组的减少方法。查看一些 示例 以了解一些高级策略。
resample()
方法可以直接从 DataFrameGroupBy
对象中使用,请参阅 groupby 文档。
基础知识
代码语言:javascript复制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
可以设置为‘left’或‘right’以指定间隔的哪一端是封闭的:
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
警告
对于所有频率偏移,默认值为‘left’,除了‘ME’、‘YE’、‘QE’、‘BME’、‘BYE’、‘BQE’和‘W’,它们的默认值都是‘right’。
这可能会意外地导致向前查看,其中稍后时间的值被拉回到先前时间,如下例所示,使用 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
注意星期日的值被拉回到上一个星期五。要使星期日的值推到星期一,可以使用以下方法:
代码语言:javascript复制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
可以设置为‘start’或‘end’(详细信息如下)。它指定了低频周期如何转换为高频周期。
上采样
对于上采样,您可以指定一种上采样方式和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_method
为None
,那么中间值将被填充为NaN
。
由于resample
是基于时间的 groupby,以下是一种有效地仅重新采样不全为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)
如果我们想要重新采样到系列的完整范围:
代码语言:javascript复制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
相反,我们可以仅重新采样那些具有点的组,如下所示:
代码语言:javascript复制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 和 window API,Resampler
可以被选择性地重新采样。
对于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 方法,我们可以选择特定的列或多列。
代码语言:javascript复制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
在重新采样的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: