Conversion d'une sortie Pandas GroupBy de Series en DataFrame

497

Je commence avec des données d'entrée comme celle-ci

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

Qui, une fois imprimé, apparaît comme ceci:

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

Le regroupement est assez simple:

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

et l'impression donne un GroupByobjet:

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

Mais ce que je veux finalement, c'est un autre objet DataFrame qui contient toutes les lignes de l'objet GroupBy. En d'autres termes, je veux obtenir le résultat suivant:

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

Je ne vois pas comment accomplir cela dans la documentation des pandas. Tout indice serait le bienvenu.

saveenr
la source
1
Outre la question: quelle version de pandas utilisez-vous? Si j'exécute les 2 premières commandes, je reçois g1 asEmpty DataFrame Columns: [] Index: [(Alice, Seattle), (Bob, Seattle), (Mallory, Portland), (Mallory, Seattle)]
Timofey
1
Le titre de la question est trompeur en ce qui concerne la réponse acceptée
matanster
@matanster puis-je vous demander à quoi vous êtes venu ici pour connaître la réponse? Nous pouvons penser à rédiger une réponse plus précise et à attirer l'attention des utilisateurs avec un commentaire sous la question.
cs95
@coldspeed Ceci est juste un problème typique avec SO, les titres des questions sont laissés s'écarter considérablement du contenu de la question et des réponses. Si la méta n'était pas aussi hostile, ce serait probablement un aspect utile à relever.
matanster
@matanster Je suis d'accord, mais j'étais seulement curieux de savoir ce que vous cherchiez réellement la réponse, de sorte que cela vous a conduit ici.
cs95

Réponses:

530

g1ici est une trame de données. Il a cependant un index hiérarchique:

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)

Peut-être que vous voulez quelque chose comme ça?

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 quelque chose comme:

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
la source
27
reset.index()fait le travail, super!
gented
54
Vous auriez pu utiliser:df1.groupby( [ "Name", "City"] ).size().to_frame(name = 'count').reset_index()
Nehal J Wani
3
Le deuxième exemple d'utilisation .reset_index()me semble être le meilleur moyen de joindre la sortie à partir de laquelle vous obtiendrez df.groupby('some_column').apply(your_custom_func). Ce n'était pas intuitif pour moi.
Alexander
5
Est-ce également vrai en Python 3? Je trouve une fonction groupby renvoyant l' pandas.core.groupby.DataFrameGroupByobjet, non pandas.core.frame.DataFrame.
Adrian Keister
3
Cette réponse ne semble pas pertinente pour les derniers python et pandas
matanster
129

Je veux changer légèrement la réponse donnée par Wes, car la version 0.16.2 l'exige as_index=False. Si vous ne le définissez pas, vous obtenez une trame de données vide.

Source :

Les fonctions d'agrégation ne renverront pas les groupes sur lesquels vous agrégez s'ils sont nommés colonnes, quand as_index=True, par défaut. Les colonnes groupées seront les indices de l'objet retourné.

La réussite as_index=Falserenvoie les groupes sur lesquels vous agrégez, s'ils sont nommés colonnes.

Fonctions sont celles qui regroupe des données réduisent la dimension des objets retournés, par exemple: mean, sum, size, count, std, var, sem, describe, first, last, nth, min, max. C'est ce qui se passe quand on fait par exemple DataFrame.sum()et qu'on récupère a Series.

Le nième peut agir comme un réducteur ou un filtre, voir ici .

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
#

ÉDITER:

Dans la version 0.17.1et les versions ultérieures, vous pouvez utiliser subsetdans countet reset_indexavec le paramètre 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

La différence entre countet sizeest celle qui sizecompte les valeurs de NaN alors que ce countn'est pas le cas.

jezrael
la source
8
Je pense que c'est la manière la plus simple - une ligne qui utilise le fait que vous pouvez nommer la colonne de la série avec reset_index:df1.groupby( [ "Name", "City"]).size().reset_index(name="count")
Ben
1
Y a-t-il une raison pour laquelle as_index=False' stopped working in latest versions? I also tried to run df1.groupby (["Nom", "Ville"], as_index = False) .size () `mais cela n'affecte pas le résultat (probablement parce que le résultat du regroupement n'est SeriespasDataFrame
Roman Pekar
1
Je ne suis pas sûr, mais il semble qu'il n'y ait que 2 colonnes et groupbypar ces colonnes. Mais je ne suis pas sûr, car je ne suis pas développeur de pandas.
jezrael
20

Simplement, cela devrait faire la tâche:

import pandas as pd

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

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

Ici, grouped_df.size()récupère le nombre unique de groupes et la reset_index()méthode réinitialise le nom de la colonne que vous souhaitez qu'elle soit. Enfin, la Dataframe()fonction pandas est appelée pour créer un objet DataFrame.

Surya
la source
2
Découvrez la méthode .to_frame (): grouped_df.size (). To_frame ('Group_Count')
Sealander
12

La clé est d'utiliser la méthode reset_index () .

Utilisation:

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

Vous avez maintenant votre nouvelle trame de données dans g1 :

trame de données de résultat

Ferd
la source
9

Peut-être ai-je mal compris la question, mais si vous souhaitez reconvertir le groupe en un cadre de données, vous pouvez utiliser .to_frame (). Je voulais réinitialiser l'index lorsque je l'ai fait, j'ai donc également inclus cette partie.

exemple de code sans rapport avec la question

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

J'ai trouvé que cela fonctionnait pour moi.

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()
lefévrier
la source
6

La solution ci-dessous peut être plus simple:

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

J'ai agrégé avec des données sages et stocké dans une trame de données

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

Ces solutions n'ont fonctionné que partiellement pour moi car je faisais plusieurs agrégations. Voici un exemple de sortie de mon groupe par que je voulais convertir en trame de données:

Sortie Groupby

Parce que je voulais plus que le nombre fourni par reset_index (), j'ai écrit une méthode manuelle pour convertir l'image ci-dessus en une trame de données. Je comprends que ce n'est pas la façon la plus pythonique / pandas de le faire car elle est assez verbeuse et explicite, mais c'était tout ce dont j'avais besoin. Fondamentalement, utilisez la méthode reset_index () expliquée ci-dessus pour démarrer un cadre de données "d'échafaudage", puis parcourez les paires de groupes dans le cadre de données groupé, récupérez les indices, effectuez vos calculs par rapport au cadre de données non groupé et définissez la valeur dans votre nouveau cadre de données agrégé .

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 dictionnaire n'est pas votre truc, les calculs pourraient être appliqués en ligne dans la boucle 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
la source
Pourriez-vous s'il vous plaît partager l'ensemble de données que vous avez utilisé pour votre solution? Merci beaucoup!
JeffZheng