python - tablas - ¿Cómo hacer una tabla cruzada pandas con porcentajes?
python and pandas (5)
Dado un marco de datos con diferentes variables categóricas, ¿cómo devuelvo una tabulación cruzada con porcentajes en lugar de frecuencias?
df = pd.DataFrame({''A'' : [''one'', ''one'', ''two'', ''three''] * 6,
''B'' : [''A'', ''B'', ''C''] * 8,
''C'' : [''foo'', ''foo'', ''foo'', ''bar'', ''bar'', ''bar''] * 4,
''D'' : np.random.randn(24),
''E'' : np.random.randn(24)})
pd.crosstab(df.A,df.B)
B A B C
A
one 4 4 4
three 2 2 2
two 2 2 2
El uso de la opción de márgenes en la tabla de referencias cruzadas para calcular los totales de filas y columnas nos permite estar lo suficientemente cerca como para pensar que debería ser posible usar un agresivo o un grupo, pero mi exiguo cerebro no puede analizarlo.
B A B C
A
one .33 .33 .33
three .33 .33 .33
two .33 .33 .33
Desde Pandas 0.18.1 en adelante, hay una opción de normalize
:
In [1]: pd.crosstab(df.A,df.B, normalize=''index'')
Out[1]:
B A B C
A
one 0.333333 0.333333 0.333333
three 0.333333 0.333333 0.333333
two 0.333333 0.333333 0.333333
Donde se puede normalizar en all
, index
(filas) o columns
.
Más detalles están disponibles en la documentación .
Otra opción es usar div lugar de aplicar:
In [11]: res = pd.crosstab(df.A, df.B)
Dividir por la suma sobre el índice:
In [12]: res.sum(axis=1)
Out[12]:
A
one 12
three 6
two 6
dtype: int64
Similar a lo anterior, necesitas hacer algo con la división de enteros (yo uso astype (''float'')):
In [13]: res.astype(''float'').div(res.sum(axis=1), axis=0)
Out[13]:
B A B C
A
one 0.333333 0.333333 0.333333
three 0.333333 0.333333 0.333333
two 0.333333 0.333333 0.333333
Podemos mostrarlo como porcentajes multiplicando por 100
:
pd.crosstab(df.A,df.B, normalize=''index'')/
.round(4)*100
B A B C
A
one 33.33 33.33 33.33
three 33.33 33.33 33.33
two 33.33 33.33 33.33
Donde he redondeado por conveniencia.
Si está buscando un porcentaje del total, puede dividir por el len del df en lugar de la suma de la fila:
pd.crosstab(df.A, df.B).apply(lambda r: r/len(df), axis=1)
pd.crosstab(df.A, df.B).apply(lambda r: r/r.sum(), axis=1)
Básicamente, solo tienes la función que hace row/row.sum()
, y usas apply
con axis=1
para aplicarlo por fila.
(Si haces esto en Python 2, deberías usar la from __future__ import division
para asegurarte de que la división siempre devuelva un flotador).