关于python:使用.loc访问器的pandas日期时间索引的布尔掩码

Boolean mask from pandas datetime index using .loc accessor

假设以下玩具代码:

1
2
3
4
5
6
import numpy as np
import pandas as pd
rng = pd.date_range('1/1/2011', periods=72, freq='H')
avec = np.random.rand(len(rng))
bvec = np.random.rand(len(rng))
df = pd.DataFrame({"A":avec,"B":bvec}, index=rng)

我现在可以用

选择时间间隔的一部分

1
df.loc["2011-01-02",:]

有没有办法有效地访问对应于结果切片的布尔掩码,即:

1
2
3
4
5
6
7
8
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False], dtype=bool)

我已经尝试过早期 stackoverflow 答案中的建议,但 df.index.date 需要很长时间才能在我的数据集上运行...


如果性能很重要,链 2 布尔掩码:

1
(df.index >="2011-01-02") & (df.index <"2011-01-03")


您可以提取索引的 numpy 表示并与 np.datetime64 对象进行比较:

1
2
3
4
5
import numpy as np
from datetime import datetime

(df.index.values >= np.datetime64(datetime.strptime("2011-01-02", '%Y-%m-%d'))) & \\
(df.index.values < np.datetime64(datetime.strptime("2011-01-03", '%Y-%m-%d')))

行为注意事项

上述解决方案特定于问题中的查询。正如@Jeff 指出的那样, datetime 的字符串表示使用部分索引。所以使用 numpy 应该只在特定情况下使用。

有关详细信息,请参阅关于 datetime 索引的 pandas 文档。

性能基准测试

1
2
3
4
5
6
7
8
9
10
11
12
13
df = pd.concat([df]*1000)

%timeit (df.index >="2011-01-02") & (df.index <"2011-01-03")

%timeit (df.index.values >= np.datetime64(datetime.strptime("2011-01-02", '%Y-%m-%d'))) & \\
        (df.index.values < np.datetime64(datetime.strptime("2011-01-03", '%Y-%m-%d')))

assert ((df.index >="2011-01-02") & (df.index <"2011-01-03") == \\
        (df.index.values >= np.datetime64(datetime.strptime("2011-01-02", '%Y-%m-%d'))) & \\
        (df.index.values < np.datetime64(datetime.strptime("2011-01-03", '%Y-%m-%d')))).all()

# 1.21 ms ± 23 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# 527 μs ± 11.3 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)


IIUC,你可以这样做:

1
2
3
4
5
6
7
8
9
10
df.index.isin(df.loc["2011-01-02",:].index)

array([False, False, False, False, False, False, False, False, False,
   False, False, False, False, False, False, False, False, False,
   False, False, False, False, False, False,  True,  True,  True,
    True,  True,  True,  True,  True,  True,  True,  True,  True,
    True,  True,  True,  True,  True,  True,  True,  True,  True,
    True,  True,  True, False, False, False, False, False, False,
   False, False, False, False, False, False, False, False, False,
   False, False, False, False, False, False, False, False, False], dtype=bool)

但是,@jezrael 的回答更快。我将此作为替代解决方案。这提供的唯一便利是如果您想按名称而不是日期字符串引用切片数据帧,例如:

1
2
3
4
# named slice of your original dataframe:
sliced_df = df.loc["2011-01-02",:]
# get boolean array:
df.index.isin(sliced_df.index)