Konwersja danych wyjściowych Pandas GroupBy z Series na DataFrame

494

Zaczynam od takich danych wejściowych jak to

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

Które po wydrukowaniu wygląda następująco:

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

Grupowanie jest dość proste:

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

a drukowanie daje GroupByobiekt:

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

Ale ostatecznie chcę innego obiektu DataFrame, który zawiera wszystkie wiersze w obiekcie GroupBy. Innymi słowy, chcę uzyskać następujący wynik:

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

Nie widzę, jak to osiągnąć w dokumentacji pand. Wszelkie wskazówki byłyby mile widziane.

saveenr
źródło
1
Na bok pytanie: jakiej wersji pandy używasz? Jeśli wykonam pierwsze 2 polecenia, otrzymam g1 jakoEmpty DataFrame Columns: [] Index: [(Alice, Seattle), (Bob, Seattle), (Mallory, Portland), (Mallory, Seattle)]
Timofey
1
Tytuł pytania wprowadza w błąd w odniesieniu do przyjętej odpowiedzi
matanster,
@ matanster, czy mogę zapytać, po co tu przyszedłeś, szukając odpowiedzi? Możemy pomyśleć o napisaniu dokładniejszej odpowiedzi i skierowaniu uwagi użytkowników za pomocą komentarza pod pytaniem.
cs95
@coldspeed To tylko typowy problem z SO, tytuły pytań mogą znacznie odbiegać od treści pytania i odpowiedzi. Jeśli meta nie byłaby tak wroga, prawdopodobnie byłby to użyteczny aspekt do podniesienia tam.
matanster
@matanster Zgadzam się, ale byłem tylko ciekawy, dlaczego tak naprawdę szukałeś odpowiedzi, że doprowadziło cię to tutaj.
cs95

Odpowiedzi:

529

g1tutaj jest DataFrame. Ma jednak indeks hierarchiczny:

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)

Może chcesz coś takiego?

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

Lub coś takiego:

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
źródło
26
reset.index()wykonuje robotę, świetnie!
gented
54
Mogłeś użyć:df1.groupby( [ "Name", "City"] ).size().to_frame(name = 'count').reset_index()
Nehal J Wani
3
Drugi przykład użycia .reset_index()wydaje mi się najlepszym sposobem dołączenia do wyjścia, z którego będziesz korzystać df.groupby('some_column').apply(your_custom_func). To nie było dla mnie intuicyjne.
Alexander
5
Czy dotyczy to również Pythona 3? Znajduję funkcję grupowania zwracającą pandas.core.groupby.DataFrameGroupByobiekt, a nie pandas.core.frame.DataFrame.
Adrian Keister,
3
Ta odpowiedź wydaje się nieistotna w przypadku najnowszych pytonów i pand
matanster,
127

Chcę nieco zmienić odpowiedź udzieloną przez Wesa, ponieważ wymaga wersji 0.16.2 as_index=False. Jeśli go nie ustawisz, otrzymasz pustą ramkę danych.

Źródło :

Funkcje agregujące nie zwrócą grup, które agregujesz, jeśli są to kolumny nazwane, kiedy jest as_index=Trueto domyślna. Zgrupowane kolumny będą indeksami zwracanego obiektu.

Przekazanie as_index=Falsezwróci grupy, które agregujesz, jeśli są nazwane kolumnami.

Agregowanie funkcje są te, które zmniejszają wymiar zwracanych obiektów, na przykład: mean, sum, size, count, std, var, sem, describe, first, last, nth, min, max. Tak się dzieje, gdy robisz na przykład DataFrame.sum()i odzyskujesz Series.

nth może działać jako reduktor lub filtr, patrz tutaj .

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
#

EDYTOWAĆ:

W wersji 0.17.1, a później można wykorzystać subsetw counti reset_indexz parametrem namew 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

Różnica między counti sizepolega na tym, że sizezlicza wartości NaN, podczas gdy countnie.

jezrael
źródło
8
Myślę, że jest to najłatwiejszy sposób - jedna linijka, która wykorzystuje ładny fakt, że można nazwać kolumnę z serią reset_index:df1.groupby( [ "Name", "City"]).size().reset_index(name="count")
Ben
1
Czy istnieje powód, dla którego as_index=False' stopped working in latest versions? I also tried to run df1.groupby ([„Nazwa”, „Miasto”], as_index = False) .size () `ale nie wpływa na wynik (prawdopodobnie dlatego, że wynik grupowania Seriesnie jestDataFrame
Roman Pekar
1
Nie jestem pewien, ale wydaje się, że są tylko 2 kolumny i groupbywedług tych kolumn. Ale nie jestem pewien, ponieważ nie jestem programistą pand.
jezrael
20

Po prostu powinno to wykonać zadanie:

import pandas as pd

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

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

Tutaj grouped_df.size()pobiera unikalną liczbę grup według liczby, a reset_index()metoda resetuje nazwę kolumny, którą chcesz, aby była. Na koniec Dataframe()wywoływana jest funkcja pandy w celu utworzenia obiektu DataFrame.

Surya
źródło
2
Sprawdź metodę .to_frame (): grouped_df.size (). To_frame ('Group_Count')
Sealander,
11

Kluczem jest użycie metody reset_index () .

Posługiwać się:

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

Teraz masz nową ramkę danych w g1 :

wynikowa ramka danych

Ferd
źródło
9

Być może źle rozumiem pytanie, ale jeśli chcesz przekonwertować grupę z powrotem na ramkę danych, możesz użyć .to_frame (). Chciałem zresetować indeks, kiedy to zrobiłem, więc także tę część.

przykładowy kod niezwiązany z pytaniem

df = df['TIME'].groupby(df['Name']).min()
df = df.to_frame()
df = df.reset_index(level=['Name',"TIME"])
brandog
źródło
6

Odkryłem, że to działało dla mnie.

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
źródło
6

Poniższe rozwiązanie może być prostsze:

df1.reset_index().groupby( [ "Name", "City"],as_index=False ).count()
Xiao QianYu
źródło
4

Połączyłem z mądrymi danymi i zapisałem w ramce danych

almo_grp_data = pd.DataFrame({'Qty_cnt' :
almo_slt_models_data.groupby( ['orderDate','Item','State Abv']
          )['Qty'].sum()}).reset_index()
Manivannan Murugavel
źródło
3

Te rozwiązania tylko częściowo działały dla mnie, ponieważ robiłem wiele agregacji. Oto przykładowe dane wyjściowe zgrupowane według tego, że chciałem przekonwertować na ramkę danych:

Grupuj dane wyjściowe

Ponieważ chciałem więcej niż liczba podana przez reset_index (), napisałem ręczną metodę konwersji powyższego obrazu w ramkę danych. Rozumiem, że nie jest to najbardziej pythonowy / pandowy sposób na zrobienie tego, ponieważ jest dość gadatliwy i wyraźny, ale to wszystko, czego potrzebowałem. Zasadniczo użyj opisanej powyżej metody reset_index (), aby uruchomić ramkę danych „rusztowania”, a następnie zapętlić pary par w zgrupowanej ramce danych, pobrać indeksy, wykonać obliczenia względem niezgrupowanej ramki danych i ustawić wartość w nowej zagregowanej ramce danych .

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]

Jeśli słownik nie jest twoją rzeczą, obliczenia można zastosować bezpośrednio w pętli 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
źródło
Czy możesz udostępnić zestaw danych użyty do rozwiązania? Wielkie dzięki!
JeffZheng,