【问题标题】:GroupBy aggregate function that computes two values at onceGroupBy 聚合函数,一次计算两个值
【发布时间】:2021-06-16 08:06:06
【问题描述】:

我的数据名如下:

import pandas as pd

df = pd.DataFrame({
    'A': [1, 1, 1, 2, 2, 2],
    'B': [1, 2, 3, 4, 5, 6],
    'C': [4, 5, 6, 7, 8, 9],
})

现在我想对每个组产生两个值进行分组和聚合。结果应该类似于以下内容:

expected = df.groupby('A').agg([min, max])

#     B       C    
#   min max min max
# A                
# 1   1   3   4   6
# 2   4   6   7   9

但是,在我的例子中,我没有两个不同的函数 minmax,而是有一个函数可以同时计算这两个值:

def minmax(x):
    """This function promises to compute the min and max in one go."""
    return min(x), max(x)

现在我的问题是,如何使用这个 one 函数为每个组生成两个聚合值?

这有点与this answer 有关,但我不知道该怎么做。我能想到的最好的办法是使用双重嵌套的apply,但这不是很优雅,而且它会在行而不是列上产生多索引:

result = df.groupby('A').apply(
    lambda g: g.drop(columns='A').apply(
        lambda h: pd.Series(dict(zip(['min', 'max'], minmax(h))))
    )
)

#        B  C
# A          
# 1 min  1  4
#   max  3  6
# 2 min  4  7
#   max  6  9

【问题讨论】:

    标签: python pandas pandas-groupby aggregate


    【解决方案1】:

    如果您遇到一个返回 tuple 值的函数。我会:

    1. 定义一个新函数,将 tuple 值包装到 dict 中,以便您预定义 dict.keys() 以与您希望的列名称保持一致。
    2. 使用谨慎的for 循环,不会浪费时间和空间。

    包装函数

    # Given Function
    def minmax(x):
        """This function promises to compute the min and max in one go."""
        return min(x), max(x)
    
    # wrapped function
    def minmax_dict(x):
        return dict(zip(['min', 'max'], minmax(x)))
    

    小心for循环

    我的目标是将此字典传递给pd.DataFrame 构造函数。这意味着,我想要键中的 MultiIndex 列元素的元组。我希望值是字典,键是索引元素。

    dat = {}
    for a, d in df.set_index('A').groupby('A'):
        for cn, c in d.iteritems():
            for k, v in minmax_dict(c).items():
                dat.setdefault((cn, k), {})[a] = v
    
    pd.DataFrame(dat).rename_axis('A')
    
        B       C    
      min max min max
    A                
    1   1   3   4   6
    2   4   6   7   9
    

    添加细节

    看看精心制作的字典

    data
    
    {('B', 'min'): {1: 1, 2: 4},
     ('B', 'max'): {1: 3, 2: 6},
     ('C', 'min'): {1: 4, 2: 7},
     ('C', 'max'): {1: 6, 2: 9}}
    

    【讨论】:

    • “不要那样做”,你指的是你的答案还是我的目标?我还应该指出,该解决方案应该比简单的df.groupby('A').agg([lambda x: minmax(x)[0], lambda x: minmax(x)[1]]) 更有效。当然,为了对此进行基准测试,我需要提供更复杂的数据框以及更有用的minmax 实现。
    • 当你建议我不应该这样做时,可能会有误解,因为我没有太多选择。为了简化示例,我使用了minmax 函数,但实际上我有一个不能简单地拆分为多个其他函数的函数。事实上,我的函数将每个组的数据拟合到一个模型中,然后返回一堆拟合参数及其误差估计值(所以实际上不止两个)。然后,生成的数据框应包含每个组的参数估计值。
    • 啊,有道理。假设你是故意这样做的,我有点判断力。如果没有,那么您可以做一些事情来将函数包装成更方便的东西。让我考虑一下。
    • @a_guest 我用我的实际推荐更新了我的帖子。
    【解决方案2】:

    另一种解决方案:

    pd.concat({k:d.agg(minmax).set_axis(['min','max'])
               for k,d in df.drop('A',axis=1).groupby(df['A'])
              })
    

    输出:

           B  C
    1 min  1  4
      max  3  6
    2 min  4  7
      max  6  9
    

    【讨论】:

    • 解决方案应该等同于df.groupby('A').agg([min, max]),即多索引应该在列上。此外,分组循环与在 groupby 对象上使用 apply 并没有太大区别,是吗?
    • 用 unstack 链接解决方案,您应该得到所需的格式。是的,它相当于申请。最后一个建议我和@piRSqaured 说的一样,避免做你用 minmax 函数做的事情
    • 使用unstack 给了我几乎 一个等效的解决方案,唯一的区别是未命名的索引。我的问题中使用的示例可能有点误导,因为它由两个不同的功能组成。在我的用例中,我有一个函数myfunc,它不能简单地分成两个函数。做到这一点的唯一方法是df.groupby('A').agg([lambda x: myfunc(x)[0], lambda x: myfunc(x)[1]]),但myfunc 的计算成本并不低,因此会浪费大量计算能力。
    • 如果你想要这个名字,那么在 unstack 之后用 rename_axis('A') 链接它。
    • 嗯,这正是我来这里问这个问题的原因,因为堆叠和嵌套如此多的函数调用不再是真正可读的了。所以我希望有一个更清洁的解决方案。无论如何感谢您的回答。顺便说一句,您应该更新它以匹配问题的预期结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    相关资源
    最近更新 更多