使用 lambda 函数聚合 pandas 组

问题描述 投票:0回答:3

我有一个汇总声明如下:

data = data.groupby(['type', 'status', 'name']).agg({
    'one' : np.mean, 
    'two' : lambda value: 100* ((value>32).sum() / reading.mean()), 
    'test2': lambda value: 100* ((value > 45).sum() / value.mean())
})

我收到 KeyErrors。我已经能够让它适用于一个 lambda 函数,但不能适用于两个。

python pandas numpy lambda group-by
3个回答
52
投票

您需要在

data
中指定要聚合其值的列。 例如,

data = data.groupby(['type', 'status', 'name'])['value'].agg(...)

而不是

data = data.groupby(['type', 'status', 'name']).agg(...)

如果您没有提及列(例如

'value'
),则传递给
agg
的字典中的键将被视为列名称。
KeyError
是 Pandas 告诉您它在 DataFrame
one
中找不到名为
two
test2
data
的列的方式。

注意:将字典传递给

groupby/agg
已被弃用。相反,接下来您应该传递一个元组列表。每个元组的形式预计为
('new_column_name', callable)
.


这是可运行的示例:

import numpy as np
import pandas as pd

N = 100
data = pd.DataFrame({
    'type': np.random.randint(10, size=N),
    'status': np.random.randint(10, size=N),
    'name': np.random.randint(10, size=N),
    'value': np.random.randint(10, size=N),
})

reading = np.random.random(10,)

data = data.groupby(['type', 'status', 'name'])['value'].agg(
    [('one',  np.mean), 
    ('two', lambda value: 100* ((value>32).sum() / reading.mean())), 
    ('test2', lambda value: 100* ((value > 45).sum() / value.mean()))])
print(data)
#                   one  two  test2
# type status name                 
# 0    1      3     3.0    0    0.0
#             7     4.0    0    0.0
#             9     8.0    0    0.0
#      3      1     5.0    0    0.0
#             6     3.0    0    0.0
# ...

如果这与您的情况不符,请提供符合您情况的可运行代码。


1
投票

正如 @unutbu 提到的,问题不在于 lambda 函数的数量,而在于传递给

agg()
的字典中的键不作为列出现在
data
中。 OP似乎尝试使用命名聚合,它将自定义列标题分配给聚合列。一个简单的方法是在聚合后调用
set_axis()
。例如,以下内容产生与 @unutbu 建议的命名聚合相同的输出。

data = (
    data.groupby(['type', 'status', 'name'])['value']
    .agg(['mean', lambda value: 100* ((value>32).sum() / reading.mean()), lambda value: 100* ((value > 45).sum() / value.mean())])
    .set_axis(['one', 'two', 'test2'], axis=1)   # <---- rename columns here
)

如果您想为涉及多列的聚合分配自定义名称,这尤其有用,因为

groupby.agg
调用可以正常完成。

N = 100
data = pd.DataFrame({
    'type': np.random.randint(2, size=N),
    'value1': np.random.randint(50, size=N),
    'value2': np.random.randint(50, size=N)
})
reading = np.random.random(10)

x = (
    data.groupby('type')
    .agg({'value1': [np.mean, lambda value: 100* ((value>32).sum() / reading.mean())],
          'value2': lambda value: 100* ((value > 45).sum() / value.mean())})
    .set_axis(['one', 'two', 'test2'], axis=1)
)


          one          two      test2
type
0    24.00000  2892.413355  18.627451
1    21.44186  1190.993734   4.589114

要使用命名聚合执行相同操作,需要构建一个复杂的字典,并将其作为 kwargs 传递给

groupby.agg()

x = (
    data.groupby('type')
    .agg(**{'one': ('value1',  np.mean), 
            'two': ('value1', lambda value: 100* ((value>32).sum() / reading.mean())),
            'test2': ('value2', lambda value: 100* ((value > 45).sum() / value.mean()))})
)

x.equals(y) # True

0
投票

我更喜欢接近 R 的 tidyverse 的语法,它提供了很大的灵活性和可读性。您可以通过以下方式应用自定义函数:

# Some input data
df = pd.DataFrame({
    'col1': [0, 1, 0, 1, 0],
    'col2': [10, 20, 30, 40, 50],
    'col3': [100, 200, 300, 400, 500],
})

# Tidyverse-like aggregations
(
    df
    .groupby('col1')
    .agg(
        percent_col2_above_30=('col2', lambda x: sum(x>30)/len(x)),
        col3_max_divided_by_min=('col3', lambda x: max(x)/min(x))
    )
)
© www.soinside.com 2019 - 2024. All rights reserved.