在pandas中按除一个索引列外的所有列分组

15 投票
2 回答
10302 浏览
提问于 2025-04-19 05:09

我的数据分析经常依赖一个简单但有点不靠谱的模式,那就是“对所有东西进行分组,除了某些东西”。以这个多重索引的例子 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 个回答

1

groupby的文档中,有一个例子说明如何在一个多重索引中,按除了一个指定的列以外的所有列进行分组。这个例子使用了索引名称的.difference方法:

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

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

撰写回答