Всем привет!
Меня зовут Сабина, я лидер команды исследователей данных во ВкусВилле. Мы помогаем бизнесу принимать решения, ориентируясь в том числе на данные. Сегодня я расскажу об одном таком случае. Статья будет полезна аналитикам, которые хотят перестать беспокоиться и начать использовать линейную регрессию из питоновской библиотеки stasmodels.
Бизнес-контекст
Отдел B2B прорабатывает новый сервис сборки и доставки. Важное изменение — возить будем не в пакетах, а в многоразовых контейнерах. Они представлены в нескольких вариантах по объёму, и нужно понять, какие и в каком соотношении заказать.
Кроме того, необходимо учесть правила сборки: товары категории "Кулинария", "ФРОВ" и "Заморозка" складывать отдельно от всех.
Аналитическая задача
Нужно определить, какие объемы чаще всего возят курьеры, округлить вверх до объемов предложенных контейнеров и посчитать их соотношение.
Подготовка данных
В БД есть справочник товаров, однако данными по объему для большинства из них мы не располагаем. Мешать сборщикам в дарксторе, измеряя средний объем Манго Египет, или искать его в таблице плотностей, чтобы перейти от веса к объему, конечно, никто не собирается.
Как известно, самый популярный товар среди покупателей розницы — это пакет. И клиенты B2B в этом с ними солидарны. А у пакета как раз есть объём.
Тогда нужно подготовить таблицу, в которой строка — одна покупка, а поля:
-
количество пакетов
-
количество автономных категорий (Кулинария, ФРОВ, Заморозка, Остальное)
-
и для каждой автономной категории по два поля: кол-во единиц штучных товаров и суммарный вес товаров весовых.
Остаётся посчитать, какой объём забирает на себя килограмм и штука каждой автономной категории, и решение у нас в кармане (или в пакете).
Будь у нас много заказов, в которых только одна автономная категория и только один юнит (кг или шт), то решалось бы всё запросом SQL. Но в данном случае это не так, поэтому применяем линейную регрессию.
Линейная регрессия
Формулируем теоретическую модель, от чего зависит количество пакетов:
И при помощи питоновской библиотеки 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())
После выполнения кода видим саммари:
Исключаем незначимую переменную, запускаем еще раз.
На что обращаем внимание:
-
- какую долю дисперсии зависимой переменной объяснила математическая модель. Может принимать значения [0, 1]. Важный показатель, но ориентироваться только на него не стоит. И нет жёсткого трешхолда, после какого значения модель хорошая. Даже в публикуемых научных статьях он может быть 0,3.
-
Р-value всей модели. Должно быть ниже уровня значимости (обычно 0,05)
-
Коэффициенты при независимых переменных модели должны пройти проверку на адекватность. Например, чем больше категорий (cnt_cat), тем больше пакетов — зависимость прямая, коэффициент положительный.
-
Модуль от t-статистики показывает, какой вклад в модель вносит конкретная переменная.
-
P-value переменной ниже уровня значимости (обычно уровень значимости принимают 0,05)
-
Статистика Дарбина-Уотсона отражает наличие автокорреляции. Если автокорреляции нет, равна 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 литров. Что и было рекомендовано заказчику.
Дополнительно
Новый сервис сделал доставку удобнее для клиента и курьера. Но работу сборщику мы усложнили: раньше он не задумываясь брал стандартный пакет, а теперь предстоит по составу корзины прикинуть, какой контейнер под автономную категорию взять.
Наша линейная регрессия поможет и сборщику, если ее зашить в интерфейс: полученная формула может на основании товаров в корзине подсказать, какие контейнеры взять под каждую автономную категорию.
Автор: sabinasim