Несколько агрегаций одного и того же столбца с использованием pandas GroupBy.agg ()

129

Есть ли встроенный в pandas способ применения двух разных агрегатных функций f1, f2к одному и тому же столбцу df["returns"]без необходимости вызывать agg()несколько раз?

Пример фрейма данных:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

Синтаксически неправильный, но интуитивно правильный способ сделать это:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

Очевидно, Python не допускает дублирования ключей. Есть ли другой способ выразить свой вклад agg()? Может быть, [(column, function)]лучше подойдет список кортежей , позволяющий применять несколько функций к одному столбцу? Но agg()похоже, что он принимает только словарь.

Есть ли обходной путь для этого, кроме определения вспомогательной функции, которая просто применяет обе функции внутри нее? (Как это вообще будет работать с агрегацией?)

Ely
источник
Связанные - Агрегация в
пандах
2
Начиная с версии 0.25, pandas предоставляет более интуитивно понятный синтаксис для множественных агрегатов, а также для переименования выходных столбцов. См. Документацию по именованным агрегациям .
cs95
К вашему сведению, этот вопрос был задан еще на pandas 0.8.x в 9/2012
smci,
1
К вашему сведению, принятый ответ также устарел - не передавайте agg () dict of dicts.
cs95,
@ cs95: Я знаю, что он устарел, я говорю, что SO завален старыми устаревшими решениями из старых версий. У SO нет способа отметить это - кроме комментариев.
smci

Ответы:

159

Вы можете просто передать функции в виде списка:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

или как словарь:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
БМУ
источник
5
Есть ли способ указать имена столбцов результатов?
Бен
3
@Ben Я думаю, ты должен потом переименовать. пример Тома Аугспургера (см. ячейку 25)
Stewbaca
1
@Ben: Я добавил пример
bmu
10
@sparc_spread Передача нескольких функций в виде списка хорошо описана в документации pandas . В будущей версии pandas переименование и передача нескольких функций в качестве словаря не рекомендуется. Подробности можно найти в журнале изменений 0.20 , который я также резюмировал в другом месте на SO .
joelostblom 03
3
Об этом уже было сказано, но использование словарей для переименования выходных столбцов по возрасту не рекомендуется. Вместо этого вы можете указать список кортежей. Смотрите этот ответ.
cs95
102

TLDR; Pandas groupby.aggимеет новый, более простой синтаксис для указания (1) агрегатов для нескольких столбцов и (2) множественных агрегатов для столбца. Итак, чтобы сделать это для панд> = 0,25 , используйте

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

ИЛИ

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas> = 0.25: именованная агрегация

Pandas изменил поведение GroupBy.aggв пользу более интуитивного синтаксиса для указания именованных агрегатов. См. Раздел документации 0.25 по улучшениям, а также соответствующие проблемы GitHub GH18366 и GH26512 .

Из документации,

Для поддержки агрегации по столбцу с контролем над именами выходных столбцов pandas принимает специальный синтаксис GroupBy.agg(), известный как «именованная агрегация», где

  • Ключевые слова - это имена выходных столбцов.
  • Значения представляют собой кортежи, первый элемент которых является столбцом для выбора, а второй элемент - агрегированием, применяемым к этому столбцу. Pandas предоставляет именованный кортеж pandas.NamedAgg с полями ['column', 'aggfunc'], чтобы было понятнее, каковы аргументы. Как обычно, агрегирование может быть вызываемым или строковым псевдонимом.

Теперь вы можете передать кортеж через аргументы ключевого слова. Кортежи соответствуют формату (<colName>, <aggFunc>).

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                   'height': [9.1, 6.0, 9.5, 34.0],
                   'weight': [7.9, 7.5, 9.9, 198.0]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

В качестве альтернативы вы можете использовать pd.NamedAgg(по сути именованный кортеж), который делает вещи более явными.

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Для Series это еще проще, просто передайте aggfunc аргументу ключевого слова.

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

Наконец, если имена ваших столбцов не являются действительными идентификаторами Python, используйте словарь с распаковкой:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

Панды <0,25

В более поздних версиях pandas, предшествующих 0.24, при использовании словаря для указания имен столбцов для вывода агрегации вы получите FutureWarning:

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

Использование словаря для переименования столбцов не рекомендуется в версии 0.20. В более поздних версиях pandas это можно указать проще, передав список кортежей. При указании функций таким образом, все функции для этого столбца должны быть указаны как кортежи пар (имя, функция).

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Или,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895
cs95
источник
4
Это должен быть лучший ответ, поскольку используется более ясное и чистое решение с использованием более новой версии интерфейса.
NKSHELL
Примеры, используемые для именованной агрегации, не решают исходную проблему использования нескольких агрегаций в одном столбце. Например, можете ли вы агрегировать как минимум, так и максимум для высоты без предварительного подмножества для df.groupby('kind')['height']?
победитель
1
@victor Я добавил TL; DR в верхней части ответа, который напрямую касается вопроса. И ответ на ваш второй вопрос - да, пожалуйста, взгляните на правку моего ответа.
cs95
Было бы замечательно использовать более общий код для последнего примера вашего ответа> = 0,25 для обработки агрегирования нескольких столбцов, как это. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Онур Эдже
6

Было бы что-то вроде этой работы:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565
Чанг Ше
источник
2
Нет, не работает. Если вы посмотрите на строку документа, в aggregateней явно указано, что при dictпередаче ключа ключи должны быть именами столбцов. Итак, либо ваш пример - это то, что вы ввели, не проверив наличие этой ошибки, либо Pandas нарушает здесь свои собственные документы.
ely
N / MI не видел там дополнительного вызова returns. Так это серийная версия агрегата? Я хочу создать агрегатную версию DataFrame и хочу сразу применить несколько разных агрегатов к каждому столбцу.
ely
1
Попробуйте это: df.groupby ('dummy'). Agg ({'returns': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang She
Выдает ошибку утверждения без сообщения. Судя по виду кода (pandas.core.internals.py, строки 406-408, версия 0.7.3), похоже, что он выполняет проверку в конце, чтобы убедиться, что он не возвращает больше столбцов, чем имеется ключей в первом слой словаря агрегации.
ely
Прекрасно работает на мастере. Хотите попробовать обновить?
Chang She