Die Gesamtzahl der Pandas ist unterschiedlich


91

Angenommen, ich habe ein Protokoll der Benutzeraktivität und möchte einen Bericht über die Gesamtdauer und die Anzahl der eindeutigen Benutzer pro Tag erstellen.

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

Die Gesamtdauer ist ziemlich einfach:

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

Ich möchte die Dauer und die Anzahl der Unterscheidungsmerkmale gleichzeitig summieren, aber ich kann anscheinend kein Äquivalent für count_distinct finden:

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

Das funktioniert, aber es gibt doch einen besseren Weg, oder?

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

Ich denke, ich muss nur eine Funktion bereitstellen, die die Anzahl der verschiedenen Elemente eines Series-Objekts an die Aggregatfunktion zurückgibt, aber ich habe nicht viel Kontakt mit den verschiedenen Bibliotheken, die mir zur Verfügung stehen. Es scheint auch, dass das groupby-Objekt diese Informationen bereits kennt. Würde ich also nicht einfach den Aufwand duplizieren?

Antworten:


152

Wie wäre es mit:

>>> 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
Das ist es. pd.Series.nunique ist das, was ich nicht finden konnte. Nun, ich konnte nicht richtig arbeiten. Im Nachhinein ziemlich offensichtlich. Vielen Dank!
Dave

5
Diese Antwort ist veraltet. Sie können jetzt nuniquedirekt verwenden. Siehe @Blodwyn Pigs Lösung unten
Ted Petrou

Danke @ TedPetrou, ich bin der Coder, der früher als Blodwyn Pig bekannt war;)
Ricky McMaster

Hey, weißt du, wie man eine nicht doppelte Zählung erhält?
Ambleu

57

'nunique' ist eine Option für .agg () seit pandas 0.20.0, also:

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

Ist es möglich, die eindeutigen Werte zu ermitteln und zu ermitteln? so etwas wieduration: np.unique
Kerl

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

Wie bekommen wir die Ausgabe?

16

Wenn man nur die bereits gegebenen Antworten hinzufügt, "nunique"scheint die Lösung mit der Zeichenfolge viel schneller zu sein. Sie wurde hier in einem Datenrahmen mit ~ 21 Millionen Zeilen getestet und dann in ~ 2 Millionen gruppiert

%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

1
Schöner Fang! Ich denke, es ist b / c in einem "Lambda" / "anderen Funktion" -Fall, in dem es sequentiell angewendet wird, während "bekannte" Funktionen vektorisiert auf die gesamte Spalte angewendet werden.
Ufos

Welche Lösung stammt von @Blodwyn Pig?
Chogg

@Chogg, der schnellste!
m-dz

@Chogg - Entschuldigung, ich habe meinen Benutzernamen geändert. Ich war es.
Ricky McMaster
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.