Wie gruppiere ich Datenrahmenzeilen in einer Liste in Pandas Groupby?

274

Ich habe einen Pandas-Datenrahmen dfwie:

a b
A 1
A 2
B 5
B 5
B 4
C 6

Ich möchte nach der ersten Spalte gruppieren und die zweite Spalte als Listen in Zeilen erhalten :

A [1,2]
B [5,5,4]
C [6]

Ist es möglich, so etwas mit pandas groupby zu machen?

Abhishek Thakur
quelle

Antworten:

393

Sie können dies tun, indem Sie groupbyin der Spalte von Interesse und dann apply listin jeder Gruppe gruppieren:

In [1]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6]})
        df

Out[1]: 
   a  b
0  A  1
1  A  2
2  B  5
3  B  5
4  B  4
5  C  6

In [2]: df.groupby('a')['b'].apply(list)
Out[2]: 
a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

In [3]: df1 = df.groupby('a')['b'].apply(list).reset_index(name='new')
        df1
Out[3]: 
   a        new
0  A     [1, 2]
1  B  [5, 5, 4]
2  C        [6]
EdChum
quelle
7
Dies nimmt viel Zeit in Anspruch, wenn der Datensatz sehr groß ist, z. B. 10 Millionen Zeilen. Gibt es einen schnelleren Weg, dies zu tun? Die Anzahl der Unikate in 'a' liegt jedoch bei 500.000
Abhishek Thakur
6
groupby ist notorisch langsam und speicherhungrig. Sie können nach Spalte A sortieren, dann idxmin und idxmax suchen (wahrscheinlich in einem Diktat speichern) und damit Ihren Datenrahmen in Scheiben schneiden, was meiner Meinung nach schneller wäre
EdChum
1
Als ich diese Lösung mit meinem Problem ausprobierte (mehrere Spalten zu groupBy und zu group), funktionierte sie nicht - Pandas sendeten 'Funktion reduziert nicht'. Dann habe ich tuplehier die zweite Antwort verwendet: stackoverflow.com/questions/19530568/… . Weitere Informationen finden Sie in der zweiten Antwort unter stackoverflow.com/questions/27439023/… .
Andarin
Diese Lösung ist gut, aber gibt es eine Möglichkeit, Listensätze zu speichern. Kann ich also die Duplikate entfernen und dann speichern?
Sriram Arvind Lakshmanakumar
1
@PoeteMaudit Entschuldigung, ich verstehe nicht, was Sie fragen, und Fragen in Kommentaren zu stellen, ist in SO eine schlechte Form. Fragen Sie, wie Sie mehrere Spalten zu einer einzigen Liste verketten können?
EdChum
47

Wenn die Leistung wichtig ist, gehen Sie auf das Numpy-Level:

import numpy as np

df = pd.DataFrame({'a': np.random.randint(0, 60, 600), 'b': [1, 2, 5, 5, 4, 6]*100})

def f(df):
         keys, values = df.sort_values('a').values.T
         ukeys, index = np.unique(keys, True)
         arrays = np.split(values, index[1:])
         df2 = pd.DataFrame({'a':ukeys, 'b':[list(a) for a in arrays]})
         return df2

Tests:

In [301]: %timeit f(df)
1000 loops, best of 3: 1.64 ms per loop

In [302]: %timeit df.groupby('a')['b'].apply(list)
100 loops, best of 3: 5.26 ms per loop
BM
quelle
8
Wie können wir dies verwenden, wenn wir nach zwei oder mehr Schlüsseln gruppieren, z. B. mit .groupby([df.index.month, df.index.day])statt nur .groupby('a')?
Ru111
25

Ein praktischer Weg, um dies zu erreichen, wäre:

df.groupby('a').agg({'b':lambda x: list(x)})

Schreiben Sie benutzerdefinierte Aggregationen: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-using-py

Anamika Modi
quelle
5
lambda args: f(args)ist gleichbedeutend mitf
BallpointBen
6
Eigentlich ist gerade agg(list)genug. Siehe auch hier .
CS95
!! Ich habe nur nach einer Syntax gegoogelt und festgestellt, dass mein eigenes Notizbuch für die Lösung referenziert wurde lol. Vielen Dank für die Verknüpfung. Nur um hinzuzufügen, da 'list' keine Serienfunktion ist, müssen Sie sie entweder mit apply df.groupby('a').apply(list)oder mit agg als Teil eines Diktats verwenden df.groupby('a').agg({'b':list}). Sie können es auch mit Lambda verwenden (was ich empfehle), da Sie so viel mehr damit machen können. Beispiel: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})Hiermit können Sie eine Serienfunktion auf die Spalte c und eine eindeutige und dann eine Listenfunktion auf die Spalte b anwenden.
Akshay Sehgal
21

Wie Sie sagten, kann die groupbyMethode eines pd.DataFrameObjekts die Arbeit erledigen.

Beispiel

 L = ['A','A','B','B','B','C']
 N = [1,2,5,5,4,6]

 import pandas as pd
 df = pd.DataFrame(zip(L,N),columns = list('LN'))


 groups = df.groupby(df.L)

 groups.groups
      {'A': [0, 1], 'B': [2, 3, 4], 'C': [5]}

das gibt und indexweise Beschreibung der Gruppen.

Sie können beispielsweise Elemente einzelner Gruppen abrufen

 groups.get_group('A')

     L  N
  0  A  1
  1  A  2

  groups.get_group('B')

     L  N
  2  B  5
  3  B  5
  4  B  4
Acorbe
quelle
21

So lösen Sie dies für mehrere Spalten eines Datenrahmens:

In [5]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6],'c'
   ...: :[3,3,3,4,4,4]})

In [6]: df
Out[6]: 
   a  b  c
0  A  1  3
1  A  2  3
2  B  5  3
3  B  5  4
4  B  4  4
5  C  6  4

In [7]: df.groupby('a').agg(lambda x: list(x))
Out[7]: 
           b          c
a                      
A     [1, 2]     [3, 3]
B  [5, 5, 4]  [3, 4, 4]
C        [6]        [4]

Diese Antwort wurde von Anamika Modis Antwort inspiriert . Danke dir!

Markus Dutschke
quelle
12

Verwenden Sie eines der folgenden Rezepte groupbyund aggRezepte.

# Setup
df = pd.DataFrame({
  'a': ['A', 'A', 'B', 'B', 'B', 'C'],
  'b': [1, 2, 5, 5, 4, 6],
  'c': ['x', 'y', 'z', 'x', 'y', 'z']
})
df

   a  b  c
0  A  1  x
1  A  2  y
2  B  5  z
3  B  5  x
4  B  4  y
5  C  6  z

Verwenden Sie eine der folgenden Optionen, um mehrere Spalten als Listen zusammenzufassen:

df.groupby('a').agg(list)
df.groupby('a').agg(pd.Series.tolist)

           b          c
a                      
A     [1, 2]     [x, y]
B  [5, 5, 4]  [z, x, y]
C        [6]        [z]

Um nur eine einzelne Spalte in Gruppen aufzulisten, konvertieren Sie den groupby in ein SeriesGroupByObjekt und rufen Sie dann auf SeriesGroupBy.agg. Verwenden,

df.groupby('a').agg({'b': list})  # 4.42 ms 
df.groupby('a')['b'].agg(list)    # 2.76 ms - faster

a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object
cs95
quelle
Sind die oben genannten Methoden garantiert , um die Ordnung aufrechtzuerhalten? Bedeutet dies, dass Elemente aus derselben Zeile (aber unterschiedlichen Spalten, b und c in Ihrem Code oben) denselben Index in den resultierenden Listen haben?
Kai
@ Kai oh, gute Frage. Ja und nein. GroupBy sortiert die Ausgabe nach den Grouper-Schlüsselwerten. Die Sortierung ist jedoch im Allgemeinen stabil, sodass die relative Reihenfolge pro Gruppe erhalten bleibt. Verwenden Sie, um das Sortierverhalten vollständig zu deaktivieren groupby(..., sort=False). Hier würde es keinen Unterschied machen, da ich mich in Spalte A gruppiere, die bereits sortiert ist.
CS95
Es tut mir leid, ich verstehe deine Antwort nicht. Können Sie das näher erläutern? Ich denke, das verdient seine eigene Frage.
Kai
1
Das ist eine sehr gute Antwort! Gibt es auch eine Möglichkeit, die Werte der Liste eindeutig zu machen? so etwas wie .agg (pd.Series.tolist.unique) vielleicht?
Federico Gentile
1
@FedericoGentile können Sie ein Lambda verwenden. Hier ist eine Möglichkeit:df.groupby('a')['b'].agg(lambda x: list(set(x)))
CS95
7

Wenn Sie beim Gruppieren mehrerer Spalten nach einer eindeutigen Liste suchen, kann dies wahrscheinlich helfen:

df.groupby('a').agg(lambda x: list(set(x))).reset_index()
Vanshika
quelle
2

Verwenden wir df.groupbymit Liste und SeriesKonstruktor

pd.Series({x : y.b.tolist() for x , y in df.groupby('a')})
Out[664]: 
A       [1, 2]
B    [5, 5, 4]
C          [6]
dtype: object
YOBEN_S
quelle
2

Es ist Zeit, aggstatt zu verwenden apply.

Wann

df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6], 'c': [1,2,5,5,4,6]})

Wenn Sie möchten, dass mehrere Spalten in einer Liste gestapelt werden, führen Sie zu pd.DataFrame

df.groupby('a')[['b', 'c']].agg(list)
# or 
df.groupby('a').agg(list)

Wenn Sie eine einzelne Spalte in der Liste haben möchten, führen Sie zu ps.Series

df.groupby('a')['b'].agg(list)
#or
df.groupby('a')['b'].apply(list)

Beachten Sie, dass das Ergebnis in pd.DataFrameetwa 10- ps.Seriesmal langsamer ist als das Ergebnis, wenn Sie nur eine einzelne Spalte aggregieren. Verwenden Sie es in mehrspaltigen Fällen.

Mithril
quelle
0

Hier habe ich Elemente mit "|" gruppiert. als Trennzeichen

    import pandas as pd

    df = pd.read_csv('input.csv')

    df
    Out[1]:
      Area  Keywords
    0  A  1
    1  A  2
    2  B  5
    3  B  5
    4  B  4
    5  C  6

    df.dropna(inplace =  True)
    df['Area']=df['Area'].apply(lambda x:x.lower().strip())
    print df.columns
    df_op = df.groupby('Area').agg({"Keywords":lambda x : "|".join(x)})

    df_op.to_csv('output.csv')
    Out[2]:
    df_op
    Area  Keywords

    A       [1| 2]
    B    [5| 5| 4]
    C          [6]
Ganesh Kharad
quelle
0

Der einfachste Weg, den ich bisher gesehen habe, ist, zumindest für eine Spalte, die Anamikas Antwort nur mit der Tupelsyntax für die Aggregatfunktion ähnelt, fast das Gleiche nicht zu erreichen .

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
Metrd
quelle