【问题标题】:pandas: slice a MultiIndex by range of secondary indexpandas:按二级索引范围对 MultiIndex 进行切片
【发布时间】:2012-11-14 23:29:30
【问题描述】:

我有一个带有这样的 MultiIndex 的系列:

import numpy as np
import pandas as pd

buckets = np.repeat(['a','b','c'], [3,5,1])
sequence = [0,1,5,0,1,2,4,50,0]

s = pd.Series(
    np.random.randn(len(sequence)), 
    index=pd.MultiIndex.from_tuples(zip(buckets, sequence))
)

# In [6]: s
# Out[6]: 
# a  0    -1.106047
#    1     1.665214
#    5     0.279190
# b  0     0.326364
#    1     0.900439
#    2    -0.653940
#    4     0.082270
#    50   -0.255482
# c  0    -0.091730

我想获取第二个索引 ('sequence') 介于 2 和 10 之间的 s['b'] 值。

在第一个索引上切片可以正常工作:

s['a':'b']
# Out[109]: 
# bucket  value
# a       0        1.828176
#         1        0.160496
#         5        0.401985
# b       0       -1.514268
#         1       -0.973915
#         2        1.285553
#         4       -0.194625
#         5       -0.144112

但不是在第二种情况下,至少通过两种最明显的方式:

1) 这将返回元素 1 到 4,与索引值无关

s['b'][1:10]

# In [61]: s['b'][1:10]
# Out[61]: 
# 1     0.900439
# 2    -0.653940
# 4     0.082270
# 50   -0.255482

但是,如果我反转索引并且第一个索引是整数,第二个索引是字符串,它可以工作:

In [26]: s
Out[26]: 
0   a   -0.126299
1   a    1.810928
5   a    0.571873
0   b   -0.116108
1   b   -0.712184
2   b   -1.771264
4   b    0.148961
50  b    0.089683
0   c   -0.582578

In [25]: s[0]['a':'b']
Out[25]: 
a   -0.126299
b   -0.116108

【问题讨论】:

  • 要用 Python 3 运行这段代码,需要修改:index=pd.MultiIndex.from_tuples(list(zip(buckets, sequence)))(注意新的list)
  • 如果您有兴趣了解更多关于切片和过滤多索引数据帧的信息,请查看我的帖子:How do I slice or filter MultiIndex DataFrame levels?。谢谢!

标签: python pandas


【解决方案1】:

自 pandas 0.15.0 起,此功能有效:

s.loc['b', 2:10]

输出:

b  2   -0.503023
   4    0.704880
dtype: float64

DataFrame 略有不同 (source):

df.loc(axis=0)['b', 2:10]

【讨论】:

  • 这是最新的答案。它还很好地指出了黑白系列和 df 的细微差别。谢谢!
【解决方案2】:

作为Robbie-Clarken answers,从0.14开始你可以传递slice in the tuple you pass to loc:

In [11]: s.loc[('b', slice(2, 10))]
Out[11]:
b  2   -0.65394
   4    0.08227
dtype: float64

确实,您可以为每个级别传递一个切片:

In [12]: s.loc[(slice('a', 'b'), slice(2, 10))]
Out[12]:
a  5    0.27919
b  2   -0.65394
   4    0.08227
dtype: float64

注意:切片是包含的。


旧答案:

您也可以使用:

s.ix[1:10, "b"]

(最好在单个 ix/loc/iloc 中执行,因为此版本允许分配。)

此答案是在 2013 年初的 introduction of iloc 之前编写的,即位置/整数位置 - 在这种情况下可能是首选。创建它的原因是为了消除整数索引 pandas 对象中的歧义,并且更具描述性:“我正在对位置进行切片”。

s["b"].iloc[1:10]

也就是说,我有点不同意 ix 的文档:

最健壮和一致的方式

不是,最一致的方式是描述你在做什么:

  • 使用 loc 作为标签
  • 使用 iloc 定位
  • 两者都使用 ix(如果你真的需要的话)

记住zen of python:

显式优于隐式

【讨论】:

  • 感觉应该有一种方法可以一次性完成(使用 loc / 不使用链接),但是分配(s['b'].ix[1:10])有效,所以我想没关系。
  • 请@Andy-Hayden 更新您的回答以符合新的 pandas API。正如 Robbie-Clarken 所示:推荐使用 loc 和 slice 索引。
  • @mithrado 感谢您指出这一点,我一直想检查我所有的熊猫答案并更新它们。我需要编写一个脚本,因为手动操作太多。 ://
  • 答案中没有说的东西,但那是我真正想要的:“您可以使用slice(None) 选择该级别的所有内容。您不需要指定所有更深层次,它们将被暗示为slice(None)"。来源:Pandas docs.
【解决方案3】:

从 pandas 0.14.0 开始,可以通过提供 .loc 一个包含 slice 对象的元组来 slice multi-indexed objects:

In [2]: s.loc[('b', slice(2, 10))]
Out[2]:
b  2   -1.206052
   4   -0.735682
dtype: float64

【讨论】:

  • slice(None) 选择该级别的所有内容。见Using slicers。
  • 请注意,在loc 中使用slice() 仍然具有包容性。
【解决方案4】:

我能想到的最好方法是在这种情况下使用“选择”。尽管它甚至在文档中说“只有在没有更直接的方法时才应使用此方法。”

Indexing and selecting data

In [116]: s
Out[116]: 
a  0     1.724372
   1     0.305923
   5     1.780811
b  0    -0.556650
   1     0.207783
   4    -0.177901
   50    0.289365
   0     1.168115

In [117]: s.select(lambda x: x[0] == 'b' and 2 <= x[1] <= 10)
Out[117]: b  4   -0.177901

【讨论】:

  • 令人惊讶的是(至少对我而言),虽然对于小型系列而言相当,但当系列超过 250 时,这开始变得比使用 ix 慢。(在 ipython 中使用 %timeit 进行测试。)
【解决方案5】:

不确定这是否理想,但它可以通过创建蒙版来工作

In [59]: s.index
Out[59]: 
MultiIndex
[('a', 0) ('a', 1) ('a', 5) ('b', 0) ('b', 1) ('b', 2) ('b', 4)
 ('b', 50) ('c', 0)]
In [77]: s[(tpl for tpl in s.index if 2<=tpl[1]<=10 and tpl[0]=='b')]                                                               
Out[77]: 
b  2   -0.586568
   4    1.559988

编辑:海登的解决方案是要走的路

【讨论】:

    猜你喜欢
    • 2014-12-17
    • 2016-12-12
    • 2014-05-24
    • 2018-12-29
    • 2021-08-25
    • 2015-04-03
    • 2023-02-03
    • 2021-03-27
    • 2018-10-21
    相关资源
    最近更新 更多