Pandas groupby: come ottenere un'unione di stringhe


122

Ho un dataframe come questo:

   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

chiamata

In [10]: print df.groupby("A")["B"].sum()

sarà di ritorno

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

Ora vorrei fare "lo stesso" per la colonna "C". Poiché quella colonna contiene stringhe, sum () non funziona (anche se potresti pensare che concatenerebbe le stringhe). Quello che mi piacerebbe davvero vedere è un elenco o un insieme di stringhe per ogni gruppo, ad es

A
1    {This, string}
2    {is, !}
3    {a}
4    {random}

Ho cercato di trovare modi per farlo.

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) non funziona, sebbene

df.groupby("A")["B"]

è un

pandas.core.groupby.SeriesGroupBy object

quindi speravo che qualsiasi metodo Series avrebbe funzionato. Qualche idea?

Risposte:


178
In [4]: df = read_csv(StringIO(data),sep='\s+')

In [5]: df
Out[5]: 
   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

In [6]: df.dtypes
Out[6]: 
A      int64
B    float64
C     object
dtype: object

Quando applichi la tua funzione, non ci sono esclusioni automatiche di colonne non numeriche. Questo è più lento, però, che l'applicazione di .sum()algroupby

In [8]: df.groupby('A').apply(lambda x: x.sum())
Out[8]: 
   A         B           C
A                         
1  2  1.615586  Thisstring
2  4  0.421821         is!
3  3  0.463468           a
4  4  0.643961      random

sum per impostazione predefinita concatena

In [9]: df.groupby('A')['C'].apply(lambda x: x.sum())
Out[9]: 
A
1    Thisstring
2           is!
3             a
4        random
dtype: object

Puoi fare praticamente quello che vuoi

In [11]: df.groupby('A')['C'].apply(lambda x: "{%s}" % ', '.join(x))
Out[11]: 
A
1    {This, string}
2           {is, !}
3               {a}
4          {random}
dtype: object

Farlo su un intero frame, un gruppo alla volta. La chiave è restituire un fileSeries

def f(x):
     return Series(dict(A = x['A'].sum(), 
                        B = x['B'].sum(), 
                        C = "{%s}" % ', '.join(x['C'])))

In [14]: df.groupby('A').apply(f)
Out[14]: 
   A         B               C
A                             
1  2  1.615586  {This, string}
2  4  0.421821         {is, !}
3  3  0.463468             {a}
4  4  0.643961        {random}

Sembra che queste operazioni sono ora vettorizzati eliminando la necessità di applye lambdas. Sono venuto qui chiedendomi perché pandaseffettivamente concatena e non restituisce un errore sulla somma delle stringhe.
NelsonGon

1
Se stai cercando di concatenare stringhe e aggiungere un carattere in mezzo, la soluzione .agg consigliata da @voithos di seguito è molto più veloce di .apply consigliata qui. Nei miei test stavo diventando 5-10 volte più veloce.
Raddoppio

70

È possibile utilizzare il applymetodo per applicare una funzione arbitraria ai dati raggruppati. Quindi, se vuoi un set, applica set. Se vuoi un elenco, applica list.

>>> d
   A       B
0  1    This
1  2      is
2  3       a
3  4  random
4  1  string
5  2       !
>>> d.groupby('A')['B'].apply(list)
A
1    [This, string]
2           [is, !]
3               [a]
4          [random]
dtype: object

Se vuoi qualcos'altro, scrivi una funzione che fa quello che vuoi e poi applyquello.


Funziona bene, ma manca la colonna A.
Vineesh TP

@VineeshTP: la colonna A è stata utilizzata come colonna di raggruppamento, quindi è nell'indice, come puoi vedere nell'esempio. Puoi recuperarlo come colonna usando .reset_index().
BrenBarn

30

Potresti essere in grado di utilizzare la funzione aggregate(o agg) per concatenare i valori. (Codice non testato)

df.groupby('A')['B'].agg(lambda col: ''.join(col))

Funziona veramente. Sorprendente. Poiché @voithos ha detto "non testato", non ero molto ottimista. Bit ho testato la sua versione come voce in un dizionario agg e ha funzionato come previsto: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Ha reso la mia giornata
matthhias

2
Se stai cercando di concatenare stringhe insieme a qualche tipo di separatore, ho trovato questo suggerimento .agg molto più veloce di .apply. Per un set di dati di 600k + stringhe di testo, ho ottenuto risultati identici 5-10 volte più velocemente.
Raddoppio

14

Potresti provare questo:

df.groupby('A').agg({'B':'sum','C':'-'.join})

2
Dalla recensione: potresti aggiungere ulteriori spiegazioni alla tua risposta?
toti08

1
Groupby viene applicato sulla colonna "A" e con la funzione agg potrei usare funzioni diverse su colonne diverse diciamo somma gli elementi nella colonna "C", concatena gli elementi nella colonna "C" inserendo un "-" tra le parole
user3241146

8

una soluzione semplice sarebbe:

>>> df.groupby(['A','B']).c.unique().reset_index()

questa dovrebbe essere la risposta giusta. ti fa rispondere in modo pulito. molte grazie!
imsrgadich

Se nel caso qualcuno fosse interessato a unire i contenuti dell'elenco in una stringa df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Vivek-Ananth

8

Aggregazioni denominate con pandas >= 0.25.0

Dalla versione 0.25.0 di panda abbiamo denominato aggregazioni in cui possiamo raggruppare, aggregare e allo stesso tempo assegnare nuovi nomi alle nostre colonne. In questo modo non otterremo le colonne MultiIndex e i nomi delle colonne hanno più senso dati i dati che contengono:


aggregare e ottenere un elenco di stringhe

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', list)).reset_index()

print(grp)
   A     B_sum               C
0  1  1.615586  [This, string]
1  2  0.421821         [is, !]
2  3  0.463468             [a]
3  4  0.643961        [random]

aggregare e unire le stringhe

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', ', '.join)).reset_index()

print(grp)
   A     B_sum             C
0  1  1.615586  This, string
1  2  0.421821         is, !
2  3  0.463468             a
3  4  0.643961        random

6

Se desideri sovrascrivere la colonna B nel dataframe, dovrebbe funzionare:

    df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))

2

Seguendo la buona risposta di @ Erfan, la maggior parte delle volte in un'analisi dei valori aggregati si vogliono le combinazioni possibili uniche di questi valori di caratteri esistenti:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.