Étant donné une trame de données avec différentes variables catégorielles, comment puis-je retourner un tableau croisé avec des pourcentages au lieu de fréquences?
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
L'utilisation de l'option de marges dans le tableau croisé pour calculer les totaux des lignes et des colonnes nous rapproche suffisamment pour penser qu'il devrait être possible d'utiliser un aggfunc ou un groupby, mais mon maigre cerveau ne peut pas y penser.
B A B C
A
one .33 .33 .33
three .33 .33 .33
two .33 .33 .33
pd.crosstab(df.A, df.B).apply(lambda r: r/r.sum(), axis=1)
Fondamentalement, vous avez juste la fonction qui fait row/row.sum()
, et vous utilisez apply
avec axis=1
Pour l'appliquer par ligne.
(Si vous faites cela dans Python 2, vous devez utiliser from __future__ import division
Pour vous assurer que la division retourne toujours un flottant.)
À partir de Pandas 0.18.1 et plus, il y a une option 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
Où vous pouvez normaliser sur all
, index
(lignes) ou columns
.
Plus de détails sont disponibles dans la documentation .
Nous pouvons l'afficher sous forme de pourcentages en multipliant par 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
Où j'ai arrondi pour plus de commodité.
Si vous recherchez un pourcentage du total, vous pouvez diviser par le len du df au lieu de la somme des lignes:
pd.crosstab(df.A, df.B).apply(lambda r: r/len(df), axis=1)
Une autre option consiste à utiliser div plutôt que d'appliquer:
In [11]: res = pd.crosstab(df.A, df.B)
Divisez par la somme sur l'indice:
In [12]: res.sum(axis=1)
Out[12]:
A
one 12
three 6
two 6
dtype: int64
Similaire à ci-dessus, vous devez faire quelque chose à propos de la division entière (j'utilise 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