Comment faire un panda tableau croisé avec des pourcentages?

étant donné un cadre de données avec différentes variables catégoriques, comment retourner une tabulation croisée 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 marges dans crosstab pour calculer les totaux des lignes et des colonnes nous amène assez près pour penser que cela devrait être possible en utilisant un aggfunc ou groupby, mais mon cerveau Maigre ne peut pas réfléchir à travers.

B       A     B    C
A               
one     .33  .33  .33
three   .33  .33  .33
two     .33  .33  .33
31
demandé sur Brian Keegan 2014-01-21 04:57:56

5 réponses

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 que vous utilisez applyaxis=1 pour appliquer en ligne.

(Si cela en Python 2, vous devez utiliser from __future__ import division pour s'assurer que la division renvoie toujours un char.)

35
répondu BrenBarn 2014-01-21 01:25:40

à partir de Pandas 0.18.1, il y a un normalize option:

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.

41
répondu Harry 2016-11-14 16:35:18

si vous cherchez un pourcentage du total, vous pouvez diviser par le len du df au lieu de la somme de la ligne:

pd.crosstab(df.A, df.B).apply(lambda r: r/len(df), axis=1)
3
répondu howMuchCheeseIsTooMuchCheese 2014-09-26 00:58:01

une Autre option est d'utiliser div plutôt que de l'appliquer:

In [11]: res = pd.crosstab(df.A, df.B)

Diviser par la somme sur l'index:

In [12]: res.sum(axis=1)
Out[12]: 
A
one      12
three     6
two       6
dtype: int64

comme 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
répondu Andy Hayden 2014-01-21 03:05:40

on peut le montrer 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
2
répondu gabra 2018-02-02 18:51:22