Os documentos mostram como aplicar várias funções em um objeto de grupo por vez, usando um dict com os nomes das colunas de saída como as chaves:
In [563]: grouped['D'].agg({'result1' : np.sum,
.....: 'result2' : np.mean})
.....:
Out[563]:
result2 result1
A
bar -0.579846 -1.739537
foo -0.280588 -1.402938
No entanto, isso funciona apenas em um objeto de grupo de séries. E quando um ditado é passado de maneira semelhante a um grupo pelo DataFrame, ele espera que as chaves sejam os nomes das colunas às quais a função será aplicada.
O que quero fazer é aplicar várias funções a várias colunas (mas determinadas colunas serão operadas várias vezes). Além disso, algumas funções dependerão de outras colunas no objeto groupby (como funções sumif). Minha solução atual é ir coluna por coluna e fazer algo como o código acima, usando lambdas para funções que dependem de outras linhas. Mas isso está demorando muito (acho que leva muito tempo para percorrer um objeto groupby). Vou precisar alterá-lo para percorrer todo o objeto groupby em uma única execução, mas estou me perguntando se existe um caminho embutido nos pandas para fazer isso de maneira um tanto limpa.
Por exemplo, eu tentei algo como
grouped.agg({'C_sum' : lambda x: x['C'].sum(),
'C_std': lambda x: x['C'].std(),
'D_sum' : lambda x: x['D'].sum()},
'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)
mas como esperado, recebo um KeyError (já que as chaves precisam ser uma coluna se agg
forem chamadas de um DataFrame).
Existe alguma maneira integrada de fazer o que eu gostaria de fazer, ou a possibilidade de que essa funcionalidade seja adicionada, ou precisarei apenas percorrer o grupo manualmente?
obrigado
Respostas:
A segunda metade da resposta atualmente aceita está desatualizada e tem duas obsoletas. Primeiro e mais importante, você não pode mais passar um dicionário de dicionários para o
agg
método groupby. Segundo, nunca use.ix
.Se você deseja trabalhar com duas colunas separadas ao mesmo tempo, sugiro usar o
apply
método que passa implicitamente um DataFrame para a função aplicada. Vamos usar um quadro de dados semelhante ao de cimaUm dicionário mapeado de nomes de colunas para funções de agregação ainda é uma maneira perfeitamente boa de realizar uma agregação.
Se você não gostar desse nome feio da coluna lambda, poderá usar uma função normal e fornecer um nome personalizado para o
__name__
atributo especial como este:Usando
apply
e retornando uma sérieAgora, se você tinha várias colunas que precisavam interagir juntas, não pode usá-
agg
las, o que implicitamente passa uma Série para a função de agregação. Ao usarapply
o grupo inteiro como um DataFrame, é passado para a função.Eu recomendo criar uma única função personalizada que retorne uma série de todas as agregações. Use o índice de série como rótulos para as novas colunas:
Se você gosta de MultiIndexes, ainda pode devolver uma série com uma como esta:
fonte
a
dentro do grupo0
, não deveria ser0.418500 + 0.446069 = 0.864569
? O mesmo vale para outras células, os números não parecem somar. Poderia ser usado um quadro de dados subjacente ligeiramente diferente nos exemplos subseqüentes?Para a primeira parte, você pode passar um ditado de nomes de colunas para chaves e uma lista de funções para os valores:
ATUALIZAÇÃO 1:
Como a função agregada funciona na Série, as referências aos outros nomes de coluna são perdidas. Para contornar isso, você pode fazer referência ao quadro de dados completo e indexá-lo usando os índices de grupo na função lambda.
Aqui está uma solução hacky:
Aqui, a coluna 'D' resultante é composta pelos valores 'E' somados.
ATUALIZAÇÃO 2:
Aqui está um método que eu acho que fará tudo o que você pedir. Primeiro, crie uma função lambda personalizada. Abaixo, g faz referência ao grupo. Ao agregar, g será uma série. Passando
g.index
paradf.ix[]
seleciona o grupo atual de df. Em seguida, testo se a coluna C é menor que 0,5. A série booleana retornada é passada para ag[]
qual seleciona apenas as linhas que atendem aos critérios.fonte
{funcname: func}
como valores em vez de listas para manter meus nomes personalizados. Mas, em ambos os casos, não posso passar umlambda
que use outras colunas (comolambda x: x['D'][x['C'] < 3].sum()
acima: "KeyError: 'D'"). Alguma idéia se isso é possível?KeyError: 'D'
df['A'].ix[g.index][df['C'] < 0].sum()
. No entanto, isso está começando a ficar bastante confuso - acho que o loop manual de legibilidade pode ser preferível, além de não ter certeza de que haja uma maneira de atribuir meu nome preferido noagg
argumento (em vez de<lambda>
). Vou manter a esperança de que alguém pode saber uma maneira mais simples ...{'D': {'my name':lambda function}}
e ele fará da chave do ditado interno o nome da coluna.Como alternativa (principalmente estética) à resposta de Ted Petrou, achei que preferia uma listagem um pouco mais compacta. Por favor, não considere aceitá-lo, é apenas um comentário muito mais detalhado sobre a resposta de Ted, além de código / dados. Python / pandas não é o meu primeiro / melhor, mas achei isso para ler bem:
Acho mais remanescente de
dplyr
tubos edata.table
comandos encadeados. Para não dizer que são melhores, apenas mais familiares para mim. (Eu certamente reconheço o poder e, para muitos, a preferência de usardef
funções mais formalizadas para esses tipos de operações. Esta é apenas uma alternativa, não necessariamente melhor.)Gerei dados da mesma maneira que Ted, vou adicionar uma semente para reprodutibilidade.
fonte
Pandas >= 0.25.0
, agregações nomeadasDesde a versão pandas
0.25.0
ou superior, estamos nos afastando da agregação e renomeação baseadas em dicionário e indo para agregações nomeadas que aceitam atuple
. Agora podemos agregar + renomear simultaneamente para um nome de coluna mais informativo:Exemplo :
Aplique
GroupBy.agg
com agregação nomeada:fonte
Novo na versão 0.25.0.
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 em GroupBy.agg () , conhecida como "agregação nomeada" , em que
pandas.NamedAgg é apenas um duplo nomeado. Tuplas simples também são permitidas.
Argumentos de palavras-chave adicionais não são passados para as funções de agregação. Somente pares de (coluna, aggfunc) devem ser passados como ** kwargs. Se suas funções de agregação exigirem argumentos adicionais, aplique-as parcialmente com functools.partial ().
A agregação nomeada também é válida para agregações de grupos por séries. Nesse caso, não há seleção de coluna; portanto, os valores são apenas as funções.
fonte
A resposta de Ted é incrível. Acabei usando uma versão menor, caso alguém esteja interessado. Útil quando você procura uma agregação que depende dos valores de várias colunas:
criar um quadro de dados
agrupando e agregando com apply (usando várias colunas)
agrupando e agregando com agregação (usando várias colunas)
Eu gosto dessa abordagem, pois ainda posso usar agregados. Talvez as pessoas me digam por que a aplicação é necessária para obter várias colunas ao fazer agregações em grupos.
Parece óbvio agora, mas desde que você não selecione a coluna de interesse diretamente após o grupo por , terá acesso a todas as colunas do quadro de dados na sua função de agregação.
somente acesso à coluna selecionada
acesso a todas as colunas, já que a seleção é, afinal, a mágica
ou similar
Eu espero que isso ajude.
fonte