在多重索引上使用切片器

6

I've got a dataframe of the form:

Contract  Date      
201501    2014-04-29    1416.0
          2014-04-30    1431.1
          2014-05-01    1430.6
          2014-05-02    1443.9
          2014-05-05    1451.6
          2014-05-06    1461.4
          2014-05-07    1456.0
          2014-05-08    1441.1
          2014-05-09    1437.8
          2014-05-12    1445.2
          2014-05-13    1458.2
          2014-05-14    1487.6
          2014-05-15    1477.6
          2014-05-16    1467.9
          2014-05-19    1484.9
          2014-05-20    1470.5
          2014-05-21    1476.9
          2014-05-22    1490.0
          2014-05-23    1473.3
          2014-05-27    1462.5
          2014-05-28    1456.3
          2014-05-29    1460.5
201507    2014-05-30    1463.5
          2014-06-02    1447.5
          2014-06-03    1444.4
          2014-06-04    1444.7
          2014-06-05    1455.9
          2014-06-06    1464.0

当合同和日期是整数(int)和日期时间(datetime64)类型的索引时。

我想要的是选择一个日期范围。实现方法如下:

df.reset_index('Contract', drop=True).loc['2014-09']

但我不喜欢这样做,因为它会丢失索引/使用起来不太愉快(我要做很多这样的事情)。

我认为我应该可以这样做:

df.loc[:,'2014-09']

要恢复2014年9月的所有数据。实际上,这是不起作用的。我只能通过选择单个日期来执行操作:
df.loc[:,'2014-09-02']

为什么我的多重索引切片器不起作用?
3个回答

3
Pandas需要明确指定你是选择列还是多重索引的子级。在这种情况下,df.loc[:,'2014-09']会失败,因为Pandas尝试获取所有行,然后查找标签为'2014-09'的列(该列不存在)。
相反,你需要给出多重索引的两个级别和列标签/切片。
要从示例中选择所有的2014年5月数据,可以编写以下代码:
>>> df.loc[(slice(None), '2014-05'), :]                            
Contract Date              
201501   2014-05-01  1430.6
         2014-05-02  1443.9
         2014-05-05  1451.6
         2014-05-06  1461.4
         2014-05-07  1456.0
         2014-05-08  1441.1
         2014-05-09  1437.8
         2014-05-12  1445.2
         2014-05-13  1458.2
         2014-05-14  1487.6
         2014-05-15  1477.6
         2014-05-16  1467.9
         2014-05-19  1484.9
         2014-05-20  1470.5
         2014-05-21  1476.9
         2014-05-22  1490.0
         2014-05-23  1473.3
         2014-05-27  1462.5
         2014-05-28  1456.3
         2014-05-29  1460.5
201507   2014-05-30  1463.5

这里的[(slice(None),'2014-05'), :]转化为对于行的切片[:, '2014-05']和对于列的切片[:]

pd.IndexSlice对象被引入使得这些切片语义更加容易:

>>> idx = pd.IndexSlice
>>> df.loc[idx[:, '2014-05'], :]
# same slice of DataFrame

这个一定能行吗?因为我试了一下,它似乎只返回了所有的数据,而不是相应的切片(所以它在你的有限数据集示例中可以工作,但在我的扩展数据集上无法工作)。 - cjm2671
@cjm2671,你能否尝试使用你的样本数据集来复现它? - MaxU - stand with Ukraine
1
@cjm2671:它应该可以工作;除非您在第二级别中也切片,使用早期日期,例如df.loc[idx[:, '2013-05':], :],否则我不确定所有行将如何返回。正如MaxU建议的那样,也许您可以在较小的数据集上重现此问题,以便我们可以进一步调查? - Alex Riley
@ajcr,你知道为什么 df.loc[(slice(None), '2014-05'), :] 能够正常工作而 df.ix[(slice(None), '2014-05'), :] 不能吗? - MaxU - stand with Ukraine
@MaxU:说实话,我也不确定为什么会这样。从设计的角度来看,也许应该让“ix”函数能够正常工作,或者至少在给定多重索引切片器时产生更有帮助的错误信息。 - Alex Riley
显示剩余2条评论

2

您可以使用pd.Indexslice基于每个MultiIndexlevel范围进行选择,如下所示(请参见文档):

idx = pd.IndexSlice
df.loc[idx[:, '2014-05'], :]

获取:

Contract Date              
201501   2014-05-01  1430.6
         2014-05-02  1443.9
         2014-05-05  1451.6
         2014-05-06  1461.4
         2014-05-07  1456.0
         2014-05-08  1441.1
         2014-05-09  1437.8
         2014-05-12  1445.2
         2014-05-13  1458.2
         2014-05-14  1487.6
         2014-05-15  1477.6
         2014-05-16  1467.9
         2014-05-19  1484.9
         2014-05-20  1470.5
         2014-05-21  1476.9
         2014-05-22  1490.0
         2014-05-23  1473.3
         2014-05-27  1462.5
         2014-05-28  1456.3
         2014-05-29  1460.5
201507   2014-05-30  1463.5

1
您可以使用 .dt访问器 来提取所有九月份的值,如下所示:
df.loc[(pd.to_datetime(df['Date']).dt.month == 9)]

计时约束:
timeit df.loc[(pd.to_datetime(df['Date']).dt.month == 5)]
1000 loops, best of 3: 796 µs per loop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接