web-dev-qa-db-fra.com

Comment faire un tableau croisé pandas avec des pourcentages?

É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
45
Brian Keegan
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.)

49
BrenBarn

À 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 .

56
Harry

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é.

3
gabra

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
2
Andy Hayden