【问题标题】:Multiindex pandas groupby + aggregate, keep full indexMultiindex pandas groupby + 聚合,保持完整索引
【发布时间】:2015-02-25 10:15:20
【问题描述】:

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

 >> s
 id1    id2    
 1      a     100
        b      10
        c       9 
 2      a    2000
 3      a       5
        b      10
        c      15
        d      20
 ...

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

 >> s.groupby(level=0).aggregate(np.max)
 id1              
 1    100 
 2   2000
 3     20

但结果仅由 id1 索引。我希望我的输出如下所示:

 id1    id2    
 1      a     100
 2      a    2000
 3      d      20

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

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

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    在 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
    

    另一种在某些情况下更快的方法 - 例如当有很多组时 - 是将最大值ms中的值一起合并到一个DataFrame中,然后选择行基于ms 之间的相等性:

    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']
    

    这是一个显示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'
    

    使用 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
    

    【讨论】:

    • 感谢您的出色回答。我在this answer 中使用了transform 解决方案,我注意到不需要.astype(bool)
    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 2018-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-22
    • 2021-03-13
    • 2019-06-16
    相关资源
    最近更新 更多