Какие товары покупают вместе: анализ корзины в pandas и NumPy
Магазин хочет добавить блок «с этим покупают». Самая частая пара не обязательно окажется полезной: популярный хлеб встречается почти в любой корзине. Посчитаем совместные покупки в pandas и NumPy и разберём, как отличить массовый товар от содержательной связи между товарами.
Одна корзина — один голос
Для начала выберем единицу наблюдения: оплаченный заказ после исключения отменённых позиций. В анализе присутствия товара две пачки кофе в одном заказе считаются одним фактом. Если задача относится к комплектам по количеству, понадобится другая модель. Здесь мы отвечаем на вопрос, какие товары клиенты включают в одну покупку.
Строим матрицу: строки — заказы, столбцы — товары, ячейка равна одному, когда товар есть в заказе. crosstab сначала считает строки, а gt(0) превращает количество в присутствие. Затем astype("int64") подготавливает числовую матрицу. Это важно: нам нужны количества совместных заказов, поэтому умножаем целые значения.
Произведение транспонированной матрицы на исходную суммирует совместные единицы по заказам. На диагонали окажется число корзин с отдельным товаром, вне диагонали — число корзин с парой. Результат симметричен, поскольку совместная покупка A и B та же самая, что B и A. Направление появится позже, когда выберем товар для рекомендации.
Что дают support, confidence и lift
Support пары — доля всех заказов, в которых есть оба товара. Это оценка масштаба: если пара встречается в одной корзине из тысячи, большой коммерческий эффект от неё ещё предстоит обосновать. Confidence A→B — доля корзин A, где также встречается B. Он отвечает, как часто покупатель A уже покупает B.
У confidence есть слабость: очень популярный B будет часто встречаться с любым A. Поэтому lift делит confidence на общую долю корзин с B. Значение 1 означает совпадение с ожидаемой частотой при независимости. Значение выше 1 указывает на положительную связь в наблюдавшихся данных. При этом высокая связь может быть следствием общей акции, готового набора или расположения товаров.
Для пары нужно видеть одновременно число заказов, support и lift. Два совместных заказа редких товаров могут дать огромный lift, который исчезнет на следующей неделе. В рабочей выборке обычно вводят минимальное число корзин и проверяют результат на соседнем периоде. Порог выбирают по объёму магазина, а не копируют произвольное число из учебного примера.
import pandas as pd
import numpy as np
items = pd.DataFrame({"order":[1,1,1,2,2,3,4],"sku":["coffee","milk","coffee","coffee","milk","coffee","bread"]})
x = pd.crosstab(items.order,items.sku).gt(0).astype("int64")
joint = x.T.to_numpy() @ x.to_numpy()
co = pd.DataFrame(joint,index=x.columns,columns=x.columns)
n = len(x)
ab = co.loc["coffee","milk"]
support = ab/n
confidence = ab/x.coffee.sum()
reverse_confidence = ab/x.milk.sum()
lift = confidence/(x.milk.sum()/n)
print({"orders":n,"pair_orders":int(ab),"support":support,"confidence":confidence,"reverse_confidence":reverse_confidence,"lift":lift})
assert ab == 2
assert np.isclose(lift,4/3)
assert reverse_confidence == 1
В четырёх заказах кофе встречается трижды, молоко дважды; оба раза молоко купили с кофе. Для предложения молока покупателю кофе confidence равен 2/3, lift — 1,33. Для обратного направления confidence равен 1: каждый покупатель молока взял кофе. Цифры описывают историю корзин, а выбор направления зависит от того, какой товар уже находится у пользователя.
Дальше превратите кандидатов в допустимые предложения: исключите товары, которые уже в корзине, проверьте наличие, ограничения категории и маржу. В наборе «кофе плюс молоко» часть спроса уже реализуется без подсказки. Высокий confidence может означать, что покупатели и так добавляют B. Эффект блока рекомендаций оценивают по дополнительным покупкам или марже в эксперименте, а не по исторической частоте пары.
На большом ассортименте плотная матрица становится дорогой по памяти: число ячеек растёт с квадратом числа товаров. Начните с ограниченного периода и часто покупаемых артикулов, затем при необходимости переходите к разреженной матрице или генерации пар внутри заказа. После этого сравните результаты на одном и том же небольшом фрагменте, чтобы оптимизация не изменила определение корзины.
Для проверки товарного состава полезен отдельный разбор сравнения корзин: Две корзины с одинаковой суммой: как сравнить состав товаров в SQL.
Проверяем пару на исходных заказах
Для пары coffee–milk откройте заказы 1 и 2. Повторная строка кофе в заказе 1 не увеличила частоту пары: результат всё ещё равен двум заказам. Такая ручная сверка помогает обнаружить типичную ошибку, когда self-join товарных строк создаёт несколько одинаковых пар внутри большой корзины.
Ещё одна полезная проверка: число совместных заказов не может превышать частоту ни одного товара. В нашем случае два совместных заказа не больше трёх корзин кофе и двух корзин молока. Если это не выполняется, нужно исправлять способ подсчёта до ранжирования рекомендаций. Диагональ матрицы также должна совпадать с числом заказов отдельного товара.
Следующая рабочая задача
В «Практической аналитике данных» вы учитесь переходить от продаж к проверяемому бизнес-вопросу и рекомендации. Курс даёт учебный магазин, маршруты pandas и SQL, проверки контрольных результатов и работу с DataLens. Итог — повторный анализ нового месяца и записка с понятным действием для бизнеса. Откройте программу курса «Практическая аналитика данных».
Продолжить на практике
- Практическая аналитика данных ↗
Превратите знания SQL или Python в аналитическую работу: проверяйте данные, считайте бизнес-метрики, стройте дашборды и объясняйте выводы на трёх прикладных кейсах.
