Всем привет!
Меня зовут Сабина, я лидер команды исследователей данных во ВкусВилле. Мы помогаем бизнесу принимать решения, ориентируясь в том числе на данные. Сегодня я расскажу об одном таком случае. Статья будет полезна аналитикам, которые хотят перестать беспокоиться и начать использовать линейную регрессию из питоновской библиотеки stasmodels.

Бизнес-контекст
Отдел B2B прорабатывает новый сервис сборки и доставки. Важное изменение — возить будем не в пакетах, а в многоразовых контейнерах. Они представлены в нескольких вариантах по объёму, и нужно понять, какие и в каком соотношении заказать. 
Кроме того, необходимо учесть правила сборки: товары категории "Кулинария", "ФРОВ" и "Заморозка" складывать отдельно от всех.

Аналитическая задача
Нужно определить, какие объемы чаще всего возят курьеры, округлить вверх до объемов предложенных контейнеров и посчитать их соотношение.

Подготовка данных
В БД есть справочник товаров, однако данными по объему для большинства из них мы не располагаем. Мешать сборщикам в дарксторе, измеряя средний объем Манго Египет, или искать его в таблице плотностей, чтобы перейти от веса к объему, конечно, никто не собирается.
Как известно, самый популярный товар среди покупателей розницы — это пакет. И клиенты B2B в этом с ними солидарны. А у пакета как раз есть объём. 

Тогда нужно подготовить таблицу, в которой строка — одна покупка, а поля: 

  • количество пакетов

  • количество автономных категорий (Кулинария, ФРОВ, Заморозка, Остальное)

  • и для каждой автономной категории по два поля: кол-во единиц штучных товаров и суммарный вес товаров весовых.

Остаётся посчитать, какой объём забирает на себя килограмм и штука каждой автономной категории, и решение у нас в кармане (или в пакете).

Будь у нас много заказов, в которых только одна автономная категория и только один юнит (кг или шт), то решалось бы всё запросом SQL. Но в данном случае это не так, поэтому применяем линейную регрессию.

Линейная регрессия
Формулируем теоретическую модель, от чего зависит количество пакетов:

Колво\_пакетов \sim \\ колво\_категорий + ФРОВ_{вес} + ФРОВ_{шт} + Кулинария_{вес} + Кулинария_{шт} \\+ Заморозка_{вес} + Заморозка_{шт} + Остальное_{вес} + Остальное_{шт}

И при помощи питоновской библиотеки statsmodels собираем математическую модель:

import statsmodels.api as sm

reg_cols = ['колво_пакетов',
            'cnt_cat',
            'ФРОВ_вес',
            'ФРОВ_шт',
            'Кулинария_вес',
            'Кулинария_шт', 
            'Заморозка_вес', 
            'Заморозка_шт',
            'Остальное_вес', 
            'Остальное_шт'
           ]

y = df['колво_пакетов'] # зависимая переменная
x = df[reg_cols].drop('колво_пакетов', axis=1) # независимые переменные

x = sm.add_constant(x) # не забываем про интерсепт

model = sm.OLS(y, x).fit() # обучение модели
print(model.summary()) 

После выполнения кода видим саммари:

У признака Кулинария_вес p_value > 0.05, значит, коэффициент не стат значимый
У признака Кулинария_вес p_value > 0.05, значит, коэффициент не стат значимый

Исключаем незначимую переменную, запускаем еще раз.

На что обращаем внимание:

  1. R^2 - какую долю дисперсии зависимой переменной объяснила математическая модель. Может принимать значения [0, 1]. Важный показатель, но ориентироваться только на него не стоит. И нет жёсткого трешхолда, после какого значения модель хорошая. Даже в публикуемых научных статьях он может быть 0,3.

  2. Р-value всей модели. Должно быть ниже уровня значимости (обычно 0,05)

  3. Коэффициенты при независимых переменных модели должны пройти проверку на адекватность. Например, чем больше категорий (cnt_cat), тем больше пакетов — зависимость прямая, коэффициент положительный.

  4. Модуль от t-статистики показывает, какой вклад в модель вносит конкретная переменная.

  5. P-value переменной ниже уровня значимости (обычно уровень значимости принимают 0,05)

  6. Статистика Дарбина-Уотсона отражает наличие автокорреляции. Если автокорреляции нет, равна 2.

Теперь то, для чего строилась модель: восстанавливаем количество пакетов на категорию, подставляя коэффициенты из модели.

df['Пакетов_ФРОВ'] = (0.4136 / df['cnt_cat'] + 0.5183) * df['is_ФРОВ'] \
                    + 0.0933 * df['ФРОВ_вес'] + 0.0595 * df['ФРОВ_шт']

df['Пакетов_Кулинария'] = (0.4136 / df['cnt_cat'] + 0.5183) * df['is_Кулинария'] \
                          + 0.0374 * df['Кулинария_шт']

df['Пакетов_Заморозка'] = (0.4136 / df['cnt_cat'] + 0.5183) * df['is_Заморозка'] \
                          + 0.1739 * df['Заморозка_вес'] + 0.0294 * df['Заморозка_шт']

df['Пакетов_Остальное'] = (0.4136 / df['cnt_cat'] + 0.5183) * df['is_Остальное'] \
                        + 0.1259 * df['Остальное_вес'] + 0.0307 * df['Остальное_шт']

На примере первой строки, Пакетов_ФРОВ:

  • Интерсепт = 0.4136, но если его прибавлять при вычислении пакетов каждой категории, получится больше, чем задумывалось. То есть нужно в каждую категорию отдать часть интерсепта (делим его на количество категорий)

  • 0.5183 – коэффициент при количестве категорий

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

  • 0.0933 и 0.0595 — коэффициенты при ФРОВ_вес и ФРОВ_шт соответственно.

Вывод
Далее пакето-категории округляем вверх до объемов многоразовых контейнеров и считаем, в каком соотношении они встречались. Получилось, что почти 100% заказов можно привозить в первых трех вариантах объёмов: 14, 32 и 46 литров. Что и было рекомендовано заказчику.

Дополнительно
Новый сервис сделал доставку удобнее для клиента и курьера. Но работу сборщику мы усложнили: раньше он не задумываясь брал стандартный пакет, а теперь предстоит по составу корзины прикинуть, какой контейнер под автономную категорию взять.  
Наша линейная регрессия поможет и сборщику, если ее зашить в интерфейс: полученная формула может на основании товаров в корзине подсказать, какие контейнеры взять под каждую автономную категорию.

Комментарии (2)


  1. SunriseIsComing
    17.10.2024 12:02

    Вот так аналитика помогает спасать планету)


  1. xin21
    17.10.2024 12:02

    Супер!!! Спасибо! Это ж теперь можно !!!!!