Recuento agregado de pandas distinto

94

Digamos que tengo un registro de la actividad de los usuarios y quiero generar un informe de la duración total y el número de usuarios únicos por día.

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

La duración agregada es bastante sencilla:

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

Lo que me gustaría hacer es sumar la duración y contar las diferencias al mismo tiempo, pero parece que no puedo encontrar un equivalente para count_distinct:

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

Esto funciona, pero seguramente hay una forma mejor, ¿no?

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

Estoy pensando que solo necesito proporcionar una función que devuelva el recuento de elementos distintos de un objeto Series a la función agregada, pero no tengo mucha exposición a las diversas bibliotecas a mi disposición. Además, parece que el objeto groupby ya conoce esta información, así que ¿no estaría simplemente duplicando el esfuerzo?

Dave
fuente

Respuestas:

156

¿Qué tal cualquiera de:

>>> 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
fuente
1
Eso es. pd.Series.nunique es lo que no pude encontrar, bueno, no pude ponerme a trabajar correctamente. Bastante obvio en retrospectiva. ¡Gracias!
Dave
5
Esta respuesta está desactualizada. Ahora puede usar nuniquedirectamente. Vea la solución de @Blodwyn Pig a continuación
Ted Petrou
Gracias @TedPetrou, soy el codificador anteriormente conocido como Blodwyn Pig;)
Ricky McMaster
Oye, ¿sabes cómo obtener un recuento de no duplicados?
Ambleu
61

'nunique' es una opción para .agg () desde pandas 0.20.0, entonces:

df.groupby('date').agg({'duration': 'sum', 'user_id': 'nunique'})
Ricky McMaster
fuente
¿Es posible agg y obtener los valores únicos? algo comoduration: np.unique
chico
@guy Trydf.groupby('date').agg({'user_id': lambda s: s.unique().reset_index(drop=True)})
BallpointBen
¿Cómo obtenemos el resultado?
17

Simplemente agregando a las respuestas ya dadas, la solución que usa la cadena "nunique"parece mucho más rápida, probada aquí en un marco de datos de ~ 21M filas, luego agrupada en ~ 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
usuario6903745
fuente
1
¡Buena atrapada! Supongo que es b / c en un caso de "lambda" / "otra función", se aplica secuencialmente, mientras que las funciones "conocidas" se aplican a toda la columna de forma vectorizada.
Ovnis
¿Qué solución es de @Blodwyn Pig?
Chogg
@Chogg, ¡el más rápido!
m-dz
@Chogg - lo siento, cambié mi nombre de usuario. Fui yo.
Ricky McMaster