feat(generator): корзина отвязана от страницы, у товара — уровень спроса
- Зачем: - приёмка #40 нашла в торговых данных два точных равенства, каких в живом магазине не бывает: событие корзины случалось ровно у визитов со страницей /cart, а внутри такого визита в корзину уходили все открытые карточки. Привлекательность товара было нечем измерить, а аналитик читал бы эти равенства как склейку в разметке. - Что: - класть в корзину может любой визит, открывший карточку; страница /cart осталась шагом воронки, а у визита, дошедшего до неё, корзина непуста. - у товара появился уровень спроса — колонка каталога с тремя значениями и два ряда вероятностей в числах мира: намерение визита берётся из шага воронки, а не из метки покупателя, которая уже действует через неё. - уровни рассыпаны по каталогу одной колодой: одинакового расклада по категориям нет, связи с ценой нет, и то и другое сторожится тестами. - числа мира перемерены: конверсия карточки в корзину 8,62% (по уровням 13,1 / 9,2 / 5,8), кладут без открытия корзины 41,4% таких визитов, средний день 49 834 события, конверсия визита в покупку 2,43%. - спека генератора и мастер-спека приведены к новой форме каталога. - Проверка: - make lint && make typecheck && make test — 394 passed (было 383). - трафиковая половина побайтово та же: sha256 по (URL, времени, WatchID) всех просмотров за 14 канонических дней совпадает со снимком до правки.
This commit is contained in:
@@ -223,10 +223,12 @@ smoke — сравнение `system.columns` поднятого стенда с
|
||||
## 3. Каталог товаров
|
||||
|
||||
CSV в репозитории (`data/catalog/products.csv`: `sku`, `name`, `category`,
|
||||
`brand`, `price`) — **словарь ClickHouse** из файла. Тот же файл использует
|
||||
генератор — расхождений нет по построению. Даёт `dictGet` в витринах и
|
||||
разговор о политике обновления словаря. На кластере файл монтируется в обе
|
||||
ноды, словарь создаётся ON CLUSTER.
|
||||
`brand`, `price`, `demand`) — **словарь ClickHouse** из файла. Тот же файл
|
||||
использует генератор — расхождений нет по построению. Даёт `dictGet` в
|
||||
витринах и разговор о политике обновления словаря. На кластере файл
|
||||
монтируется в обе ноды, словарь создаётся ON CLUSTER. Последняя колонка —
|
||||
уровень спроса товара, заведена при исполнении #50 (спека генератора,
|
||||
раздел 9): генератор решает по ней, что уходит из карточки в корзину.
|
||||
|
||||
## 4. Сверка `purchase` против заказов
|
||||
|
||||
|
||||
@@ -754,14 +754,90 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
склонность из цены* — жёсткая связь, в которой менти найдёт ровно то,
|
||||
что мы в неё вложили, и ничего сверх.
|
||||
- **Границы правки — те же вилки раздела 5**: аудитория 6–8 тыс., средний
|
||||
день около 50 тыс. событий (сейчас 49 509 — запас невелик, и рост
|
||||
событий корзины считается против него), конверсия визита около 2%
|
||||
(сейчас 2,43%). Заказ рисуется из корзины, а корзина сузилась, поэтому
|
||||
доля брошенных позиций (число мира — 15%, замер по покупающим корзинам —
|
||||
12%) и состав заказа (1,87 позиции) перемеряются вместе с
|
||||
остальным: сохраняется не число, а свойство — заказ уже корзины и
|
||||
однопозиционных заказов около половины. Числа канонического мира после
|
||||
правки перемеряются и переписываются.
|
||||
день около 50 тыс. событий, конверсия визита около 2%. Заказ рисуется из
|
||||
корзины, а корзина сузилась, поэтому доля брошенных позиций и состав
|
||||
заказа перемеряются вместе с остальным: сохраняется не число, а
|
||||
свойство — заказ уже корзины и однопозиционных заказов около половины.
|
||||
|
||||
**Измерено после правки #50** (канонический мир, 14 дней; правый столбец —
|
||||
то же до правки). Доли по уровням и состав заказа считаются по окну снимка
|
||||
целиком, а не как среднее дневных долей: заказов в дне около 240, и на одном
|
||||
дне такая доля гуляет на несколько пунктов от одной случайности выборки.
|
||||
Трафиковая половина не сдвинулась ни на строку: набор просмотров страниц
|
||||
сверен со снимком, снятым до первой правки.
|
||||
|
||||
| величина | стало | было |
|
||||
|---|---|---|
|
||||
| дневная аудитория | 6 637–7 595, средняя 7 166 | без изменений |
|
||||
| визитов в день | 9 879 | без изменений |
|
||||
| событий в день, средних | 49 834 | 49 509 |
|
||||
| просмотров карточек в день | 25 067 | без изменений |
|
||||
| событий корзины в день | 2 161, в 1 306 визитах | 1 836, в 765 визитах |
|
||||
| из них визиты без страницы `/cart` | 541 — 41,4% | 0 |
|
||||
| визитов со страницей `/cart` без позиции | 0 | 0 |
|
||||
| просмотров карточек, дошедших до корзины | 8,62% | 7,32% |
|
||||
| конверсия карточки в корзину: магнит / обычный / залёживается | 13,1% / 9,2% / 5,8% | 7,7% у всех |
|
||||
| событий корзины вне `/cart`: магнит / обычный / залёживается | 42,0% / 20,2% / 17,1% | 0 у всех |
|
||||
| конверсия визита в покупку | 2,43% | без изменений |
|
||||
| позиций на заказ | 1,74; однопозиционных 56,4% | 1,87; 50,3% |
|
||||
| брошено позиций в покупающих корзинах | 7,25% | 12,0% |
|
||||
|
||||
Знаменатель «просмотров карточек» здесь — только просмотры страниц. При
|
||||
постановке #50 в него попали и строки событий корзины: событие садится на
|
||||
карточку и несёт её же страницу, отчего вышло 26 903 просмотра и 6,82%
|
||||
вместо 25 067 и 7,32%. Величина одна и та же, счёт разный.
|
||||
|
||||
Решено при исполнении #50 самой реализацией:
|
||||
|
||||
- **Намерение визита берётся из шага воронки, а не из метки покупателя.**
|
||||
Кладёт всякий визит, открывший карточку, но дошедший до `/cart` кладёт
|
||||
почти всё, что сравнивал, а прочий — редко. Метка покупателя в это
|
||||
решение не заходит, и не потому, что не влияет: она уже влияет — через
|
||||
воронку, где помеченному дано 18% против 6%. Взять её вторым слагаемым
|
||||
значило бы посчитать одно и то же дважды и протянуть план состава сквозь
|
||||
шов, по которому день отдаёт торговой половине только страницы и товары.
|
||||
Плата названа: у визита без намерения добавление ни с чем в человеке не
|
||||
связано — упрощение, но не ложь, потому что склонные покупать и так
|
||||
преобладают среди дошедших до `/cart`.
|
||||
- **Два ряда вероятностей, а не ряд с множителем.** Уровень спроса читается
|
||||
дважды: ряд для визита с намерением и ряд для того, кто смотрит. Один ряд
|
||||
с множителем намерения не годится: вероятности визита с намерением обязаны
|
||||
быть высокими (иначе его корзина схлопывается до одной позиции, а заказ до
|
||||
одного товара), а у потолка в 100% разница уровней съедается, и конверсия
|
||||
по уровням перестаёт различаться. Разрыв уровней внутри рядов поэтому
|
||||
разный: у смотрящего товар решает почти всё, у пришедшего покупать —
|
||||
заслонён намерением.
|
||||
- **Доля брошенных позиций опущена с 15% до 10%.** Корзина сузилась с 2,4
|
||||
позиции до 1,7, и прежние 15% чаще выносили её целиком: спасённая позиция
|
||||
возвращала заказ к одному товару, и однопозиционных заказов набиралось под
|
||||
60%. Свойство сохранено, число перемерено — ровно так, как этот блок и
|
||||
оговаривал.
|
||||
- **Ни одному уровню спроса нельзя дать нулевую вероятность.** Ноль у
|
||||
залежавшегося товара выглядел заманчиво — он дешевле всего разводил
|
||||
конверсию уровней, — но по товарам этого уровня возвращал ровно то
|
||||
равенство, ради которого правка и делалась: событие корзины случалось бы
|
||||
только внутри визита, открывшего `/cart`, без единого исключения на
|
||||
четверти каталога. Отвязка либо про весь ассортимент, либо она не отвязка.
|
||||
Разводить уровни поэтому приходится не нулём внизу второго ряда, а
|
||||
ослаблением залежавшегося в первом: пришедший покупать берёт его реже
|
||||
ходового.
|
||||
- **Уровень спроса — колонка `demand` в конце файла каталога**, значения
|
||||
словами («магнит», «обычный», «залёживается»). Раздан он одной колодой на
|
||||
весь каталог: доли по категориям расходятся сами (магнитов от четырёх до
|
||||
девяти на тридцать строк). Раскладывать колоду поровну в каждую категорию
|
||||
было бы той же неправдоподобной ровностью, какую тикет вычищал из данных.
|
||||
Связь с ценой отклонена выше, и тест сторожит её отсутствие — по четвертям
|
||||
цены и по медиане, потому что одни только края списка обманываются
|
||||
четырьмя исключениями. Хвост соседям: словарь ClickHouse над этим файлом
|
||||
ещё не построен (#37/#43) — колонку надо взять в его описание сразу, а не
|
||||
догонять правкой; манифесту нужен хеш каталога (хвост #42) тем более,
|
||||
потому что теперь в файле живёт ещё и поведение.
|
||||
- **Доля просмотров карточек, доходящих до корзины, выросла с 7,3% до 8,6%
|
||||
и подошла к потолку вилки.** Иначе не сошлось: треть кладущих визитов вне
|
||||
`/cart` требует своих позиций, а состав заказа требует, чтобы визит с
|
||||
намерением клал почти всё сравненное. Обе границы тянут число вверх, и
|
||||
опустить его — значит отпустить одну из них. Место до 9% невелико, и
|
||||
следующая правка торговой половины считается против него.
|
||||
|
||||
Остаётся открытым, за тикетами:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user