Várias agregações da mesma coluna usando pandas GroupBy.agg ()

127

Existe uma maneira interna de pandas para aplicar duas funções agregadoras diferentes f1, f2na mesma coluna df["returns"], sem precisar chamar agg()várias vezes?

Exemplo de quadro de dados:

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)
}) 

A maneira sintaticamente errada, mas intuitivamente correta, de fazer isso seria:

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

Obviamente, Python não permite chaves duplicadas. Existe alguma outra maneira de expressar a entrada agg()? Talvez uma lista de tuplas [(column, function)]funcionasse melhor, para permitir várias funções aplicadas à mesma coluna? Mas agg()parece que só aceita um dicionário.

Existe uma solução alternativa para isso, além de definir uma função auxiliar que apenas aplique as duas funções dentro dela? (Como isso funcionaria com a agregação, afinal?)

ely
fonte
Related - Agregação em pandas
jezrael 26/01
2
A partir de 0,25, o pandas fornece uma sintaxe mais intuitiva para várias agregações, além de renomear colunas de saída. Consulte a documentação sobre agregações nomeadas .
cs95
Para sua informação, esta pergunta foi solicitada no pandas 0.8.x em 9/2012
smci:
1
Para sua informação, a resposta aceita também está obsoleta - não passe para um ditado de ditados.
cs95
@ cs95: Eu sei que está obsoleto, estou dizendo que o SO está ficando repleto de soluções obsoletas antigas de versões antigas. O SO não tem como marcar isso - além de comentários.
smci 11/02

Respostas:

159

Você pode simplesmente passar as funções como uma lista:

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

ou como um dicionário:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
bmu
fonte
4
Existe uma maneira de especificar os nomes das colunas de resultados?
Ben
3
@ Ben Eu acho que você deve usar um renomear depois. exemplo por Tom Augspurger (consulte a célula 25)
Stewbaca 14/01
1
@ Ben: eu adicionei um exemplo
bmu
10
@sparc_spread A passagem de várias funções como uma lista está bem descrita na documentação do pandas . Renomear e passar várias funções como um dicionário será descontinuado em uma versão futura do pandas. Os detalhes estão no log de alterações 0,20 , que também resumi em outros lugares no SO .
Joelostblom 3/06
3
Já foi dito, mas o uso de dicionários para renomear colunas de saída a partir da idade foi preterido. Você pode especificar uma lista de tuplas. Veja esta resposta.
cs95
101

TLDR; O Pandas groupby.aggpossui uma sintaxe nova e mais fácil para especificar (1) agregações em várias colunas e (2) várias agregações em uma coluna. Portanto, para fazer isso em pandas> = 0,25 , use

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

OU

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas> = 0.25: Agregação Nomeada

O Pandas mudou o comportamento GroupBy.aggem favor de uma sintaxe mais intuitiva para especificar agregações nomeadas. Consulte a seção de documentos de 0,25 sobre aprimoramentos , além dos problemas relevantes do GitHub GH18366 e GH26512 .

A partir da documentação,

Para oferecer suporte à agregação específica de coluna com controle sobre os nomes das colunas de saída, o pandas aceita a sintaxe especial GroupBy.agg(), conhecida como "agregação nomeada", em que

  • As palavras-chave são os nomes das colunas de saída
  • Os valores são tuplas cujo primeiro elemento é a coluna a ser selecionada e o segundo elemento é a agregação a ser aplicada a essa coluna. O Pandas fornece os pandas.NamedAgg nomeadopleu com os campos ['column', 'aggfunc'] para deixar mais claro quais são os argumentos. Como de costume, a agregação pode ser um alias que pode ser chamado ou de sequência.

Agora você pode passar uma tupla por meio de argumentos de palavra-chave. As tuplas seguem o formato de (<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

Como alternativa, você pode usar pd.NamedAgg (essencialmente um nome nomeado) que torna as coisas mais explícitas.

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

É ainda mais simples para o Series, basta passar o aggfunc para um argumento de palavra-chave.

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       

Por fim, se os nomes das colunas não forem identificadores python válidos, use um dicionário com descompactação:

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

Pandas <0,25

Nas versões mais recentes dos pandas anteriores a 0,24, se você estiver usando um dicionário para especificar nomes de colunas para a saída de agregação, obterá 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

O uso de um dicionário para renomear colunas foi descontinuado na v0.20. Nas versões mais recentes dos pandas, isso pode ser especificado mais simplesmente passando uma lista de tuplas. Se você especificar as funções dessa maneira, todas as funções dessa coluna deverão ser especificadas como tuplas de pares (nome, função).

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

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Ou,

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

            op1       op2
dummy                    
1      0.328953  0.032895
cs95
fonte
4
Essa deve ser a resposta principal, devido ao uso de uma solução mais clara e limpa, usando a versão mais recente da interface.
NKSHELL 18/10/19
Os exemplos usados ​​para agregação nomeada não resolvem o problema original de usar várias agregações na mesma coluna. Por exemplo, você pode agregar tanto o mínimo quanto o máximo para a altura sem primeiro fazer o subconjunto df.groupby('kind')['height']?
victor
1
@ victor Adicionei um TLDR na parte superior da resposta que aborda diretamente a pergunta. E a resposta para sua segunda pergunta é sim, dê uma olhada na edição da minha resposta.
cs95
Um código mais genérico para o último exemplo de sua resposta> = 0,25 para lidar com a agregação de várias colunas como essa teria sido ótimo. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece
6

Algo assim funcionaria:

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
Chang She
fonte
2
Não, isso não funciona. Se você olhar para a sequência de documentos, aggregateela diz explicitamente que, quando a dicté passada, as chaves devem ser nomes de colunas. Portanto, seu exemplo é algo que você digitou sem verificar esse erro, ou então o Pandas quebra seus próprios documentos aqui.
ely
N / MI não viu a ligação extra returnslá. Portanto, esta é a versão em série do agregado? Estou procurando fazer a versão do DataFrame de agregação e quero aplicar várias agregações diferentes a cada coluna ao mesmo tempo.
ely
1
Tente isso: df.groupby ('dummy'). Agg ({'retorna': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Ela
Dá um erro de asserção sem mensagem. Pela aparência do código (pandas.core.internals.py, linhas 406-408, versão 0.7.3), parece que ele faz uma verificação no final para garantir que não retorne mais colunas do que as chaves na primeira camada do dicionário de agregação.
ely
Funciona bem no mestre. Deseja tentar atualizar?
Chang She