Łączna liczba pand jest różna


97

Powiedzmy, że mam dziennik aktywności użytkowników i chcę wygenerować raport o łącznym czasie trwania i liczbie unikalnych użytkowników dziennie.

import numpy as np
import pandas as pd
df = pd.DataFrame({'date': ['2013-04-01','2013-04-01','2013-04-01','2013-04-02', '2013-04-02'],
    'user_id': ['0001', '0001', '0002', '0002', '0002'],
    'duration': [30, 15, 20, 15, 30]})

Sumowanie czasu trwania jest dość proste:

group = df.groupby('date')
agg = group.aggregate({'duration': np.sum})
agg
            duration
date
2013-04-01        65
2013-04-02        45

Chciałbym jednocześnie zsumować czas trwania i liczyć różne elementy, ale nie mogę znaleźć odpowiednika dla count_distinct:

agg = group.aggregate({ 'duration': np.sum, 'user_id': count_distinct})

To działa, ale na pewno jest lepszy sposób, prawda?

group = df.groupby('date')
agg = group.aggregate({'duration': np.sum})
agg['uv'] = df.groupby('date').user_id.nunique()
agg
            duration  uv
date
2013-04-01        65   2
2013-04-02        45   1

Myślę, że po prostu muszę zapewnić funkcję, która zwraca liczbę różnych elementów obiektu Series do funkcji agregującej, ale nie mam do dyspozycji wielu bibliotek. Wydaje się również, że obiekt groupby już zna te informacje, więc czy nie powtórzyłbym tego po prostu?

Odpowiedzi:


159

Co powiesz na:

>>> df
         date  duration user_id
0  2013-04-01        30    0001
1  2013-04-01        15    0001
2  2013-04-01        20    0002
3  2013-04-02        15    0002
4  2013-04-02        30    0002
>>> df.groupby("date").agg({"duration": np.sum, "user_id": pd.Series.nunique})
            duration  user_id
date                         
2013-04-01        65        2
2013-04-02        45        1
>>> df.groupby("date").agg({"duration": np.sum, "user_id": lambda x: x.nunique()})
            duration  user_id
date                         
2013-04-01        65        2
2013-04-02        45        1

1
Otóż ​​to. pd.Series.nunique jest tym, czego nie mogłem znaleźć, cóż, nie mogłem poprawnie działać. Z perspektywy czasu dość oczywiste. Dzięki!
dave

7
Ta odpowiedź jest nieaktualna. Możesz teraz używać nuniquebezpośrednio. Zobacz rozwiązanie @Blodwyn Pig poniżej
Ted Petrou

Dzięki @TedPetrou, jestem koderem, dawniej znanym jako Blodwyn Pig;)
Ricky McMaster

Hej, czy wiesz, jak uzyskać liczbę powtórzeń?
Ambleu,

65

'nunique' jest opcją dla .agg () od pand 0.20.0, więc:

df.groupby('date').agg({'duration': 'sum', 'user_id': 'nunique'})

Czy można zaatakować i uzyskać unikalne wartości? coś w styluduration: np.unique
facet

@guy Trydf.groupby('date').agg({'user_id': lambda s: s.unique().reset_index(drop=True)})
BallpointBen

Jak otrzymujemy wynik?

19

Po dodaniu do już udzielonych odpowiedzi rozwiązanie wykorzystujące ciąg "nunique"wydaje się znacznie szybsze, przetestowane tutaj na ~ 21 milionach wierszy dataframe, a następnie zgrupowane w ~ 2M

%time _=g.agg({"id": lambda x: x.nunique()})
CPU times: user 3min 3s, sys: 2.94 s, total: 3min 6s
Wall time: 3min 20s

%time _=g.agg({"id": pd.Series.nunique})
CPU times: user 3min 2s, sys: 2.44 s, total: 3min 4s
Wall time: 3min 18s

%time _=g.agg({"id": "nunique"})
CPU times: user 14 s, sys: 4.76 s, total: 18.8 s
Wall time: 24.4 s

2
Dobry chwyt! Wydaje mi się, że jest to b / c w przypadku „lambda” / „innej funkcji” jest stosowane sekwencyjnie, podczas gdy funkcje „znane” są stosowane do całej kolumny w sposób zwektoryzowany.
Ufos

które rozwiązanie pochodzi od @Blodwyn Pig?
Chogg

@Chogg, najszybszy!
m-dz

@Chogg - przepraszam, że zmieniłem nazwę użytkownika. To ja.
Ricky McMaster
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.