2013-07-24 4 views
48

ich einen Datenrahmen wie dieses:Pandas GROUPBY: Wie eine Vereinigung von Strings bekommen

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  ! 

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

kehrt

A 
1 1.615586 
2 0.421821 
3 0.463468 
4 0.643961 

Jetzt würde ich tun "das gleiche" für die Spalte "C". Da diese Spalte Zeichenfolgen enthält, funktioniert sum() nicht (obwohl Sie vielleicht denken, dass es die Zeichenfolgen verketten würde). Was würde ich wirklich gerne sehen, ist eine Liste oder einen Satz von Saiten für jede Gruppe, das heißt

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

Ich habe versucht, Wege zu finden, dies zu tun.

Series.unique() (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html) nicht funktioniert, obwohl

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

ein

ist
pandas.core.groupby.SeriesGroupBy object 

so wurde ich jede Serie Methode funktionieren würde hoffen. Irgendwelche Ideen?

Antwort

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 

Wenn Sie eine eigene Funktion anwenden, gibt es keine automatischen Ausschlüsse für nicht numerische Spalten. Dies ist langsamer, aber als die Anwendung von .sum() zum 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 standardmäßig verkettet

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

Sie ziemlich viel tun können, was Sie

wollen
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 

tun dies ein ganze Rahmengruppe gleichzeitig. Key ist zurück ein 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

Dank Jeff. Wie könnte ich verschiedene Funktionen auf mehrere Spalten gleichzeitig anwenden, z. Summe in Spalte "B" und in Spalte "C" setzen? – Anne

+0

hinzugefügt einen Abschnitt, um das zu tun – Jeff

+1

Danke Jeff!Ich habe es noch nicht versucht, aber ich möchte die Logik dahinter verstehen. Sie erstellen ein Reihenwörterbuch und wandeln es in eine Serie um? Kann mein (zugegebenermaßen frazzled) Gehirn nicht bekommen, was das überhaupt bedeuten würde ... Würden Sie etwas mehr ausdenken? Und in 'A = x ['A']. Sum()', ist das erste A ein Objekt oder eine Zeichenkette? Wenn es eine Zeichenfolge ist, sollte es keine Anführungszeichen haben? Entschuldigung, wie gesagt, ziemlich durcheinander, ich hoffe, dass ich einen Sinn habe ... – Anne

28

Sie die apply Methode verwenden, kann eine beliebige Funktion auf den gruppierten Daten anzuwenden. Wenn Sie also ein Set wünschen, wenden Sie set an. Wenn Sie eine Liste wünschen, wenden Sie list an.

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

Wenn Sie etwas anderes wollen, schreiben Sie einfach eine Funktion, die das tut, was Sie wollen, und dann apply das.

7

Sie können möglicherweise die aggregate (oder agg) -Funktion verwenden, um die Werte zu verketten. (Ungeprüfte Code)

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

eine einfache Lösung wäre:

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