2013-07-24 30 views
48

Ho un dataframe come questo:Pandas GroupBy: Come ottenere un'unione di stringhe

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  ! 

Calling

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

tornerà

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 (sebbene si possa pensare che concatenerebbe le stringhe). Quello che mi piacerebbe davvero vedere è una lista o un insieme di stringhe per ogni gruppo, vale a dire

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

ho cercato di trovare il modo per farlo.

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

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

è un

pandas.core.groupby.SeriesGroupBy object 

quindi speravo qualsiasi metodo della serie avrebbe funzionato. Qualche idea?

risposta

92
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 si applica la propria funzione, non esistono esclusioni automatiche di colonne non numeriche. Questo è più lento, però, che l'applicazione di .sum() al groupby

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 da concatena predefinite

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

Si può fare più o meno 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 

In questo modo un intero gruppo di frame alla volta. Chiave è quello di restituire un Series

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} 
+0

Grazie Jeff. Come potrei applicare anche diverse funzioni su più colonne in una volta, ad es. somma sulla colonna "B" e impostata sulla colonna "C"? – Anne

+0

aggiunto una sezione per farlo che – Jeff

+1

Grazie Jeff!Non l'ho ancora provato ma vorrebbe capire la logica dietro. Stai creando un dizionario di serie e lo trasformi in una serie? Non riesco a convincere il mio cervello (certamente scombussolato) su cosa significherebbe ... Ti dispiacerebbe elaborare un po 'di più? E in 'A = x ['A']. Sum()', è il primo A un oggetto o una stringa? Se è una stringa, non dovrebbe avere virgolette in giro? Scusa, come ho detto, piuttosto abbattuto, spero di avere un senso ... – Anne

28

È possibile utilizzare il metodo apply per applicare una funzione arbitraria ai dati raggruppati. Quindi se vuoi un set, applica set. Se si desidera un elenco, applicare 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 si desidera qualcosa di diverso, basta scrivere una funzione che fa quello che si desidera e quindi apply che.

7

Potrebbe essere possibile utilizzare la funzione aggregate (o agg) per concatenare i valori. (Codice non testato)

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

una soluzione semplice potrebbe essere:

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