【问题标题】:selecting from multi-index pandas从多索引 pandas 中选择
【发布时间】:2021-10-24 09:11:44
【问题描述】:

我有一个包含“A”和“B”列的多索引数据框。

有没有办法通过过滤多索引的一列来选择行而不将索引重置为单列索引?

例如。

# has multi-index (A,B)
df
#can I do this? I know this doesn't work because the index is multi-index so I need to     specify a tuple

df.ix[df.A ==1]

【问题讨论】:

标签: python pandas dataframe multi-index


【解决方案1】:

一种方法是使用get_level_values索引方法:

In [11]: df
Out[11]:
     0
A B
1 4  1
2 5  2
3 6  3

In [12]: df.iloc[df.index.get_level_values('A') == 1]
Out[12]:
     0
A B
1 4  1

在 0.13 中,您将能够使用 xs with drop_level argument

df.xs(1, level='A', drop_level=False) # axis=1 if columns

注意:如果这是列 MultiIndex 而不是索引,您可以使用相同的技术:

In [21]: df1 = df.T

In [22]: df1.iloc[:, df1.columns.get_level_values('A') == 1]
Out[22]:
A  1
B  4
0  1

【讨论】:

    【解决方案2】:

    您也可以使用query,在我看来它非常易读且易于使用:

    import pandas as pd
    
    df = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [10, 20, 50, 80], 'C': [6, 7, 8, 9]})
    df = df.set_index(['A', 'B'])
    
          C
    A B    
    1 10  6
    2 20  7
    3 50  8
    4 80  9
    

    根据您的想法,您现在可以简单地执行以下操作:

    df.query('A == 1')
    
          C
    A B    
    1 10  6
    

    您还可以使用and 进行更复杂的查询

    df.query('A >= 1 and B >= 50')
    
          C
    A B    
    3 50  8
    4 80  9
    

    or

    df.query('A == 1 or B >= 50')
    
          C
    A B    
    1 10  6
    3 50  8
    4 80  9
    

    您还可以查询不同的索引级别,例如

    df.query('A == 1 or C >= 8')
    

    会回来

          C
    A B    
    1 10  6
    3 50  8
    4 80  9
    

    如果您想在查询中使用变量,you can use @:

    b_threshold = 20
    c_threshold = 8
    
    df.query('B >= @b_threshold and C <= @c_threshold')
    
          C
    A B    
    2 20  7
    3 50  8
    

    【讨论】:

    • 很好的答案,确实更具可读性。你知道是否可以查询不同索引级别的两个字段,例如:df.query('A == 1 or C &gt;= 8')
    • @obchardon:这似乎工作正常;我用你的例子编辑了我的答案。
    • 我有时间和字符串作为多索引,这会导致字符串表达式出现问题。但是,df.query() 如果在查询的表达式中使用“@”引用变量,则可以很好地处理变量,例如df.query('A == @var) 用于环境中的变量 var
    • @Solly:谢谢,我在答案中添加了这个。
    • 这里的多索引在哪里?
    【解决方案3】:

    你可以使用DataFrame.xs():

    In [36]: df = DataFrame(np.random.randn(10, 4))
    
    In [37]: df.columns = [np.random.choice(['a', 'b'], size=4).tolist(), np.random.choice(['c', 'd'], size=4)]
    
    In [38]: df.columns.names = ['A', 'B']
    
    In [39]: df
    Out[39]:
    A      b             a
    B      d      d      d      d
    0 -1.406  0.548 -0.635  0.576
    1 -0.212 -0.583  1.012 -1.377
    2  0.951 -0.349 -0.477 -1.230
    3  0.451 -0.168  0.949  0.545
    4 -0.362 -0.855  1.676 -2.881
    5  1.283  1.027  0.085 -1.282
    6  0.583 -1.406  0.327 -0.146
    7 -0.518 -0.480  0.139  0.851
    8 -0.030 -0.630 -1.534  0.534
    9  0.246 -1.558 -1.885 -1.543
    
    In [40]: df.xs('a', level='A', axis=1)
    Out[40]:
    B      d      d
    0 -0.635  0.576
    1  1.012 -1.377
    2 -0.477 -1.230
    3  0.949  0.545
    4  1.676 -2.881
    5  0.085 -1.282
    6  0.327 -0.146
    7  0.139  0.851
    8 -1.534  0.534
    9 -1.885 -1.543
    

    如果您想保持A 级别(drop_level 关键字参数仅从 v0.13.0 开始可用):

    In [42]: df.xs('a', level='A', axis=1, drop_level=False)
    Out[42]:
    A      a
    B      d      d
    0 -0.635  0.576
    1  1.012 -1.377
    2 -0.477 -1.230
    3  0.949  0.545
    4  1.676 -2.881
    5  0.085 -1.282
    6  0.327 -0.146
    7  0.139  0.851
    8 -1.534  0.534
    9 -1.885 -1.543
    

    【讨论】:

    • 哈,我刚刚更新了我的答案,注意:仅在 0.13 中可用。
    • 哦,很高兴知道。我不记得每个版本中添加了哪些小便利。
    • 大声笑,事实上,这个问题是对激发这种便利性的问题的欺骗! :)
    • 我想知道这是否也允许从一个多索引级别中选择多个项目?尝试df.xs(['a','b'], level='A', axis=1) 之类的方法会导致错误:KeyError: 'b'
    【解决方案4】:

    了解如何访问多索引 pandas DataFrame 可以帮助您完成各种类似的任务。

    复制粘贴到您的代码中以生成示例:

    # hierarchical indices and columns
    index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],
                                       names=['year', 'visit'])
    columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR', 'Temp']],
                                         names=['subject', 'type'])
    
    # mock some data
    data = np.round(np.random.randn(4, 6), 1)
    data[:, ::2] *= 10
    data += 37
    
    # create the DataFrame
    health_data = pd.DataFrame(data, index=index, columns=columns)
    health_data
    

    会给你这样的表:

    按列标准访问

    health_data['Bob']
    type       HR   Temp
    year visit      
    2013    1   22.0    38.6
            2   52.0    38.3
    2014    1   30.0    38.9
            2   31.0    37.3
    
    
    health_data['Bob']['HR']
    year  visit
    2013  1        22.0
          2        52.0
    2014  1        30.0
          2        31.0
    Name: HR, dtype: float64
    
    # filtering by column/subcolumn - your case:
    health_data['Bob']['HR']==22
    year  visit
    2013  1         True
          2        False
    2014  1        False
          2        False
    
    health_data['Bob']['HR'][2013]    
    visit
    1    22.0
    2    52.0
    Name: HR, dtype: float64
    
    health_data['Bob']['HR'][2013][1]
    22.0
    

    按行访问

    health_data.loc[2013]
    subject Bob Guido   Sue
    type    HR  Temp    HR  Temp    HR  Temp
    visit                       
    1   22.0    38.6    40.0    38.9    53.0    37.5
    2   52.0    38.3    42.0    34.6    30.0    37.7
    
    health_data.loc[2013,1] 
    subject  type
    Bob      HR      22.0
             Temp    38.6
    Guido    HR      40.0
             Temp    38.9
    Sue      HR      53.0
             Temp    37.5
    Name: (2013, 1), dtype: float64
    
    health_data.loc[2013,1]['Bob']
    type
    HR      22.0
    Temp    38.6
    Name: (2013, 1), dtype: float64
    
    health_data.loc[2013,1]['Bob']['HR']
    22.0
    

    切片多索引

    idx=pd.IndexSlice
    health_data.loc[idx[:,1], idx[:,'HR']]
        subject Bob Guido   Sue
    type    HR  HR  HR
    year    visit           
    2013    1   22.0    40.0    53.0
    2014    1   30.0    52.0    45.0
    

    【讨论】:

    • 这给出了ValueError: cannot handle a non-unique multi-index! 错误
    • 在您的按列访问中,您会如何一口气说出 Bob&HR 和 Guido &HR?
    • @user3697498 你可以在多个条件下使用pandas查询:kanoki.org/2020/01/21/…
    【解决方案5】:

    你可以使用DataFrame.loc:

    >>> df.loc[1]
    

    示例

    >>> print(df)
           result
    A B C        
    1 1 1       6
        2       9
      2 1       8
        2      11
    2 1 1       7
        2      10
      2 1       9
        2      12
    
    >>> print(df.loc[1])
         result
    B C        
    1 1       6
      2       9
    2 1       8
      2      11
    
    >>> print(df.loc[2, 1])
       result
    C        
    1       7
    2      10
    

    【讨论】:

    • 这是 IMO 最好的现代方法,其中 df.loc[2, 1]['result'] 将处理多列
    • 出于某种原因,这适用于任意数量的整数。例如df.loc[0], df.loc[1]....df.loc[n]
    【解决方案6】:

    另一种选择是:

    filter1 = df.index.get_level_values('A') == 1
    filter2 = df.index.get_level_values('B') == 4
    
    df.iloc[filter1 & filter2]
    Out[11]:
         0
    A B
    1 4  1
    

    【讨论】:

      【解决方案7】:

      您可以使用MultiIndex 切片。例如:

      arrays = [["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
                ["one", "two", "one", "two", "one", "two", "one", "two"]]   
      tuples = list(zip(*arrays))
      index = pd.MultiIndex.from_tuples(tuples, names=["A", "B"])
      df = pd.DataFrame(np.random.randint(9, size=(8, 2)), index=index, columns=["col1", "col2"])
      
               col1  col2
      A   B              
      bar one     0     8
          two     4     8
      baz one     6     0
          two     7     3
      foo one     6     8
          two     2     6
      qux one     7     0
          two     6     4
      

      Atwo 中选择所有B

      df.loc[(slice(None), 'two'), :]
      

      输出:

               col1  col2
      A   B              
      bar two     4     8
      baz two     7     3
      foo two     2     6
      qux two     6     4
      

      要从A 中选择barbaz,从B 中选择two

      df.loc[(['bar', 'baz'], 'two'), :]
      

      输出:

               col1  col2
      A   B              
      bar two     4     8
      baz two     7     3
      

      【讨论】:

        猜你喜欢
        • 2020-09-05
        • 1970-01-01
        • 2017-08-27
        • 2018-10-07
        • 2017-05-24
        • 1970-01-01
        相关资源
        最近更新 更多