Как сделать кросс-таблицу pandas с процентами?

Учитывая данные с различными категориальными переменными, как мне вернуть перекрестную таблицу с процентами вместо частот?

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

Использование параметра полей в кросс-таблице для вычисления итогов строк и столбцов приближает нас настолько, чтобы думать, что это должно быть возможно с помощью aggfunc или groupby, но мой скудный мозг не может это продумать.

B       A     B    C
A               
one     .33  .33  .33
three   .33  .33  .33
two     .33  .33  .33

Ответ 1

pd.crosstab(df.A, df.B).apply(lambda r: r/r.sum(), axis=1)

В основном у вас есть функция, которая выполняет row/row.sum(), и вы используете apply с axis=1 чтобы применить ее по строке.

(Если вы делаете это в Python 2, вы должны использовать from __future__ import division чтобы деление всегда возвращало float.)

Ответ 2

Начиная с Pandas 0.18.1, есть вариант 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

Где вы можете нормализовать all или all, index (строки) или columns.

Более подробная информация содержится в документации.

Ответ 3

Мы можем показать его в процентах путем умножения на 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

Где я округлен для удобства.

Ответ 4

Если вы ищете процент от общего количества, вы можете разделить на len df вместо суммы строки:

pd.crosstab(df.A, df.B).apply(lambda r: r/len(df), axis=1)

Ответ 5

Другой вариант - использовать div, а не применять:

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

Разделите на сумму по индексу:

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

Как и выше, вам нужно что-то сделать для целочисленного деления (я использую 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

Ответ 6

Нормализация индекса просто сработает. Используйте параметр normalize = "index" в pd.crosstab().