Conteo agregado de pandas distinto

3 minutos de lectura

avatar de usuario
dave

Digamos que tengo un registro de la actividad de los usuarios y quiero generar un informe de la duración total y la cantidad 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]})

Agregar la duración es bastante sencillo:

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 los distintivos 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 mejor manera, ¿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 Serie 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?

¿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

  • Eso es todo. pd.Series.nunique es lo que no pude encontrar, bueno, no pude hacer que funcionara correctamente. Bastante obvio en retrospectiva. ¡Gracias!

    – dave

    1 de septiembre de 2013 a las 3:36


  • Esta respuesta está desactualizada. Ahora puedes usar nunique directamente. Vea la solución de @Blodwyn Pig a continuación

    –Ted Petrou

    5 de noviembre de 2017 a las 20:07

  • Oye, ¿sabes cómo obtener un recuento no duplicado?

    – haneulkim

    25 de febrero de 2020 a las 4:17

avatar de usuario
Ricky McMaster

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

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

  • ¿Es posible agregar y obtener los valores únicos? algo como duration: np.unique

    – tipo

    26/09/2017 a las 20:31

  • @chico Prueba df.groupby('date').agg({'user_id': lambda s: s.unique().reset_index(drop=True)})

    – BolígrafoBen

    14 de agosto de 2018 a las 19:01

  • ¿Cómo obtenemos la salida?

    usuario9366862

    15/10/2018 a las 21:09

avatar de usuario
usuario6903745

Solo agregando a las respuestas ya dadas, la solución usando la cadena "nunique" parece mucho más rápido, probado aquí en un marco de datos de ~ 21 millones de filas, luego agrupado en ~ 2 millones

%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

  • ¡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

    9 dic 2017 a las 20:16

  • ¿Qué solución es de @Blodwyn Pig?

    – Chogg

    30 de marzo de 2018 a las 19:26

  • @Chogg, ¡el más rápido!

    – mdz

    23 de abril de 2018 a las 14:11

  • @Chogg: lo siento, cambié mi nombre de usuario. Fui yo.

    –Ricky McMaster

    16 de octubre de 2018 a las 7:49

Si desea obtener solo una cantidad de valores distintos por grupo, puede usar el método nunique directamente con el DataFrameGroupBy objeto:

df.groupby('date')['user_id'].nunique()

¿Ha sido útil esta solución?