Привет, Хабр! На связи команда продуктовой аналитики.
Подбор и обновление ассортимента товаров — постоянная головная боль для любого ритейлера. Это трудоемкий процесс, где каждая ошибка стоит реальных денег. В ecom.tech мы стараемся сделать его проще при помощи автоматизации, а заодно изучаем предпочтения покупателей. На этот раз мы искали, что обычно покупают в паре – так называемые комплементарные товары.
В этой статье расскажем:
с чем обычно покупают лапшу быстрого приготовления, а с чем — детское питание;
как география, время суток и другие факторы влияют на выбор покупателей;
как все эти полученные знания можно применить в ассортиментных матрицах дарксторов и бизнес-процессах ритейла.
Катмены, ассортимент дарксторов и Scrum
В Самокате около 2000 центров формирования заказов — ЦФЗ (они же дарксторы) и порядка 2500 разновидностей активных товаров. Для каждого даркстора нужна ассортиментная матрица примерно на 8000 позиций — перечень продаваемых товаров с указанием бренда, характеристиками, количеством и сроками реализации. Раз в неделю ее нужно обновлять. Представьте, какой объем информации нужно проанализировать, чтобы получить оптимальный ассортимент за один такой спринт!
Поэтому у Самоката есть большая команда категорийных менеджеров (катменов), которые день за днем формируют ассортимент исходя из знаний и опыта, логики и рыночных условий. Их глобальная цель — сделать классный ассортимент для Самоката в плане среднего чека, количества заказов и, конечно, удовлетворенности покупателей.
В первую очередь они ищут крутые новинки на рынке: ходят по конференциям, покупают аналитику и отслеживают тренды, а затем запускают новинку на нашем контрактном производстве. Дальше катмены распределяют товары по городам и дарксторам, в которых, вероятнее всего, на них будет спрос.
Возможности даркстора — первое, от чего они отталкиваются при составлении матрицы. Мы точно знаем вместимость каждой зоны хранения. Например, в стандартный холодильный ларь влезает 50 разных видов мороженого по N упаковок. Если приедет 52 или 53 вида, то часть будет лежать в термосумке, или испортится, потому что морозильник переполнен. Это риски и потеря денег.
Второй фактор — экспертность сотрудников, которые формируют стартовый набор товаров и затем его проверяют. Они десятки лет делают это под разные сети и условия. Дают возможность этому ассортименту продаваться, проводят аналитику продаж и запускают ассортиментные A/B-тесты, раскладывают товары на ABC и XYZ категории. Сопоставляют свой эталон с результатами аналитики: если, условно, получаются нужные 50 наименований мороженого, запускают в матрицу. Если не хватает, то оставшееся место заполняют новинками, которые еще не продавались в Самокате и исследуют дальше. И так по кругу: гипотезы, запуски, результаты, анализ. Все гибко и очень напоминает Scrum.
Есть федеральные категории товара с консистентным спросом и малым числом производителей, например, бытовая химия, когда от Владивостока до Калининграда все моют и стирают одним брендом моющего средства. Здесь один специалист отвечает за всю географию. А в региональных категориях, вроде кулинарии, наоборот спрос очень разный от города к городу. Тот же борщ в России готовят по-разному: в Казани и в Москве это будет разный набор продуктов от разных производителей. Поэтому в каждом регионе или даже городе работает отдельный катмен.
В общем, определяем, какой товар в каком дарксторе будем продавать, ищем возможности оптимизации размещения товаров и хотим делать клиентам наиболее релевантные предложения.
В поисках комплементарных товаров
Формирование ассортимента, это сложная работа, и мы в ecom.tech ищем способы хотя бы отчасти ее упростить. Один из путей — автоматизация поиска комплементарных товаров во всем ассортименте и в рамках отдельных дарксторов.
С учетом пространства ЦФЗ нужно сделать так, чтобы в минимально возможную площадь хранения и минимально возможный выбор на витрине уместилось то, что понадобится пользователям. А это значит, что товары должны быть максимально полезными и востребованными.
Товары-комплементы хорошо подходят под эти требования: мы знаем, какие товары триггерят продажи других, и наоборот — что почти не покупают без пары.
Кажется, это просто — чего тут искать? Ясно же, что нужно затачивать ассортимент под потребности покупателей в разных местах, и что, наверное, в бизнес-центрах лучше будет продаваться кулинария, круассаны и кофе, а в спальниках пойдет замороженная рыба, баклажки с водой и подгузники. Однако все не так просто, даже с учетом колоссального опыта и постоянной аналитики. Забегая вперед скажу, что мы нашли и ошибки, и неочевидные связи. Например, можно подумать что комплементарные товары — товары из разных категорий, типа: гель для мытья посуды + губка. На практике же оказалось, что комплементов все таки больше внутри одной категории.
Итак, мы хотели провести исследование и найти как можно больше устойчивых комплементарных связок, чтобы дать дополнительную опору категорийным менеджерам при вводе-выводе товаров и облегчить рутину.
Еще больше гипотез и потенциальных преимуществ
Если подтвердим базовую гипотезу про товары, которые драйвят продажи друг друга, то ускорим оборачиваемость и уменьшим сток. Товары не будут застаиваться, закупать будут больше, и мы операционно выиграем. Еще крутой момент – люди будут сразу в одном месте получать все, что привыкли покупать в паре.
Есть и другие варианты возможного профита:
Если среди комплементарных товаров найти какие-то стабильные связки во всех географиях и во все сезоны, то их можно поставлять сразу во все дарксторы. Можно формировать и предлагать готовые наборы. Покупателю удобно и чек выше.
Если отследить сезонные изменения, например, когда летом берут набор продуктов для окрошки с квасом, а зимой примерно из этого же готовят салат оливье, то можно более точно закупать товар.
Если увеличить ассортимент в комплементарных категориях, сократив наименее продаваемые товары можно будет подобрать оптимальную ширину линейки товаров для каждой категории и эффективнее управлять пространством в дарксторах.
В общем тема важная и перспективная, но сперва нам надо было найти товары-комплементы.
Подход и подготовка к исследованию
Для этого существует, например, поиск ассоциативных правил (Associations rules learning, ARL) — общепринятые в индустрии формулы и технологии поиска взаимосвязей или ассоциаций в айтемсетах. Кратко это звучит как «купил стол, купишь и стул» и основывается на анализе транзакций, в которых алгоритмы находят совпадения и сортируют по силе.
На старте нам нужны: город, месяц, номер заказа и список всех товаров.
Чтобы очистить данные, применяем различные фильтры. Отбираем только исполненные заказы, без отмененных и модифицированных. А еще отдельно анализируем дарксторы внутри каждой категории — спальные, деловые районы и т. д.
Затем загоняем чеки в базу данных, где выделено по одному столбику для каждого товара и отмечаем true-false, то есть, был этот товар в конкретном чеке или нет.
На этой основе считаем все нужные показатели, вроде доли товара во всех чеках, доли товара в чеках с другим товаром и так далее.
На выходе получаем таблицу с парами и несколькими метриками силы связи этих пар — насколько вероятно, что в чеке с товаром А будет еще и товар Б. В зависимости от значений метрик мы с большей или меньшей уверенностью говорим, что связь есть.
Реализация и ошибки
Классическая методика поиска комплементарных товаров предлагает анализировать каждую строку чека. Но разве важно, что в одном случае с молоком покупают батон 350г, а в другом батон в нарезке 270г?
На фоне огромного количества уникальных товаров и миллионов чеков у нас появилось ощущение, что считать все подряд будет слишком долго и не факт, что результативно. Решили заменить уникальный товар на самую мелкую категорию в иерархии и исследовать, как друг с другом дружат такие категории.
Одной из самых «сильных» пар стала: влажный корм для мелких собак + влажный корм для средних собак. Будто каждый второй второй покупатель завел себе разом болонку и бультерьера. Согласитесь, очень странно.
Стало понятно, что без погружения в айтемы с этим не разобраться. Когда прогнали весь алгоритм уже не по категориям, а по товарам, обнаружили, что в действительности друг с другом связаны два одинаковых корма с разными вкусами, с ягненком и курицей. Но тот что с ягненком был ошибочно занесен не в ту подкатегорию. Человеческий фактор, да.
Итак, пришлось, «как по учебнику» — считать частые айтемсеты. Первые пары искали методом поиска ассоциативных правил, при помощи простого самописного скрипта и проверяли результаты вручную, но с десятком миллионов ежемесячных транзакций мой код не справился — пришлось искать библиотеки, которые стабильно работают под высокой нагрузкой.
Технически поиск комплементарных товаров делится на две стадии — поиск частых айтемсетов и собственно подсчет метрик комплементарности. Остановились на опенсорсной библиотеке mlxtend.
import pandas as pd
#!pip install apyori
#!pip install mlxtend
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, fpgrowth, association_rules
С помощью алгоритма fpgrowth выбрали частые айтемсеты, а метрики считали через association_rules. Mlxtend хорошо переваривала наши объемы данных, работала довольно быстро и без сбоев. В качестве входных данных мы взяли базу продаж sales_df, которая представляет собой таблицу вида:
order_id |
product_id |
1 |
Шоколадные шарики для завтрака |
1 |
Молоко |
1 |
Яйцо |
2 |
Молоко |
3 |
Шоколадные шарики для завтрака |
3 |
Молоко |
4 |
Шоколадные шарики для завтрака |
4 |
Молоко |
5 |
Яйцо |
5 |
Молоко |
6 |
Яйцо |
6 |
Помидор |
Ее необходимо было преобразовать в формат, с которым будет работать поиск частых айтемсетов. К счастью в mlxtend есть решение для этого:
dataset = sales_df.groupby('order_id')['product_id'].apply(list).tolist()
te = TransactionEncoder()
te_ary = te.fit(dataset).transform(dataset)
df = pd.DataFrame(te_ary, columns=te.columns_)
Так наш датафрейм sales_df, полученный импортом из БД преобразуется в df, который выглядит следующим образом:
|
Шоколадные шарики для завтрака |
Молоко |
Яйцо |
0 |
TRUE |
TRUE |
TRUE |
1 |
FALSE |
TRUE |
FALSE |
2 |
TRUE |
TRUE |
FALSE |
3 |
FALSE |
TRUE |
TRUE |
4 |
FALSE |
FALSE |
TRUE |
5 |
TRUE |
TRUE |
FALSE |
Оказалось, что это одна из самых ресурсозатратных операций во всем пайплайне. Для ее оптимального выполнения можно использовать эффективные алгоритмы, такие как Apriori или FP-growth. Сравнение этих алгоритмов можно найти, например, в этой лекции.
Для поиска частых наборов из данных о потребительской корзине мы использовали FP-growth. Допустим, у нас есть следующий DataFrame:
frequent_itemsets = fpgrowth(df, min_support=0.001, use_colnames=True)
В результате получится таблица частых наборов:
support |
itemsets |
0.83 |
(Молоко) |
0.50 |
(Яйцо) |
0.50 |
(Шоколадные шарики для завтрака) |
0.33 |
(Яйцо, Молоко) |
0.50 |
(Молоко, шарики для завтрака) |
0.17 |
(Яйцо, шоколадные шарики для завтрака) |
0.17 |
(Яйцо, молоко,шоколадные шарики для завтрака) |
support здесь — это частота встречаемости данной комбинации на всем наборе данных. Например, «Молоко» присутствует в 5 из 6 транзакций, поэтому его support равен 0.83. Параметр min_support=0.001 задает порог, позволяющий отсеять непопулярные комбинации. Таким образом, будут обработаны только транзакции, которые встречаются как минимум в каждом 1000м чеке. Если увеличить min_support до например до 0.4, то мы потеряем часть комбинаций:
support |
itemsets |
0.83 |
(Молоко) |
0.50 |
(Яйцо) |
0.50 |
(Шоколадные шарики для завтрака) |
0.50 |
(Молоко, шоколадные шарики для завтрака) |
Естественно для реальных задач ставить min_support=0.4 совершенно некорректно. Обычно значение этой переменной выбирается исходя из задач исследования и технических возможностей и находится в диапазоне 0.01-0.0001.
После получения частых наборов можно вычислить различные метрики, характеризующие ассоциативные правила:
result = association_rules(frequent_itemsets, metric="confidence", min_threshold=0.4)
Здесь задается метрика confidence (уверенность в правиле) и порог min_threshold=0.4 для отсева слабых правил. На выходе получаем таблицу:
antecedents |
consequents |
antecedent support |
consequent support |
support |
confidence |
lift |
leverage |
conviction |
zhangs_metric |
(Яйцо) |
(Молоко) |
0.50 |
0.83 |
0.33 |
0.67 |
0.80 |
-0.08 |
0.50 |
-0.33 |
(Молоко) |
(Шоколадные шарики для завтрака) |
0.83 |
0.50 |
0.50 |
0.60 |
1.20 |
0.08 |
1.25 |
1.00 |
(Шоколадные шарики для завтрака) |
(Молоко) |
0.50 |
0.83 |
0.50 |
1.00 |
1.20 |
0.08 |
inf |
0.33 |
(Яйцо, Молоко) |
(Шоколадные шарики для завтрака) |
0.33 |
0.50 |
0.17 |
0.50 |
1.00 |
0.00 |
1.00 |
0.00 |
(Яйцо, шоколадные шарики для завтрака) |
(Молоко) |
0.17 |
0.83 |
0.17 |
1.00 |
1.20 |
0.03 |
inf |
0.20 |
antecedents и consequents — предпосылка и следствие ассоциативного правила;
antecedent support и consequent support — поддержка предпосылки и следствия;
support — поддержка правила (вероятность совместного появления предпосылки и следствия);
confidence — уверенность в правиле (условная вероятность следствия при наличии предпосылки); отражает, например, насколько мы уверены в том, что молоко купят вместе с Nesquik. Самая понятная из метрик ассоциативных правил; на нашем датасете: все чеки несквик содержат еще и молоко, поэтому 2/2 = 100%, мы точно уверены; при этом только половина заказов с {яйцо, молоко} содержат еще и Nesquik, поэтому тут получаем только 50%;
lift — показатель независимости предпосылки и следствия (значение больше 1 означает положительную корреляцию);
leverage — разница между наблюдаемой и ожидаемой (при независимости) частотами совместного появления; значение 0 означает независимость;
conviction — еще один показатель силы правила, альтернативный lift;
zhangs_metric — комплексная метрика, учитывающая как уверенность, так и «неожиданность» правила; этакий гибрид confidence и leverage; значения получаются от -1 до 1, где -1 означает строго негативное воздействие, 0 - отсутствие связи, а 1 - строго позитивную зависимость; оптимальный вариант, если по какой-то причине необходимо выбрать только одну метрику.
Несмотря на большое количество метрик, не все из них могут быть полезны в конкретной задаче. Некоторые из них зачастую содержат похожую информацию или конфликтуют друг с другом. Не существует универсального правила, согласно которому от какого значения метрики X товары А и B надо точно нужно ставить на одну полку. Выбор наиболее подходящих метрик часто требует экспертной оценки и ручного анализа результатом в покраской ячеек прямо в экселе и анализом графов. Этим мы и занялись.
Неочевидные вкусы покупателей
Использование такой методики позволило нам увидеть важные комплементарные пары, которые мы не нашли бы иначе. Например, больше всего сильных связей между товарами обнаружилось в сибирском регионе — там немного меньше ассортимент, соответственно меньше разнообразие внутри чека и парные покупки легче отследить. Этот принцип распространяется и на время суток — меньше всего комплементарных товаров удается выделить вечером, потому что больше заказов, больше вариативность, больше исходов и сила связи снижается. Например, по утрам видим сильные связки: печенье (сахарное/сдобное/сложное) + молоко пастеризованное. Такие комбинации алгоритм не отбирает днем или вечером, только утром. Разве никто не покупает вечером молоко с печеньем?
Конечно, это не так. На самом деле и печенье и молоко (как вместе, так и по отдельности) вечером покупают больше. Просто все остальные товары по вечерам покупают еще больше, поэтому на их фоне молоко и печенье просто не детектится алгоритмом для анализа (не попадают в список частых айтемсетов). А утром заказы с печеньем и молоком составляют большую долю от всех чеков, поэтому закономерность становится более явной. Аналогично получилось и с парой «ванилин» + «разрыхлитель», она тоже обнаруживалась только для утренней выборки, что опять же странно, ведь шарлотку все пекут вечером, правда?
Одна из самых сильных связок по всем метрикам, очень стабильный и интересный пример, который встречается везде и всегда, это куриная лапша быстрого приготовления + говяжья лапша быстрого приготовления. На самом деле стабильных комбинаций по принципу: одинаковый товар, но с разным вкусом, очень много. Все хотят разнообразия. Такие внутрикатегорийные связки крепкие, стабильные (присутствуют на протяжении всего года) и распространенные (во всех регионах).
Обычно это:
Детские каши или творожки одинакового производителя и объема, но разных вкусов.
Йогурты разных вкусов.
Творожные сырки разных вкусов (кроме Картошка + Кокос, такого сочетания почему-то нет).
Комбинации влажного кошачьего корма с разными вкусами.
Все эти комбинации мы бы пропустили, если бы пошли по изначальному плану анализа по самым мелким подкатегориям, не спускаясь на уровень айтемов.
А еще в комплементарных парах можно выделить региональные особенности: только в южных регионах и круглый год к свежим овощам покупают зелень, а в Сибири разнообразие зелени не столь важно, в основном берут лук. В частности в Москве покупают помидоры с огурцами, в Сочи постоянно к томатам берут петрушку + укроп, а в Новосибирске помидоры предпочитают с луком. А к готовому набору для окрошки почему-то всегда покупают не квас (или кефир?), а огурцы. Казалось бы, зачем? Однако смотрим на сам набор — в нем нет огурцов! Так что все логично (производителю на заметку).
Раз уж мы заговорили об окрошке — все‑таки покупатели в основном делают ее на квасе! Только на юге к товарам для окрошки (наборам или отдельным компонентам) покупают кисломолочные продукты — причем речь идет не только о кефире, используется также тан и айран.
Если покопаться в причинах, часто может оказаться, что связь между разными товарами не имеет ничего общего со вкусовыми предпочтениями клиентов. Однако, даже такие совпадения помогают понять, как улучшить процессы внутри компании.
Связи между категориями
Еще один способ получить высокоуровневый взгляд на ситуацию — это анализ межкатегорийных связей. Для этого в таблице чеков мы поменяли значение в поле product_id на соответствующую этому товару категорию (мы брали категорию самого низкого уровня, например «сухари-гренки» или «молоко пастеризованное», при этом остальной алгоритм оставили без изменений.
В результате товары, которым не хватало их собственной поддержки (support) для попадания в множество частых наборов (frequent itemsets), теперь объединяются по категориям и попадают в анализ. Это делает результирующий файл более компактным.
Визуализация межкатегорийных связей
Ситуация становится еще нагляднее, если вынести все связи на граф. Рассмотрим пример с авокадо.
В качестве исходных данных нам потребуется таблица, полученная в результате работы association_rules:
antecedents |
consequents |
zhangs_metric |
Авокадо |
Огурцы |
0.64 |
Авокадо |
Сыр творожный |
0.82 |
Авокадо |
Яйцо куриное |
0.50 |
Лососи соленые |
Авокадо |
0.86 |
Лососи соленые |
Огурцы |
0.54 |
Лососи соленые |
Сыр творожный |
0.90 |
Лососи соленые |
Яйцо куриное |
0.40 |
Лососи холодного копчения |
Авокадо |
0.86 |
Лососи холодного копчения |
Огурцы |
0.57 |
Лососи холодного копчения |
Сыр творожный |
0.89 |
Лососи холодного копчения |
Яйцо куриное |
0.40 |
Огурцы |
Яйцо куриное |
0.53 |
Салаты микс |
Авокадо |
0.82 |
Салаты микс |
Огурцы |
0.80 |
antecedents и consequents – предпосылка и следствие ассоциативного правила, а zhangs_metric – комплексная метрика, учитывающая уверенность и «неожиданность» правила.
Здесь мы предлагаем использовать zhangs_metric, так как она является достаточно универсальной метрикой для визуализации. Для построения графа использовали библиотеку got_net.
got_net = Network(height='900px', width='1500px', bgcolor='#FFFFFF', font_color='black')
got_net.toggle_physics(False)
got_data = final_compl_df_av
# Extract relevant data for the nodes and edges
sources = got_data['antecedents'].astype(str)
targets = got_data['consequents'].astype(str)
weights = got_data['zhangs_metric']
# Создаем узлы и связи
edge_data = zip(sources, targets, weights)
for e in edge_data:
src, dst, w = e
src_color = color_map[l1]
dst_color = color_map[l2]
got_net.add_node(src, title=src)
got_net.add_node(dst, title=dst)
got_net.add_edge(src, dst, value=w, color='rgba(200,200,200,0.5)')
#Сохраняем граф в файл
got_net.show('test-level4_avo.html', notebook=False)
Получаем картинку, которая наглядно показывает, что входит в авокадо-тост, и как авокадо объединяет разные элементы продуктовой корзины.
По отображаемой толщине связей можно сделать вывод, что вообще-то лосось с творожным сыром (или салат с огурцом) будут хорошо продаваться и без авокадо. Однако, если отобразить на графе вообще все товары, это будет выглядеть, мягко скажем, не очень понятно.
Это можно исправить, поработав над несколькими ключевыми моментами:
Категоризация. Раскрасить крупные категории товаров (фрукты, молочные продукты и т.д.) разными цветами для более наглядного разделения.
Отображение связей. Отображать список соседей (связанных товаров) для каждого узла на графике. На больших объемах данных не все связи легко отследить по линиям.
Читаемость. Растянуть узлы по разным областям графика для улучшения читаемости при работе с большим количеством данных.
Для реализации этих улучшений необходимо:
Выполнить джойн категории из справочника товаров к данным.
Сгенерировать справочник цветов для каждой категории. Число категорий может варьироваться, поэтому генерация цветов должна быть гибкой.
Создать маппинг категорий на цвета.
Применить маппинг при построении графа визуализации.
import colorsys
def generate_distinct_colors(n):
# Функция для создания мэппинга цветов на категории
colors = []
hues = np.linspace(0, 1, n, endpoint=False)
np.random.shuffle(hues) # Shuffle hues to ensure variety
for hue in hues:
# Convert HSV color to RGB and then to HEX, keeping saturation and value constant for readability
rgb = colorsys.hsv_to_rgb(hue, 0.7, 0.7)
hex_color = '#{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))
colors.append(hex_color)
return colors
# Подсчитываем число категорий и генерим нужное число цветов
num_categories = len(unique_levels)
distinct_colors = generate_distinct_colors(num_categories)
# Создаем мэппинг цветов
color_map = {level: color for level, color in zip(unique_levels, distinct_colors)}
got_net = Network(height='900px', width='1500px', bgcolor='#FFFFFF', font_color='black')
got_net.toggle_physics(False)
got_data = final_compl_df_av
# Extract relevant data for the nodes and edges
sources = got_data['antecedents'].astype(str)
targets = got_data['consequents'].astype(str)
weights = got_data['zhangs_metric']
color_sources = got_data['level1_x']
color_targets = got_data['level1_y']
# Create nodes and edges with the specified sizes and colors
edge_data = zip(sources, targets, weights, color_sources, color_targets)
for e in edge_data:
src, dst, w, l1, l2 = e
src_color = color_map[l1]
dst_color = color_map[l2]
got_net.add_node(src, title=src, color=src_color)
got_net.add_node(dst, title=dst, color=dst_color)
got_net.add_edge(src, dst, value=w, color='rgba(200,200,200,0.5)')
# Save and display the network graph
got_net.show('test-level4_avo.html', notebook=False)
Для отображения списка соседей можно использовать такой код:
neighbor_map = got_net.get_adj_list()
# Add neighbor data to node hover data
for node in got_net.nodes:
neighbors = neighbor_map[node['id']]
neighbor_text = '\n'.join(neighbors)
node['title'] = f"{node['id']} Neighbors:\n{neighbor_text}"
Для читаемости картинки нужно работать с настройками физики. Для данной визуализации мы использовали такие параметры:
got_net.toggle_physics(False)
node_size = 35
got_net.set_options("""
{
"nodes": {
"size": 35,
"font": {
"size": 14
}
},
"edges": {
"length": 250
},
"physics": {
"forceAtlas2Based": {
"gravitationalConstant": -100,
"centralGravity": 0.01,
"springLength": 30,
"springConstant": 0.08,
"avoidOverlap": 0.5
},
"maxVelocity": 50,
"solver": "forceAtlas2Based",
"timestep": 0.035,
"stabilization": { "iterations": 1 }
}
}
""")
Они могут не подойти для других графов, однако pyvis снабжен подробной документацией, которая поможет подобрать подходящие настройки.
Анализ межкатегорийных связей
После настройки параметров визуализации мы получаем созвездия, где разными цветами обозначены различные категории товаров. Это позволяет сразу увидеть, какие товары связаны только внутри своей категории, а какие взаимодействуют с соседями.
Например, как вы думаете, какие товары являются наиболее популярными «парами» для сливок? Кофе, молоко, сахар? Все так, если речь идет о сливках малой жирности. Однако, если начать рассматривать сливки 20–30%, здесь наиболее популярными товарами-комплементами становятся … курица и шампиньоны!
И это уже полезный инсайт для менеджера категории «Птица». Ведь если наличие гарнира и является важным условием для покупки, то возможность использовать соус, который надо готовить используя другие базовые ингредиенты уже не является такой очевидной.
Смотря на такую картинку, менеджер категории может:
Добавляя курицу в новый ЦФЗ, свериться с менеджером молочных продуктов, а будут ли там продаваться сливки высокой жирности
Следить за тем, чтобы товарыбыли в ассортименте парами (или даже тройками — с учетом шампиньонов), иначе покупатели, которые планировали приготовить курицу в сливках, могут уйти в другой магазин.
Предлагать комбо-наборы со скидкой или добавлять эти товары в контент-план сториз в приложении.
Курс прежний: ИТ для ритейла реального времени
Пока что мы поговорили только о первой, небольшой части исследования. Среди результатов есть и узкоспециальный, но от того не менее приятный итог: мы в команде продуктовой аналитики на практике проверили, что можем использовать для расчетов конкретный подход с Python‑библиотекой mlextend и последующим переносом результатов на графы.
Информация о комплементарности товаров позволяет:
Выводить новые новые товары в продажу парами, в тех дарксторах, где их еще нет.
Выдавать скидки на товар 1 с расчетом того, что он подстегнет к покупке товара 2, комплементарного для товара 1 (и имеющего хорошую маржинальность).
Формировать комбо‑наборы как целиком, так и N-1 товаров, которые и так берут друг с другом, а последний — тот, чьи продажи хотим простимулировать.
Всё это — часть работы по внедрению в работу катменов алгоритмических рекомендаций. Очередной шаг на пути к большой цели — заменить однотипные ручные операции на алгоритмы, снять с людей рутину и сделать трудозатратный аналитический процесс подбора товаров немного проще.