多索引熊猫 groupby + 聚合,保持完整索引

han*_*nna 5 python pandas

我有一个两级分层索引的整数序列。

 >> s
 id1    id2    
 1      a     100
        b      10
        c       9 
 2      a    2000
 3      a       5
        b      10
        c      15
        d      20
 ...
Run Code Online (Sandbox Code Playgroud)

我想按 id1 分组,并选择最大值,但在结果中有完整的索引。我尝试了以下方法:

 >> s.groupby(level=0).aggregate(np.max)
 id1              
 1    100 
 2   2000
 3     20
Run Code Online (Sandbox Code Playgroud)

但结果仅由 id1 索引。我希望我的输出看起来像这样:

 id1    id2    
 1      a     100
 2      a    2000
 3      d      20
Run Code Online (Sandbox Code Playgroud)

这里提出了一个相关但更复杂的问题: Multiindexed Pandas groupby, ignore a level? 正如它所说,答案是一种黑客。

有谁知道更好的解决方案?如果不是,那么 id2 的每个值都是唯一的特殊情况呢?

unu*_*tbu 5

在 groupby 之后选择完整行的一种方法是使用groupby/transform构建布尔掩码,然后使用掩码从s以下位置选择完整行:

In [110]: s[s.groupby(level=0).transform(lambda x: x == x.max()).astype(bool)]
Out[110]: 
id1  id2
1    a       100
2    a      2000
3    d        20
Name: s, dtype: int64
Run Code Online (Sandbox Code Playgroud)

另一种在某些情况下更快的方法 - 例如当有很多组时 - 是将最大值m与 中的值一起合并到 DataFrame 中s,然后根据m和之间的相等选择行s

def using_merge(s):
    m = s.groupby(level=0).agg(np.max)
    df = s.reset_index(['id2'])
    df['m'] = m
    result = df.loc[df['s']==df['m']]
    del result['m']
    result = result.set_index(['id2'], append=True)
    return result['s']
Run Code Online (Sandbox Code Playgroud)

这是一个示例using_merge,虽然更复杂,但可能比 更快using_transform

import numpy as np
import pandas as pd
def using_transform(s):
    return s[s.groupby(level=0).transform(lambda x: x == x.max()).astype(bool)]

N = 10**5
id1 = np.random.randint(100, size=N)
id2 = np.random.choice(list('abcd'), size=N)
index = pd.MultiIndex.from_arrays([id1, id2])
ss = pd.Series(np.random.randint(100, size=N), index=index)
ss.index.names = ['id1', 'id2']
ss.name = 's'
Run Code Online (Sandbox Code Playgroud)

使用 IPython 的%timeit函数对这两个函数进行计时会产生:

In [121]: %timeit using_merge(ss)
100 loops, best of 3: 12.8 ms per loop

In [122]: %timeit using_transform(ss)
10 loops, best of 3: 45 ms per loop
Run Code Online (Sandbox Code Playgroud)