Pandy groupby: Jak uzyskać związek strun


122

Mam taką ramkę danych:

   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       !

Powołanie

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

wróci

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

Teraz chciałbym zrobić „to samo” dla kolumny „C”. Ponieważ ta kolumna zawiera ciągi, sum () nie działa (chociaż możesz pomyśleć, że połączy łańcuchy). To, co naprawdę chciałbym zobaczyć, to lista lub zestaw ciągów dla każdej grupy, tj

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

Próbowałem znaleźć sposób, aby to zrobić.

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) nie działa, chociaż

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

jest

pandas.core.groupby.SeriesGroupBy object

więc miałem nadzieję, że jakakolwiek metoda serii zadziała. Jakieś pomysły?

Odpowiedzi:


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

Kiedy stosujesz własną funkcję, nie ma automatycznego wykluczania kolumn nienumerycznych. Jest to jednak wolniejsze niż zastosowanie .sum()dogroupby

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 domyślnie łączy się

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

Możesz robić prawie wszystko, co chcesz

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

Robię to na całej klatce, jednej grupie naraz. Kluczem jest zwrócenie plikuSeries

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}

Wygląda na to, że operacje te są teraz wektoryzowane, co eliminuje potrzebę applyi lambdas. Przyszedłem tutaj, zastanawiając się, dlaczego pandaswłaściwie konkatuje i nie zwraca błędu podczas sumowania ciągów.
NelsonGon

1
Jeśli próbujesz połączyć łańcuchy i dodać znak pomiędzy nimi, rozwiązanie .agg zalecane przez @voithos poniżej jest znacznie szybsze niż zalecane tutaj .apply. W moich testach robiłem się 5-10x szybciej.
Doubledown

70

Możesz użyć tej applymetody, aby zastosować dowolną funkcję do zgrupowanych danych. Więc jeśli chcesz zestaw, aplikuj set. Jeśli chcesz otrzymać listę, aplikuj 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

Jeśli chcesz czegoś innego, po prostu napisz funkcję, która robi to, co chcesz, a potem applyto.


Działa dobrze, ale brakuje kolumny A.
Vineesh TP

@VineeshTP: Kolumna A została użyta jako kolumna grupująca, więc znajduje się w indeksie, jak widać w przykładzie. Możesz go odzyskać jako kolumnę, używając .reset_index().
BrenBarn

30

Możesz użyć funkcji aggregate(lub agg) do konkatenacji wartości. (Nieprzetestowany kod)

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

To naprawdę działa. Niesamowity. Jak @voithos wspomniał o „nietestowanym”, nie byłem zbyt optymistyczny. Bit Testowałem jego wersję jako wpis w słowniku Agg i działała zgodnie z przeznaczeniem: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Made my day
matthhias

2
Jeśli próbujesz połączyć ciągi znaków razem z jakimś rodzajem separatora, zauważyłem, że ta sugestia .agg jest znacznie szybsza niż .apply. W przypadku zestawu danych obejmującego ponad 600 tys. Ciągów tekstowych otrzymałem identyczne wyniki 5–10 razy szybciej.
Doubledown

14

Możesz spróbować tego:

df.groupby('A').agg({'B':'sum','C':'-'.join})

2
Z recenzji: czy możesz dodać więcej wyjaśnień do swojej odpowiedzi?
toti08

1
Grupowanie jest stosowane w kolumnie `` A '', a za pomocą funkcji ag można użyć różnych funkcji w różnych kolumnach, na przykład zsumować elementy w kolumnie
``

8

prostym rozwiązaniem byłoby:

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

to powinna być właściwa odpowiedź. dostajesz czystą odpowiedź. wielkie dzięki!
imsrgadich

Jeśli ktoś jest zainteresowany połączeniem zawartości listy w string df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Vivek-Ananth

8

Nazwane agregacje z pandas >= 0.25.0

Od wersji 0.25.0 pandy nazwaliśmy agregacje, w których możemy grupować, agregować i jednocześnie przypisywać nowe nazwy do naszych kolumn. W ten sposób nie otrzymamy kolumn MultiIndex, a nazwy kolumn mają większy sens, biorąc pod uwagę zawarte w nich dane:


agregować i uzyskać listę ciągów

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]

agregować i łączyć w ciągi

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

6

Jeśli chcesz nadpisać kolumnę B w ramce danych, powinno to zadziałać:

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

2

Zgodnie z dobrą odpowiedzią @ Erfana, w większości przypadków w analizie zagregowanych wartości potrzebujesz unikalnych możliwych kombinacji tych istniejących wartości znaków:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
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.