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?

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]

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

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


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

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!


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

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

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

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.


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]

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'))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.