Jak grupować wiersze ramki danych w listę w grupach pand?

274

Mam ramkę danych pand, dftakich jak:

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

Chcę pogrupować według pierwszej kolumny i uzyskać drugą kolumnę jako listy w wierszach :

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

Czy można zrobić coś takiego za pomocą Panda Groupby?

Abhishek Thakur
źródło

Odpowiedzi:

393

Możesz to zrobić za pomocą groupbygrupowania według kolumny zainteresowań, a następnie apply listkażdej grupy:

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
źródło
7
To zajmuje dużo czasu, jeśli zbiór danych jest ogromny, powiedzmy 10 milionów wierszy. Czy jest na to szybszy sposób? Liczba unikatów w „a” wynosi jednak około 500 tys.
Abhishek Thakur
6
groupby jest notorycznie wolne i głodne pamięci, co możesz zrobić, to posortować według kolumny A, a następnie znaleźć idxmin i idxmax (prawdopodobnie przechowują to w nagraniu), a użycie tego do wycięcia ramki danych byłoby szybsze, myślę
EdChum
1
Kiedy próbowałem tego rozwiązania z moim problemem (posiadanie wielu kolumn do grupowania według grup i grup), nie działało - wysłano pandy „Funkcja nie zmniejsza”. Następnie użyłem tuplepo drugim odpowiedź tutaj: stackoverflow.com/questions/19530568/... . Wyjaśnienia znajdują się w drugiej odpowiedzi na stackoverflow.com/questions/27439023/ ...
Andarin
To rozwiązanie jest dobre, ale czy istnieje sposób na przechowywanie zestawu list, co oznacza, że ​​mogę usunąć duplikaty, a następnie je zapisać?
Sriram Arvind Lakshmanakumar
1
@PoeteMaudit Przepraszam, nie rozumiem, o co pytasz i zadawanie pytań w komentarzach jest złe w SO. Czy pytasz, jak połączyć wiele kolumn w jedną listę?
EdChum
47

Jeśli wydajność jest ważna, przejdź do poziomu numpy:

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

Testy:

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
źródło
8
Jak możemy tego użyć, jeśli grupujemy według dwóch lub więcej kluczy, np. .groupby([df.index.month, df.index.day])Zamiast zamiast .groupby('a')?
ru111
25

Przydatnym sposobem na osiągnięcie tego byłoby:

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

Zajrzyj do pisania Niestandardowe agregacje: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-using-py

Anamika Modi
źródło
5
lambda args: f(args)jest równoważne zf
BallpointBen,
6
Właściwie agg(list)to wystarczy. Zobacz także tutaj .
cs95,
!! Właśnie szukałem składni i zdałem sobie sprawę, że mój własny notebook został wymieniony w rozwiązaniu lol. Dziękujemy za połączenie tego. Żeby dodać, ponieważ „lista” nie jest funkcją szeregową, będziesz musiał albo użyć jej z aplikacją, df.groupby('a').apply(list)albo użyć z agiem jako części dykta df.groupby('a').agg({'b':list}). Możesz również użyć go z lambda (co polecam), ponieważ możesz zrobić o wiele więcej z nim. Przykład: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})który pozwala zastosować funkcję szeregową do kolumny c i unikalną, a następnie funkcję listy do kolumny b.
Akshay Sehgal
21

Jak mówiłeś, groupbymetoda pd.DataFrameobiektu może wykonać zadanie.

Przykład

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

który daje i grupuje opis grup.

Aby uzyskać elementy pojedynczych grup, możesz na przykład to zrobić

 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
źródło
21

Aby rozwiązać ten problem dla kilku kolumn ramki danych:

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]

Ta odpowiedź została zainspirowana odpowiedzią Anamiki Modi . Dziękuję Ci!

Markus Dutschke
źródło
12

Skorzystaj z dowolnej z poniższych opcji groupbyi aggprzepisów.

# 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

Aby agregować wiele kolumn jako listy, użyj dowolnej z następujących opcji:

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]

Aby grupować listę tylko jednej kolumny, przekonwertuj groupby na SeriesGroupByobiekt, a następnie wywołaj SeriesGroupBy.agg. Posługiwać się,

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
źródło
czy powyższe metody gwarantują zachowanie porządku? co oznacza, że ​​elementy z tego samego wiersza (ale różnych kolumn, b i c w powyższym kodzie) będą miały ten sam indeks na wynikowych listach?
Kai
@Kai oh, dobre pytanie. Tak i nie. GroupBy sortuje dane wyjściowe według wartości klucza grupowania. Jednak sortowanie jest ogólnie stabilne, więc zachowane jest względne uporządkowanie na grupę. Aby całkowicie wyłączyć sortowanie, użyj groupby(..., sort=False). Tutaj nie miałoby to znaczenia, ponieważ grupuję według kolumny A, która jest już posortowana.
cs95
przepraszam, nie rozumiem twojej odpowiedzi. Czy możesz wyjaśnić bardziej szczegółowo. Myślę, że to zasługuje na własne pytanie ..
Kai
1
To bardzo dobra odpowiedź! Czy istnieje również sposób, aby wartości listy były unikalne? coś w stylu .agg (pd.Series.tolist.unique)?
Federico Gentile,
1
@FedericoGentile możesz użyć lambda. Oto jeden sposób:df.groupby('a')['b'].agg(lambda x: list(set(x)))
cs95,
7

Jeśli szukasz unikalnej listy podczas grupowania wielu kolumn, może to prawdopodobnie pomóc:

df.groupby('a').agg(lambda x: list(set(x))).reset_index()
Vanshika
źródło
2

Użyjmy df.groupbyrazem z listą i Serieskonstruktorem

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
źródło
2

Czas użyć aggzamiast apply.

Kiedy

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

Jeśli chcesz umieścić wiele kolumn na liście, wynik pd.DataFrame

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

Jeśli chcesz pojedynczej kolumny na liście, wynik ps.Series

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

Uwaga: wynik pd.DataFramejest około 10 razy wolniejszy niż wynik, ps.Seriesgdy agregujesz tylko jedną kolumnę, użyj jej w przypadku wielokolumn.

Mithril
źródło
0

Tutaj pogrupowałem elementy za pomocą „|” jako separator

    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
źródło
0

Najłatwiejszy sposób, w jaki nie widziałem, pozwala osiągnąć większość tego samego co najmniej dla jednej kolumny, która jest podobna do odpowiedzi Anamiki tylko ze składnią krotki dla funkcji agregującej.

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
Metrd
źródło