Convertir una salida de Pandas GroupBy de Series a DataFrame

497

Estoy empezando con datos de entrada como este

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

Que cuando se imprime aparece así:

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

La agrupación es bastante simple:

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

e imprimir produce un GroupByobjeto:

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

Pero lo que quiero eventualmente es otro objeto DataFrame que contenga todas las filas en el objeto GroupBy. En otras palabras, quiero obtener el siguiente resultado:

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

No puedo ver cómo lograr esto en la documentación de los pandas. Cualquier sugerencia sería bienvenida.

saveenr
fuente
1
Aparte de la pregunta: ¿qué versión de pandas usas? Si ejecuto los primeros 2 comandos, obtengo g1 comoEmpty DataFrame Columns: [] Index: [(Alice, Seattle), (Bob, Seattle), (Mallory, Portland), (Mallory, Seattle)]
Timofey
1
El título de la pregunta es engañoso con respecto a la respuesta aceptada
matanster
@matanster, ¿puedo preguntar a qué viniste aquí para saber la respuesta? Podemos pensar en escribir una respuesta más precisa y dirigir la atención de los usuarios con un comentario debajo de la pregunta.
cs95
@coldspeed Esto es solo un problema típico con SO, se permite que los títulos de las preguntas difieran significativamente del contenido de la pregunta y las respuestas. Si el meta no fuera tan hostil, probablemente sería un aspecto útil para plantear allí.
matanster
@matanster Estoy de acuerdo, sin embargo, solo tenía curiosidad por saber qué es lo que realmente estaba buscando la respuesta, de modo que lo llevó hasta aquí.
cs95

Respuestas:

530

g1Aquí hay un DataFrame. Sin embargo, tiene un índice jerá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)

¿Quizás quieres algo como esto?

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

O 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
fuente
27
reset.index()hace el trabajo, genial!
Gented
54
Podría haber usado:df1.groupby( [ "Name", "City"] ).size().to_frame(name = 'count').reset_index()
Nehal J Wani
3
El segundo ejemplo usando .reset_index()me parece ser la mejor manera de unir la salida que obtendrá df.groupby('some_column').apply(your_custom_func). Esto no fue intuitivo para mí.
Alexander
55
¿Esto también es cierto en Python 3? Estoy encontrando una función groupby que devuelve el pandas.core.groupby.DataFrameGroupByobjeto, no pandas.core.frame.DataFrame.
Adrian Keister
3
Esta respuesta parece irrelevante para las últimas python y pandas
matanster
129

Quiero cambiar ligeramente la respuesta dada por Wes, porque requiere la versión 0.16.2 as_index=False. Si no lo configura, obtendrá un marco de datos vacío.

Fuente :

Las funciones de agregación no devolverán los grupos sobre los que está agregando si se denominan columnas, cuando as_index=True, por defecto. Las columnas agrupadas serán los índices del objeto devuelto.

Pasar as_index=Falsedevolverá los grupos sobre los que está agregando, si se denominan columnas.

La agregación de funciones son los que reducen la dimensión de los objetos devueltos, por ejemplo: mean, sum, size, count, std, var, sem, describe, first, last, nth, min, max. Esto es lo que sucede cuando lo haces, por ejemplo, DataFrame.sum()y vuelves a Series.

Enésimo puede actuar como un reductor o un filtro, ver aquí .

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:

En la versión 0.17.1y posteriores puede usar subseten county reset_indexcon parámetro nameen 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

La diferencia entre county sizees que sizecuenta los valores de NaN mientras countque no.

jezrael
fuente
8
Creo que esta es la forma más fácil: un trazador de líneas que utiliza el hecho de que puede nombrar la columna de la serie con reset_index:df1.groupby( [ "Name", "City"]).size().reset_index(name="count")
Ben
1
¿Hay alguna razón por la cual as_index=False' stopped working in latest versions? I also tried to run df1.groupby (["Nombre", "Ciudad"], as_index = False) .size () `pero no afecta el resultado (probablemente porque el resultado de la agrupación Seriesno esDataFrame
Roman Pekar
1
No estoy seguro, pero parece que solo hay 2 columnas y groupbypor estas columnas. Pero no estoy seguro, porque no soy desarrollador de pandas.
Israel
20

Simplemente, esto debería hacer la tarea:

import pandas as pd

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

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

Aquí, grouped_df.size()muestra el recuento de grupo único y el reset_index()método restablece el nombre de la columna que desea que sea. Finalmente, Dataframe()se llama a la función pandas para crear un objeto DataFrame.

Surya
fuente
2
Consulte el método .to_frame (): grouped_df.size (). To_frame ('Group_Count')
Sealander
12

La clave es usar el método reset_index () .

Utilizar:

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

Ahora tiene su nuevo marco de datos en g1 :

marco de datos de resultados

Ferd
fuente
9

Tal vez no entiendo bien la pregunta, pero si desea convertir el grupo de nuevo a un marco de datos, puede usar .to_frame (). Quería restablecer el índice cuando hice esto, así que también incluí esa parte.

código de ejemplo no relacionado con la pregunta

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

Encontré que esto funcionó para mí.

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()
thefebruaryman
fuente
6

La siguiente solución puede ser más simple:

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

Me agregué con Cantidad sabia de datos y almaceno a marco de datos

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

Estas soluciones solo funcionaron parcialmente para mí porque estaba haciendo múltiples agregaciones. Aquí hay una salida de muestra de mi agrupada por la que quería convertir a un marco de datos:

Salida grupal

Como quería más que el recuento proporcionado por reset_index (), escribí un método manual para convertir la imagen de arriba en un marco de datos. Entiendo que esta no es la forma más pitónica / panda de hacer esto, ya que es bastante detallada y explícita, pero era todo lo que necesitaba. Básicamente, use el método reset_index () explicado anteriormente para iniciar un marco de datos de "andamiaje", luego recorra los emparejamientos de grupo en el marco de datos agrupado, recupere los índices, realice sus cálculos contra el marco de datos desagrupado y establezca el valor en su nuevo marco de datos 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]

Si un diccionario no es lo tuyo, los cálculos podrían aplicarse en línea en el ciclo 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
fuente
¿Podría compartir el conjunto de datos que utilizó para su solución? ¡Muchas gracias!
JeffZheng