Obtenga recuentos por grupo usando pandas [duplicado]

5

Tengo un marco de datos de pandas que contiene datos como se muestra a continuación:

ID  year_month_id   Class
1   201612          A
2   201612          D
3   201612          B
4   201612          Other
5   201612          Other
6   201612          Other
7   201612          A
8   201612          Other
9   201612          A
1   201701          B

Por lo tanto, una identificación puede estar en cualquier clase en un mes en particular y el próximo mes su clase podría cambiar. Ahora, lo que quiero hacer es que para cada ID obtenga la cantidad de meses que ha estado en una clase en particular y también la última clase a la que pertenece. Algo como a continuación:

ID  Class_A Class_B Class_D Other Latest_Class
1   2        3       4         0    B
2   12       0       0         0    D

¿Cómo logro esto en Python? ¿Alguien puede ayudarme con esto? Además, dado que el conjunto de datos real es enorme y la verificación manual no es posible, ¿cómo puedo obtener una lista de identificaciones que pertenecen a más de una clase?

0
4

Podemos usar tabla dinámica y concat ie

ndf = df.pivot_table(index=['ID'],columns=['Class'],aggfunc='count',fill_value=0)\
    .xs('year_month_id', axis=1, drop_level=True)

ndf['latest'] = df.sort_values('ID').groupby('ID')['Class'].tail(1).values

Class  A  B  D  Other latest
ID                          
1      1  1  0      0      B
2      0  0  1      0      D
3      0  1  0      0      B
4      0  0  0      1  Other
5      0  0  0      1  Other
6      0  0  0      1  Other
7      1  0  0      0      A
8      0  0  0      1  Other
9      1  0  0      0      A
3
  • 1
    Usar pivotes una buena opción aquí, supongo que debería ser la más rápida.
    cs95
    20 dic 2017 a las 12:48
  • 1
    Cuando uno pivota solo 2 columnas y usa countcomo aggfunc, llenar con ceros (exactamente en este caso) vale la pena considerar su uso pd.crosstab.
    jo9k
    20/12/2017 a las 13:35
  • muchas gracias @Dark. Dado que los datos son enormes y no puedo ir manualmente y verificar si la salida es correcta para cada ID, ¿cómo puedo obtener una lista de ID que tienen entradas como 1 en más de 1 columna? 20/12/2017 a las 13:56
3

Puede obtener recuentos groupbycon agregado count, remodelar por unstack. Por último, agregue una nueva columna con drop_duplicates:

df1 = df.groupby(['ID','Class'])['year_month_id'].count().unstack(fill_value=0)
df1['Latest_Class'] = df.drop_duplicates('ID', keep='last').set_index('ID')['Class']
print (df1)
Class  A  B  D  Other Latest_Class
ID                                
1      1  1  0      0            B
2      0  0  1      0            D
3      0  1  0      0            B
4      0  0  0      1        Other
5      0  0  0      1        Other
6      0  0  0      1        Other
7      1  0  0      0            A
8      0  0  0      1        Other
9      1  0  0      0            A
1
  • Votante negativo, si hay algún problema con mi respuesta, hágamelo saber para que pueda corregirlo. Gracias.
    jezrael
    20 dic 2017 a las 13:09
3

Puede obtener un recuento de clases asistidas con groupby+ value_counts+ unstack-

g = df.groupby('ID')
i = g.Class.value_counts().unstack(fill_value=0)

Para obtener la última clase, use groupby+ last-

j = g.Class.last()

Concatenar para obtener su resultado -

pd.concat([i, j], 1).rename(columns={'Class': 'LastClass'})

    A  B  D  Other LastClass
ID                          
1   1  1  0      0         B
2   0  0  1      0         D
3   0  1  0      0         B
4   0  0  0      1     Other
5   0  0  0      1     Other
6   0  0  0      1     Other
7   1  0  0      0         A
8   0  0  0      1     Other
9   1  0  0      0         A

Para obtener una lista de ID que tienen más de 1 por fila, use sum+ una máscara -

k = i.sum(axis=1)
k[k > 1]

ID
1    2
dtype: int64
2
  • 1
    Votante negativo, si hay algún problema con la respuesta, hágamelo saber para que pueda corregirlo. Gracias.
    jezrael
    20 dic 2017 a las 13:09
  • 2
    @jezrael Alguien confundió la Navidad con el día de los inocentes.
    cs95
    20/12/2017 a las 13:11
1

Cuando uno pivota solo 2 columnas y usa countas aggfunc, llenando las entradas faltantes con ceros (exactamente en este caso), vale la pena considerar usar pd.crosstab:

 >> new_df = pd.crosstab(df.ID, df.Class)
 >> new_df
Class  A  B  D  Other
ID
1      1  1  0      0
2      0  0  1      0
3      0  1  0      0
4      0  0  0      1
5      0  0  0      1
6      0  0  0      1
7      1  0  0      0
8      0  0  0      1
9      1  0  0      0

Obtiene el último valor de clase del marco de datos inicial con la agrupación por ID y la elección de la última entrada:

>> df.groupby('ID').Class.last()
ID
1        B
2        D
3        B
4    Other
5    Other
6    Other
7        A
8    Other
9        A

Entonces puedes juntarlos con concatenación:

>> new_df = pd.concat([new_df, df.groupby('ID').Class.last()], 1)
    A  B  D  Other  Class
ID
1   1  1  0      0      B
2   0  0  1      0      D
3   0  1  0      0      B
4   0  0  0      1  Other
5   0  0  0      1  Other
6   0  0  0      1  Other
7   1  0  0      0      A
8   0  0  0      1  Other
9   1  0  0      0      A

Y para obtener resultados exactamente como lo deseaba:

>> new_df = new_df.rename(columns={'Class':'LastClass'})
    A  B  D  Other LastClass
ID
1   1  1  0      0         B
2   0  0  1      0         D
3   0  1  0      0         B
4   0  0  0      1     Other
5   0  0  0      1     Other
6   0  0  0      1     Other
7   1  0  0      0         A
8   0  0  0      1     Other
9   1  0  0      0         A

Poniendo todo junto como un delineador:

>> new_df = pd.concat([pd.crosstab(df.ID, df.Class),df.groupby('ID').Class.last()],1).rename(columns={'Class':'LastClass'})

>> new_df
    A  B  D  Other LastClass
ID
1   1  1  0      0         B
2   0  0  1      0         D
3   0  1  0      0         B
4   0  0  0      1     Other
5   0  0  0      1     Other
6   0  0  0      1     Other
7   1  0  0      0         A
8   0  0  0      1     Other
9   1  0  0      0         A