Łą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?

dave
źródło

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
DSM
źródło
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'})
Ricky McMaster
źródło
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
user6903745
źródło
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