Convertendo uma saída do Pandas GroupBy da Series para DataFrame

496

Estou começando com dados de entrada como este

df1 = pandas.DataFrame( { 
    "Name" : ["Alice", "Bob", "Mallory", "Mallory", "Bob" , "Mallory"] , 
    "City" : ["Seattle", "Seattle", "Portland", "Seattle", "Seattle", "Portland"] } )

Que quando impresso aparece assim:

   City     Name
0   Seattle    Alice
1   Seattle      Bob
2  Portland  Mallory
3   Seattle  Mallory
4   Seattle      Bob
5  Portland  Mallory

O agrupamento é bastante simples:

g1 = df1.groupby( [ "Name", "City"] ).count()

e a impressão produz um GroupByobjeto:

                  City  Name
Name    City
Alice   Seattle      1     1
Bob     Seattle      2     2
Mallory Portland     2     2
        Seattle      1     1

Mas o que eu quero eventualmente é outro objeto DataFrame que contenha todas as linhas no objeto GroupBy. Em outras palavras, quero obter o seguinte resultado:

                  City  Name
Name    City
Alice   Seattle      1     1
Bob     Seattle      2     2
Mallory Portland     2     2
Mallory Seattle      1     1

Não vejo como fazer isso na documentação dos pandas. Qualquer dica seria bem-vinda.

saveenr
fonte
1
Além da pergunta: qual versão do pandas você usa? Se executar 2 primeiros comandos fico g1 comoEmpty DataFrame Columns: [] Index: [(Alice, Seattle), (Bob, Seattle), (Mallory, Portland), (Mallory, Seattle)]
Timofey
1
O título da pergunta é enganoso em relação à resposta aceita #
matanster
@matanster, posso perguntar o que você veio aqui procurando saber a resposta? Podemos pensar em escrever uma resposta mais precisa e direcionar a atenção dos usuários com um comentário na pergunta.
cs95
@ coldspeed Esse é apenas um problema típico do SO, pois os títulos das perguntas divergem significativamente do conteúdo da pergunta e das respostas. Se a meta não fosse tão hostil, isso provavelmente seria um aspecto útil a ser levantado lá.
matanster
@matanster Eu concordo, no entanto, eu só estava curioso para saber o que é que você estava realmente procurando a resposta, de tal forma que ela o levou até aqui.
cs95

Respostas:

530

g1aqui está um DataFrame. Possui um índice hierárquico:

In [19]: type(g1)
Out[19]: pandas.core.frame.DataFrame

In [20]: g1.index
Out[20]: 
MultiIndex([('Alice', 'Seattle'), ('Bob', 'Seattle'), ('Mallory', 'Portland'),
       ('Mallory', 'Seattle')], dtype=object)

Talvez você queira algo assim?

In [21]: g1.add_suffix('_Count').reset_index()
Out[21]: 
      Name      City  City_Count  Name_Count
0    Alice   Seattle           1           1
1      Bob   Seattle           2           2
2  Mallory  Portland           2           2
3  Mallory   Seattle           1           1

Ou algo como:

In [36]: DataFrame({'count' : df1.groupby( [ "Name", "City"] ).size()}).reset_index()
Out[36]: 
      Name      City  count
0    Alice   Seattle      1
1      Bob   Seattle      2
2  Mallory  Portland      2
3  Mallory   Seattle      1
Wes McKinney
fonte
27
reset.index()faz o trabalho, ótimo!
gented 13/10/2015
54
Você poderia ter usado:df1.groupby( [ "Name", "City"] ).size().to_frame(name = 'count').reset_index()
Nehal J Wani 13/08
3
O segundo exemplo usando .reset_index()me parece ser a melhor maneira de juntar a saída que você obterá df.groupby('some_column').apply(your_custom_func). Isso não foi intuitivo para mim.
Alexander Alexander
5
Isso também é verdade no Python 3? Estou encontrando uma função groupby retornando o pandas.core.groupby.DataFrameGroupByobjeto, não pandas.core.frame.DataFrame.
Adrian Keister
3
Esta resposta parece irrelevante para as últimas pandas python e
matanster
128

Quero mudar um pouco a resposta dada por Wes, porque a versão 0.16.2 exige as_index=False. Se você não configurá-lo, receberá um quadro de dados vazio.

Fonte :

As funções de agregação não retornarão os grupos que você está agregando se tiverem nomes de colunas, quando as_index=True, o padrão. As colunas agrupadas serão os índices do objeto retornado.

A passagem as_index=Falseretornará os grupos que você está agregando, se tiverem nomes de colunas.

Agregando funções são aqueles que reduzem a dimensão dos objetos retornados, por exemplo: mean, sum, size, count, std, var, sem, describe, first, last, nth, min, max. É o que acontece quando você faz, por exemplo, DataFrame.sum()e volta a Series.

Pode funcionar como um redutor ou um filtro, veja aqui .

import pandas as pd

df1 = pd.DataFrame({"Name":["Alice", "Bob", "Mallory", "Mallory", "Bob" , "Mallory"],
                    "City":["Seattle","Seattle","Portland","Seattle","Seattle","Portland"]})
print df1
#
#       City     Name
#0   Seattle    Alice
#1   Seattle      Bob
#2  Portland  Mallory
#3   Seattle  Mallory
#4   Seattle      Bob
#5  Portland  Mallory
#
g1 = df1.groupby(["Name", "City"], as_index=False).count()
print g1
#
#                  City  Name
#Name    City
#Alice   Seattle      1     1
#Bob     Seattle      2     2
#Mallory Portland     2     2
#        Seattle      1     1
#

EDITAR:

Na versão 0.17.1e posterior, você pode usar subsetin counte reset_indexcom o parâmetro namein size:

print df1.groupby(["Name", "City"], as_index=False ).count()
#IndexError: list index out of range

print df1.groupby(["Name", "City"]).count()
#Empty DataFrame
#Columns: []
#Index: [(Alice, Seattle), (Bob, Seattle), (Mallory, Portland), (Mallory, Seattle)]

print df1.groupby(["Name", "City"])[['Name','City']].count()
#                  Name  City
#Name    City                
#Alice   Seattle      1     1
#Bob     Seattle      2     2
#Mallory Portland     2     2
#        Seattle      1     1

print df1.groupby(["Name", "City"]).size().reset_index(name='count')
#      Name      City  count
#0    Alice   Seattle      1
#1      Bob   Seattle      2
#2  Mallory  Portland      2
#3  Mallory   Seattle      1

A diferença entre counte sizeé que sizeconta os valores de NaN enquanto countnão conta.

jezrael
fonte
8
Penso que esta é a maneira mais fácil - um forro de um que usa o fato agradável que você pode nomear a coluna série com reset_index:df1.groupby( [ "Name", "City"]).size().reset_index(name="count")
Ben
1
Existe uma razão pela qual o as_index=False' stopped working in latest versions? I also tried to run df1.groupby (["Name", "City"], como_index = False) .size () `mas não afeta o resultado (provavelmente porque o resultado do agrupamento Seriesnão éDataFrame
Roman Pekar
1
Não tenho certeza, mas parece que existem apenas 2 colunas e groupbypor essas colunas. Mas não tenho certeza, porque não sou desenvolvedor de pandas.
Jezrael #
20

Simplesmente, isso deve fazer a tarefa:

import pandas as pd

grouped_df = df1.groupby( [ "Name", "City"] )

pd.DataFrame(grouped_df.size().reset_index(name = "Group_Count"))

Aqui, grouped_df.size()o grupo exclusivo é contado e o reset_index()método redefine o nome da coluna que você deseja que seja. Finalmente, a Dataframe()função pandas é chamada para criar um objeto DataFrame.

Surya
fonte
2
Verifique o método .to_frame (): grouped_df.size (). To_frame ('Group_Count')
#
12

A chave é usar o método reset_index () .

Usar:

import pandas

df1 = pandas.DataFrame( { 
    "Name" : ["Alice", "Bob", "Mallory", "Mallory", "Bob" , "Mallory"] , 
    "City" : ["Seattle", "Seattle", "Portland", "Seattle", "Seattle", "Portland"] } )

g1 = df1.groupby( [ "Name", "City"] ).count().reset_index()

Agora você tem seu novo quadro de dados em g1 :

quadro de dados do resultado

Ferd
fonte
9

Talvez eu entenda mal a pergunta, mas se você deseja converter o grupo de volta em um dataframe, pode usar .to_frame (). Eu queria redefinir o índice quando fiz isso, então incluí essa parte também.

código de exemplo não relacionado à pergunta

df = df['TIME'].groupby(df['Name']).min()
df = df.to_frame()
df = df.reset_index(level=['Name',"TIME"])
brandog
fonte
6

Eu achei que isso funcionou para mim.

import numpy as np
import pandas as pd

df1 = pd.DataFrame({ 
    "Name" : ["Alice", "Bob", "Mallory", "Mallory", "Bob" , "Mallory"] , 
    "City" : ["Seattle", "Seattle", "Portland", "Seattle", "Seattle", "Portland"]})

df1['City_count'] = 1
df1['Name_count'] = 1

df1.groupby(['Name', 'City'], as_index=False).count()
o fevereiro
fonte
6

A solução abaixo pode ser mais simples:

df1.reset_index().groupby( [ "Name", "City"],as_index=False ).count()
Xiao QianYu
fonte
4

Agreguei dados Qty sábios e armazenei no dataframe

almo_grp_data = pd.DataFrame({'Qty_cnt' :
almo_slt_models_data.groupby( ['orderDate','Item','State Abv']
          )['Qty'].sum()}).reset_index()
Manivannan Murugavel
fonte
3

Essas soluções funcionaram apenas parcialmente para mim porque eu estava fazendo várias agregações. Aqui está um exemplo de saída do meu agrupado por que eu queria converter em um dataframe:

Saída Groupby

Como eu queria mais do que a contagem fornecida por reset_index (), escrevi um método manual para converter a imagem acima em um quadro de dados. Entendo que essa não é a maneira mais pitônica / panda de fazer isso, pois é bastante detalhada e explícita, mas era tudo o que eu precisava. Basicamente, use o método reset_index () explicado acima para iniciar um quadro de dados "andaimes", depois percorra os pares de grupos no quadro de dados agrupado, recupere os índices, execute seus cálculos no quadro de dados não agrupado e defina o valor no seu novo quadro de dados agregado .

df_grouped = df[['Salary Basis', 'Job Title', 'Hourly Rate', 'Male Count', 'Female Count']]
df_grouped = df_grouped.groupby(['Salary Basis', 'Job Title'], as_index=False)

# Grouped gives us the indices we want for each grouping
# We cannot convert a groupedby object back to a dataframe, so we need to do it manually
# Create a new dataframe to work against
df_aggregated = df_grouped.size().to_frame('Total Count').reset_index()
df_aggregated['Male Count'] = 0
df_aggregated['Female Count'] = 0
df_aggregated['Job Rate'] = 0

def manualAggregations(indices_array):
    temp_df = df.iloc[indices_array]
    return {
        'Male Count': temp_df['Male Count'].sum(),
        'Female Count': temp_df['Female Count'].sum(),
        'Job Rate': temp_df['Hourly Rate'].max()
    }

for name, group in df_grouped:
    ix = df_grouped.indices[name]
    calcDict = manualAggregations(ix)

    for key in calcDict:
        #Salary Basis, Job Title
        columns = list(name)
        df_aggregated.loc[(df_aggregated['Salary Basis'] == columns[0]) & 
                          (df_aggregated['Job Title'] == columns[1]), key] = calcDict[key]

Se você não gosta de um dicionário, os cálculos podem ser aplicados em linha no loop for:

    df_aggregated['Male Count'].loc[(df_aggregated['Salary Basis'] == columns[0]) & 
                                (df_aggregated['Job Title'] == columns[1])] = df['Male Count'].iloc[ix].sum()
John Galt
fonte
Você poderia compartilhar o conjunto de dados que você usou para sua solução? Muito obrigado!
JeffZheng 7/02/19