Pandas: ¿cómo agrupar con contar con múltiples niveles en filas?

Tengo el siguiente dataframe

|----|----| | A | B | | a1 | b1 | | a2 | b1 | | a1 | b2 | | a2 | b3 | 

Quiero contar por B por A y obtener el siguiente resultado:

 |----|----|-------| | A | B | Count | | a1 | b1 | 1 | | | b2 | 1 | | | b3 | NaN | | a2 | b1 | 1 | | | b2 | NaN | | | b3 | 1 | 

Normalmente hago esto con df.groupby([B])[A].count() pero en este caso con un poco de tabla dinámica es confuso para mí

Gracias por adelantado.

ACTUALIZACIÓN:

df.info()

  Int64Index: 20422 entries, 180 to 96430 Data columns (total 2 columns): B 20422 non-null object A 20422 non-null object dtypes: object(2) memory usage: 478.6+ KB 

Me estoy poniendo con df.groupby([B])[A].value_counts().unstack().stack(dropna=False).reset_index(name="Count") :

 |--|----|----|-------| | | A | B | Count | |0 | a1 | b1 | 1 | |1 | a1 | b2 | 1 | |2 | a1 | b3 | NaN | |3 | a2 | b1 | 1 | |4 | a2 | b2 | NaN | |5 | a2 | b3 | 1 | 

1) Una forma sería agrupar en "A" y calcular los distintos recuentos de elementos bajo "B" usando value_counts . Luego, una fusión de unstack y stack con dropna=False para obtener el DF deseado:

 df.groupby('A')['B'].value_counts().unstack().stack(dropna=False).reset_index(name="Count") 

2) pd.crosstab también proporciona una buena alternativa si reemplazamos los elementos de conteo cero con np.NaN después del astackmiento:

 pd.crosstab(df['A'], df['B']).stack().replace({0:np.nan}).reset_index(name="Count") 

Ambos enfoques producen:

introduzca la descripción de la imagen aquí


edit1:

Para que la clave agrupada, "A" se muestre en un formato determinado (es decir, mantenga la primera aparición mientras reemplaza el rest con una cadena vacía)

 df_g = pd.crosstab(df['A'], df['B']).stack().replace({0:np.nan}).reset_index(name="Count") df_g.loc[df_g.duplicated('A'), "A"] = "" 

introduzca la descripción de la imagen aquí

edit2:

Si desea que "A" sea ​​una sola célula sana que forme parte de un DF múltiples DF :

 df.groupby('A')['B'].value_counts().unstack().stack(dropna=False ).reset_index(name="Count").set_index(['A', 'B']) 

introduzca la descripción de la imagen aquí

Podrías agrupar por ambas columnas y acceder al tamaño de cada grupo:

  df.groupby(['A', 'B']).size() 

devoluciones:

 AB a1 b1 1 b2 1 a2 b1 1 b3 1 dtype: int64 

Sin embargo, no te dará NaN para combinaciones no existentes.