Нашли ошибку?
Скачать чистый
урок 2 из 4≈ 15 минупражнения: 0 из 3

Таблицы частот

pd.crosstab считает, сколько раз встречается каждое сочетание значений двух столбцов, и превращает количества в доли — по строкам, по столбцам или от всей таблицы. Как не перепутать направление долей.

Python запустится при первом выполнении

value_counts отвечает на вопрос «сколько раз встречается каждое значение одного столбца». Когда столбцов два — «сколько покупок сделано в каждом городе через каждый канал» — нужна таблица: значения одного столбца в строках, другого — в столбцах, на пересечении — количество. Её называют таблицей частот или таблицей сопряжённости.

crosstab

Функции pd.crosstab передают два столбца: первый ляжет в строки, второй — в столбцы.

[ ]Сколько строк у каждой пары «город, канал»
import pandas as pd

orders = pd.read_csv("data/shop_orders.csv")
pd.crosstab(orders["city"], orders["channel"])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
channel маркетплейс приложение сайт
city
Екатеринбург 64 99 149
Казань 87 85 169
Москва 239 260 449
Новосибирск 68 82 108
Санкт-Петербург 169 158 262

То же самое сделала бы pivot_table с aggfunc="count", но crosstab короче: ей не нужен столбец со значениями — она считает сами строки. Итоги добавляют знакомые margins и margins_name:

[ ]С итогами
pd.crosstab(orders["city"], orders["channel"], margins=True, margins_name="Всего")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
channel маркетплейс приложение сайт Всего
city
Екатеринбург 64 99 149 312
Казань 87 85 169 341
Москва 239 260 449 948
Новосибирск 68 82 108 258
Санкт-Петербург 169 158 262 589
Всего 627 684 1137 2448
[ ]Упражнение 1Категории и каналы решено

Сохраните в cat_channel таблицу частот: категории в строках, каналы в столбцах, без итогов. В tea_app сохраните число строк с чаем, купленным через приложение.

cat_channel = ...
tea_app = ...

Доли: по строкам, по столбцам, от всего

Количества трудно сравнивать: в Москве всего больше. Параметр normalize превращает их в доли — и у него три режима. От выбора режима зависит, на какой вопрос отвечает таблица.

normalize="index" — доли внутри строки: каждая строка в сумме даёт 1.

[ ]Как распределены покупки города по каналам
pd.crosstab(orders["city"], orders["channel"], normalize="index").round(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
channel маркетплейс приложение сайт
city
Екатеринбург 0.205 0.317 0.478
Казань 0.255 0.249 0.496
Москва 0.252 0.274 0.474
Новосибирск 0.264 0.318 0.419
Санкт-Петербург 0.287 0.268 0.445

Читается по строке: «в Казани 49.6 % покупок сделано на сайте».

normalize="columns" — доли внутри столбца: в сумме 1 даёт каждый столбец.

[ ]Как распределены покупки канала по городам
pd.crosstab(orders["city"], orders["channel"], normalize="columns").round(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
channel маркетплейс приложение сайт
city
Екатеринбург 0.102 0.145 0.131
Казань 0.139 0.124 0.149
Москва 0.381 0.380 0.395
Новосибирск 0.108 0.120 0.095
Санкт-Петербург 0.270 0.231 0.230

Читается по столбцу: «14.9 % покупок на сайте приходится на Казань».

normalize="all" — доли от всей таблицы: единицу дают все ячейки вместе.

[ ]Доля каждой пары во всех покупках
pd.crosstab(orders["city"], orders["channel"], normalize="all").round(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
channel маркетплейс приложение сайт
city
Екатеринбург 0.026 0.040 0.061
Казань 0.036 0.035 0.069
Москва 0.098 0.106 0.183
Новосибирск 0.028 0.033 0.044
Санкт-Петербург 0.069 0.065 0.107

«6.9 % всех покупок — Казань, сайт».

Три числа для одной и той же ячейки — 0.496, 0.149 и 0.069 — и все верные. Различается знаменатель: покупки Казани, покупки сайта, все покупки.

Вопрос
t = pd.DataFrame({"a": ["x", "x", "y", "y"], "b": ["p", "q", "p", "p"]})
print(pd.crosstab(t["a"], t["b"], normalize="index").loc["x", "p"])

Что выведет этот код?

[ ]Упражнение 2Что покупают в каждом городе решено

Сохраните в city_mix доли категорий в покупках каждого города: города в строках, категории в столбцах, каждая строка в сумме даёт 1; округление до трёх знаков. В tea_city сохраните город, где доля чая самая высокая.

city_mix = ...
tea_city = ...

Связь двух признаков

Таблица частот — самый простой способ увидеть, связаны ли два признака. Возьмём доставку: зависит ли оценка от скорости? Оставим заказы, где известны и срок, и оценка, и разобьём срок на три группы:

[ ]Скорость доставки × оценка
delivery = pd.read_csv("data/delivery_h1.csv").dropna()
delivery["speed"] = pd.cut(delivery["days"], bins=[0, 3, 5, 14], labels=["быстро", "нормально", "долго"])
delivery["rating"] = delivery["rating"].astype("int64")
pd.crosstab(delivery["speed"], delivery["rating"])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
rating 1 2 3 4 5
speed
быстро 0 3 31 92 76
нормально 0 10 67 102 20
долго 7 33 47 17 0

Количества в строках разные, поэтому сравнивать нужно доли внутри строки:

[ ]Распределение оценок при каждой скорости
pd.crosstab(delivery["speed"], delivery["rating"], normalize="index").round(2)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
rating 1 2 3 4 5
speed
быстро 0.00 0.01 0.15 0.46 0.38
нормально 0.00 0.05 0.34 0.51 0.10
долго 0.07 0.32 0.45 0.16 0.00

Картина ясная: при быстрой доставке больше 80 % оценок — четвёрки и пятёрки, при долгой пятёрок нет вовсе, а три четверти оценок — двойки и тройки.

[ ]Упражнение 3Довольные покупатели решено

Добавьте в delivery столбец happy: True, если оценка 4 или 5. Сохраните в happy_table таблицу долей: скорость в строках, happy в столбцах, доли внутри строки (не округляйте их). В fast_happy и slow_happy сохраните долю довольных при быстрой и при долгой доставке.

# добавьте в delivery столбец happy
happy_table = ...
fast_happy = ...
slow_happy = ...

83 % довольных при быстрой доставке и 16 % при долгой. Оценка сильно связана со скоростью доставки.

Пояснение с ответом появится, когда решите упражнение «Довольные покупатели».

Итоги

  • pd.crosstab(столбец для строк, столбец для столбцов) — таблица частот двух столбцов.
  • normalize="index" — доли внутри строки, "columns" — внутри столбца, "all" — от всей таблицы.
  • Читая долю, уточняйте знаменатель: что в сумме даёт единицу.
  • margins=True добавляет итоги, как у pivot_table.

Подробнее в справочнике

Нашли ошибку?