在 Pandas 中,如何按除一个索引列之外的所有列分组?

15

我的数据分析经常会回到一个简单但不太可靠的模式,即“除了某些列进行分组”。以这个多重索引的例子 df 为例:

                      accuracy  velocity
name condition trial                    
john a         1     -1.403105  0.419850
               2     -0.879487  0.141615
     b         1      0.880945  1.951347
               2      0.103741  0.015548
hans a         1      1.425816  2.556959
               2     -0.117703  0.595807
     b         1     -1.136137  0.001417
               2      0.082444 -1.184703

比如,我现在想要对所有可用的试验进行平均,同时保留名称和条件信息。这很容易实现:

average = df.groupby(level=('name', 'condition')).mean()

然而,在实际环境中,多重索引中存储了更多元数据。每行的索引跨越8-10列,因此上述模式变得非常笨拙。最终,我正在寻找一种“丢弃”操作;我想执行一个可以抛出或减少单个索引列的操作。在上面的情况下,那就是试验编号。

我应该勇敢面对还是有更加惯用的方法?这可能是反模式!我想建立起关于“真正的pandas方式”的良好直觉...提前致谢。

2个回答

13
你可以为此定义一个帮助函数:
def allbut(*names):
    names = set(names)
    return [item for item in levels if item not in names]

演示:

import pandas as pd
levels = ('name', 'condition', 'trial')
names = ('john', 'hans')
conditions = list('ab')
trials = range(1, 3)

idx = pd.MultiIndex.from_product(
    [names, conditions, trials], names=levels)

df = pd.DataFrame(np.random.randn(len(idx), 2),
                      index=idx, columns=('accuracy', 'velocity'))

def allbut(*names):
    names = set(names)
    return [item for item in levels if item not in names]

In [40]: df.groupby(level=allbut('condition')).mean()
Out[40]: 
            accuracy  velocity
trial name                    
1     hans  0.086303  0.131395
      john  0.454824 -0.259495
2     hans -0.234961 -0.626495
      john  0.614730 -0.144183

您也可以删除多个级别:

In [53]: df.groupby(level=allbut('name', 'trial')).mean()
Out[53]: 
           accuracy  velocity
condition                    
a         -0.597178 -0.370377
b         -0.126996 -0.037003

这看起来不错;我一直在使用类似的东西。你认为这个模式本身是否可靠?有没有更“内置”的方法来实现相同的结果? - ap3l
1
有许多不同的变体,例如 df.groupby(level=list(set(levels)-{'name'})).mean(),但我认为它们基本上都是相同的 - 尤其是对于少量级别。我不认为有更多“内置”的方法,因此定义一个辅助函数是使代码看起来可读的最佳方式。 - unutbu

1
groupby的文档中,有一个示例,演示如何按多级索引中除一个指定列以外的所有列进行分组。它使用了索引名称的.difference方法:

df.groupby(level=df.index.names.difference(['name']))


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