【问题标题】:How to query MultiIndex index columns values in pandas如何在 pandas 中查询 MultiIndex 索引列的值
【发布时间】:2013-07-29 01:32:39
【问题描述】:

代码示例:

In [171]: A = np.array([1.1, 1.1, 3.3, 3.3, 5.5, 6.6])

In [172]: B = np.array([111, 222, 222, 333, 333, 777])

In [173]: C = randint(10, 99, 6)

In [174]: df = pd.DataFrame(zip(A, B, C), columns=['A', 'B', 'C'])

In [175]: df.set_index(['A', 'B'], inplace=True)

In [176]: df
Out[176]: 
          C
A   B      
1.1 111  20
    222  31
3.3 222  24
    333  65
5.5 333  22
6.6 777  74 

现在,我想检索 A 值:
Q1:在 [3.3, 6.6] 范围内 - 预期返回值:[3.3, 5.5, 6.6] 或 [3.3, 3.3, 5.5 , 6.6] 如果最后包含,则为 [3.3, 5.5] 或 [3.3, 3.3, 5.5] 如果不是。
Q2:在 [2.0, 4.0] 范围内 - 预期返回值:[ 3.3] 或 [3.3, 3.3]

对于任何其他 MultiIndex 维度也是如此,例如 B 值:
Q3:在 [111, 500] 范围内重复,作为数据行数range - 预期返回值:[111, 222, 222, 333, 333]

更正式的:

让我们假设 T 是一个包含 A、B 和 C 列的表。该表包括 n 行。表格单元格是数字,例如 A double、B 和 C 整数。让我们创建一个表 T 的 DataFrame,我们将其命名为 DF。让我们设置 DF 的 A 和 B 列索引(不重复,即没有单独的 A 和 B 列作为索引,并作为数据分开),即本例中的 A 和 B MultiIndex

问题:

  1. 如何编写索引查询,例如查询索引 A(或 B),比如在标签区间 [120.0, 540.0] 中?存在标签 120.0 和 540.0。我必须澄清一下,我只对作为查询响应的索引列表感兴趣!
  2. 如何相同,但如果标签 120.0 和 540.0 不存在,但有标签值低于 120、高于 120 且低于 540 或高于 540?
  3. 如果 Q1 和 Q2 的答案是唯一索引值,现在与索引范围内的数据行数相同,但有重复。

对于不是索引的列,我知道上述问题的答案,但在索引的情况下,经过长时间的网络研究和 pandas 的功能实验,我没有成功。我现在看到的唯一方法(无需额外编程)是将 A 和 B 的副本作为索引之外的数据列。

【问题讨论】:

标签: python pandas indexing slice multi-index


【解决方案1】:

通过 MultiIndex 值查询 df,例如 (A > 1.7) 和 (B : p>

In [536]: result_df = df.loc[(df.index.get_level_values('A') > 1.7) & (df.index.get_level_values('B') < 666)]

In [537]: result_df
Out[537]: 
          C
A   B      
3.3 222  43
    333  59
5.5 333  56

因此,如果仍然需要,例如获取 'A' 索引值:

In [538]: result_df.index.get_level_values('A')
Out[538]: Index([3.3, 3.3, 5.5], dtype=object)

问题在于,在大型数据帧中,按索引选择的性能比排序的常规行选择差 10%。而在重复的工作中,循环,延迟累积。见例子:

In [558]: df = store.select(STORE_EXTENT_BURSTS_DF_KEY)

In [559]: len(df)
Out[559]: 12857

In [560]: df.sort(inplace=True)

In [561]: df_without_index = df.reset_index()

In [562]: %timeit df.loc[(df.index.get_level_values('END_TIME') > 358200) & (df.index.get_level_values('START_TIME') < 361680)]
1000 loops, best of 3: 562 µs per loop

In [563]: %timeit df_without_index[(df_without_index.END_TIME > 358200) & (df_without_index.START_TIME < 361680)]
1000 loops, best of 3: 507 µs per loop

【讨论】:

    【解决方案2】:

    为了更好的可读性,我们可以简单地使用the query() Method,来避免冗长的df.index.get_level_values()reset_index/set_index来回。

    这里是目标DataFrame

    In [12]: df                                                                    
    Out[12]:                                                                       
              C                                                                    
    A   B                                                                          
    1.1 111  68                                                                    
        222  40                                                                    
    3.3 222  20                                                                    
        333  11                                                                    
    5.5 333  80                                                                    
    6.6 777  51 
    

    Q1 的答案(A[3.3, 6.6] 范围内):

    In [13]: df.query('3.3 <= A <= 6.6') # for closed interval                       
    Out[13]:                                                                       
              C                                                                    
    A   B                                                                          
    3.3 222  20                                                                    
        333  11                                                                    
    5.5 333  80                                                                    
    6.6 777  51                                                                    
    
    In [14]: df.query('3.3 < A < 6.6') # for open interval                         
    Out[14]:                                                                       
              C                                                                    
    A   B                                                                          
    5.5 333  80
    

    当然,您可以使用&lt;, &lt;=, &gt;, &gt;= 来进行任何类型的包含。


    同样,回答 Q2A[2.0, 4.0] 范围内):

    In [15]: df.query('2.0 <= A <= 4.0')                                        
    Out[15]:                                                                    
              C                                                                 
    A   B                                                                       
    3.3 222  20                                                                 
        333  11 
    

    Q3 的答案(B[111, 500] 范围内):

    In [16]: df.query('111 <= B <= 500')                                        
    Out[16]:                                                                    
              C                                                                 
    A   B                                                                       
    1.1 111  68                                                                 
        222  40                                                                 
    3.3 222  20                                                                 
        333  11                                                                 
    5.5 333  80
    

    此外,您可以非常自然地组合 col AB 的查询!

    In [17]: df.query('0 < A < 4 and 150 < B < 400')                            
    Out[17]:                                                                    
              C                                                                 
    A   B                                                                       
    1.1 222  40                                                                 
    3.3 222  20                                                                 
        333  11
    

    【讨论】:

    • 我假设 Pandas 多索引就像 SQL 中的复合索引。因此,如果我只查询 B 而不是 A。那么 Pandas 是真的使用索引还是在进行表/数据帧扫描?
    【解决方案3】:

    使用类似“浮点”的索引,您总是希望将其用作列而不是直接索引操作。无论端点是否存在,这些都将起作用。

    In [11]: df
    Out[11]: 
              C
    A   B      
    1.1 111  81
        222  45
    3.3 222  98
        333  13
    5.5 333  89
    6.6 777  98
    
    In [12]: x = df.reset_index()
    

    第一季度

    In [13]: x.loc[(x.A>=3.3)&(x.A<=6.6)]
    Out[13]: 
         A    B   C
    2  3.3  222  98
    3  3.3  333  13
    4  5.5  333  89
    5  6.6  777  98
    

    第二季度

    In [14]: x.loc[(x.A>=2.0)&(x.A<=4.0)]
    Out[14]: 
         A    B   C
    2  3.3  222  98
    3  3.3  333  13
    

    第三季度

    In [15]: x.loc[(x.B>=111.0)&(x.B<=500.0)]
    Out[15]: 
         A    B   C
    0  1.1  111  81
    1  1.1  222  45
    2  3.3  222  98
    3  3.3  333  13
    4  5.5  333  89
    

    如果您想要恢复索引,只需设置它们。这是一个便宜的操作。

    In [16]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B'])
    Out[16]: 
              C
    A   B      
    1.1 111  81
        222  45
    3.3 222  98
        333  13
    5.5 333  89
    

    如果你真的想要实际的索引值

    In [5]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B']).index
    Out[5]: 
    MultiIndex
    [(1.1, 111), (1.1, 222), (3.3, 222), (3.3, 333), (5.5, 333)]
    

    【讨论】:

    • 感谢@Jeff,但在这种情况下,最好将上述索引列的副本保留为常规数据列。但事实是,在我的情况下,初始完整表非常大并且存储在 HDFStore 中,每次通过查询接收到不同的数据帧时我都会处理。并在 HDFStore 中添加两个带有冗余数据的额外列 - 浪费空间。两难境地……
    • 实际上,如果它在商店中,那么我根本不会存储索引;将要查询的列存储为 data_columns (隐式索引它们),请参阅:pandas.pydata.org/pandas-docs/dev/…,(数据帧的)索引已存储为表中的列,因此您可以直接查询它们
    • 没有那么好的表现。用例是循环查询HDFStore求和子表;在上述每个迭代中处理其他循环中的子表。如果我将在内部循环中使用全索引 HDFStore 查询而不是 DataFrame,则每个子循环查询的处理时间将增加 100 倍以上(目前使用 IPython %timeit 检查)。
    • 你为什么要循环播放?请显示一些代码(如果你愿意,你可以做一个 sep 问题),或者在邮件列表上发布,或者在这里...up2u
    • 我的意思是“一些”(不是“总和”)子表。
    猜你喜欢
    • 2021-09-24
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 2022-12-04
    • 2020-08-01
    • 2020-06-22
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多