Pandas groupby: Wie man eine Vereinigung von Saiten bekommt

122

Ich habe einen Datenrahmen wie diesen:

   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       !

Berufung

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

wird zurückkehren

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

Jetzt möchte ich "dasselbe" für Spalte "C" tun. Da diese Spalte Zeichenfolgen enthält, funktioniert sum () nicht (obwohl Sie vielleicht denken, dass es die Zeichenfolgen verketten würde). Was ich wirklich gerne sehen würde, ist eine Liste oder ein Satz von Zeichenfolgen für jede Gruppe, dh

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 ) funktioniert jedoch nicht

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

ist ein

pandas.core.groupby.SeriesGroupBy object

Ich hatte gehofft, dass jede Serienmethode funktionieren würde. Irgendwelche Ideen?

Anne
quelle

Antworten:

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

Wenn Sie Ihre eigene Funktion anwenden, werden nicht numerische Spalten nicht automatisch ausgeschlossen. Dies ist jedoch langsamer als die Anwendung .sum()auf diegroupby

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 können so ziemlich alles tun, 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 Sie dies für einen ganzen Frame, eine Gruppe nach der anderen. Der Schlüssel ist, a zurückzugebenSeries

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}
Jeff
quelle
Es scheint, dass diese Operationen jetzt vektorisiert sind, wodurch die Notwendigkeit von applyund lambdas beseitigt wird . Ich bin hierher gekommen und habe mich gefragt, warum pandaseigentlich Concats und kein Fehler beim Summieren von Strings zurückgegeben werden.
NelsonGon
1
Wenn Sie versuchen, Zeichenfolgen zu verknüpfen und ein Zeichen dazwischen hinzuzufügen, ist die von @voithos unten empfohlene .agg-Lösung viel schneller als die hier empfohlene .apply-Lösung. Bei meinen Tests wurde ich 5-10x schneller.
Doubledown
70

Mit dieser applyMethode können Sie eine beliebige Funktion auf die gruppierten Daten anwenden. Wenn Sie also einen Satz möchten, bewerben Sie sich set. Wenn Sie eine Liste wünschen, bewerben Sie sich 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

Wenn Sie etwas anderes wollen, schreiben Sie einfach eine Funktion, die macht, was Sie wollen und dann applydas.

BrenBarn
quelle
Funktioniert gut, aber Spalte A fehlt.
Vineesh TP
@VineeshTP: Spalte A wurde als Gruppierungsspalte verwendet, daher befindet sie sich im Index, wie Sie im Beispiel sehen können. Sie können es mithilfe von als Spalte wieder herausholen .reset_index().
BrenBarn
30

Möglicherweise können Sie die Funktion aggregate(oder agg) verwenden, um die Werte zu verketten. (Ungetesteter Code)

df.groupby('A')['B'].agg(lambda col: ''.join(col))
voithos
quelle
Es funktioniert wirklich. Tolle. Wie @voithos "ungetestet" erwähnte, war ich nicht sehr optimistisch. Bit Ich habe seine Version als Eintrag in einem Agg-Wörterbuch getestet und es hat wie beabsichtigt funktioniert: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Machte meinen Tag
matthhias
2
Wenn Sie versuchen, Zeichenfolgen mit einer Art Trennzeichen zu verknüpfen, ist dieser .agg-Vorschlag viel schneller als .apply. Für einen Datensatz mit mehr als 600.000 Textzeichenfolgen habe ich identische Ergebnisse 5-10x schneller erhalten.
Doubledown
14

Sie könnten dies versuchen:

df.groupby('A').agg({'B':'sum','C':'-'.join})
user3241146
quelle
2
Aus der Bewertung: Könnten Sie bitte Ihrer Antwort weitere Erklärungen hinzufügen?
Toti08
1
Groupby wird auf Spalte 'A' angewendet und mit der Agg-Funktion könnte ich verschiedene Funktionen für verschiedene Spalten verwenden, z. B. die Elemente in Spalte 'C' summieren, die Elemente in Spalte 'C' verketten und ein '-' zwischen die Wörter
einfügen
8

Eine einfache Lösung wäre:

>>> df.groupby(['A','B']).c.unique().reset_index()
UserYmY
quelle
Dies sollte die richtige Antwort sein. bringt dich dazu, sauber zu antworten. Vielen Dank!
Imsrgadich
Wenn jemand daran interessiert ist, den Inhalt der Liste zu einer Zeichenfolge zusammenzufügen df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Vivek-Ananth
8

Benannte Aggregationen mit pandas >= 0.25.0

Seit der Pandas-Version 0.25.0 haben wir Aggregationen benannt, in denen wir unsere Spalten gruppieren, aggregieren und gleichzeitig neuen Namen zuweisen können. Auf diese Weise erhalten wir die MultiIndex-Spalten nicht, und die Spaltennamen sind angesichts der darin enthaltenen Daten sinnvoller:


aggregieren und eine Liste von Zeichenfolgen erhalten

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]

aggregieren und verbinden Sie die Zeichenfolgen

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
Erfan
quelle
6

Wenn Sie Spalte B im Datenrahmen überschreiben möchten, sollte dies funktionieren:

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

Nach der guten Antwort von @ Erfan möchten Sie bei einer Analyse der Aggregatwerte meistens die eindeutigen möglichen Kombinationen dieser vorhandenen Zeichenwerte:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
Paul Rougieux
quelle