Моделирование комплекса маркетинга с ограниченными коэффициентами

Автор: Дмитрий Иванов [Команда P9X]

~8 минут чтения

Наиболее распространённый подход в моделировании маркетингового микса (MMM) — использование множественной линейной регрессии

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

Один из вопросов, который может возникнуть у маркетологов, — какой эффект оказывает каждый канал медиа на результат. Линейная регрессия оценивает коэффициенты для каждой из независимых переменных и точку пересечения, которые дают следующие указания:

  • точка пересечения показывает среднее значение результата, когда расходы на медиарекламу равны нулю;
  • коэффициенты показывают величину изменения результата при изменении независимой переменной на единицу;
  • направление изменения: коэффициенты могут быть положительными, отрицательными или нулевыми.

Однако в моделировании маркетингового микса предполагается, что каналы медиарекламы должны иметь неотрицательный эффект. То есть с каждым увеличением расходов на рекламу на единицу продажи или выручка должны увеличиваться, замедляться или, по крайней мере, оставаться нулевыми (эффект насыщения). Это означает, что результирующий коэффициент для каналов медиа должен быть положительным.

Данные

Я продолжаю использовать набор данных, предоставленный Robyn под лицензией MIT, как в моих предыдущих статьях, для практических и нетривиальных примеров, и следую тем же шагам подготовки данных, применяя Prophet для разложения трендов, сезонности и праздников.

Набор данных состоит из 208 недель выручки (с 2015–11–23 по 2019–11–11) с:

  • 5 каналами расходов на медиа: tv_S, ooh_S, print_S, VK_S, search_S;
  • 2 каналами медиа, которые также имеют информацию об экспозиции (Impression, Clicks): VK_I, search_clicks_P (не используются в этой статье);
  • Органическими каналами без расходов: newsletter;
  • Контрольными переменными: events, holidays, продажи конкурентов (competitor_sales_B).

Линейная регрессия с неограниченными коэффициентами

Давайте сначала определим наши независимые и зависимые переменные:

target = "revenue"
media_channels = ["tv_S", "ooh_S", "print_S", "VK_S", "search_S"]
organic_channels = ["newsletter"]
control_features = ["trend", "season", "holiday", "competitor_sales_B", "events"]
features = control_features + media_channels + organic_channels

Мы имеем 11 независимых переменных в общей сложности, 5 из них — каналы расходов на медиа плюс 1 органический канал, и 5 — контрольные переменные. Давайте применим классическую множественную линейную регрессию, используя пакет statsmodels, и затем проверим, даёт ли подгонка кривой с помощью функции curve_fit те же результаты для неограниченной линейной регрессии.

Линейная регрессия с ограниченными коэффициентами

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

def prepare_bounds(intercept_value, 
                       control_length, 
                       media_length, 
                       control_value, 
                       media_value):
    lower_bounds_array = []
    lower_bounds_array.append(intercept_value)
    for i in range(control_length):
        lower_bounds_array.append(control_value)
    for i in range(media_length):
        lower_bounds_array.append(media_value)
    return lower_bounds_array

Давайте подготовим ограничения:

  • Нижние границы для точки пересечения и каналов медиа равны 0, а для контрольных переменных — -infinity.
  • Верхние границы равны infinity для всех переменных.
lower_bounds_array = 
    prepare_bounds(intercept_value = 0, 
                   control_length = len(control_features), 
                   media_length = len(media_channels) + len(organic_channels), 
                   control_value = -np.inf, 
                   media_value = 0)
upper_bounds_array = 
    prepare_bounds(intercept_value = np.inf, 
                   control_length = len(control_features), 
                   media_length = len(media_channels) + len(organic_channels), 
                   control_value = np.inf, 
                   media_value = np.inf)

Регрессия Риджа с ограниченными коэффициентами с использованием R glmnet

Я решил использовать R glmnet в Python, потому что не смог найти лучшего решения. Я мог найти обёртки glmnet в Python, но из-за зависимостей Fortran я не смог скомпилировать их на своей машине с Windows. Кроме того, glmnet поставляется с двумя удобными функциями: cv.glmnet, которая выполняет перекрёстную проверку и определяет оптимальный параметр lambda, и функция glmnet, которая строит окончательную модель. Обе функции выполняют стандартизацию данных и позволяют контролировать знак коэффициентов и точку пересечения.

Установка R и RPy2

Конечно, у вас должен быть установлен R. Затем установите RPy2:

pip install rpy2

Проверьте, распознаёт ли RPy2 вашу установку R, импортировав RPy2:

from rpy2 import robjects as ro
from rpy2.robjects import pandas2ri
from rpy2.robjects.packages import importr
import rpy2.situation
base = importr("base")
glm = importr("glmnet")

Если импорт прошёл гладко, получите некоторую информацию о вашей установке R:

for row in rpy2.situation.iter_info():
    print(row)

Заключение

В некоторых ситуациях, следуя бизнес-логике, как в моделировании маркетингового микса, мы хотим заставить линейную регрессию найти решение с положительными коэффициентами. Однако не все статистические пакеты поддерживают эту функциональность, и требуется некоторый обходной путь.

Я показал два способа ограничения коэффициентов, используя функцию оптимизации нелинейных наименьших квадратов SciPy curve_fit и регрессию Риджа из пакета R glmnet. Последний подход требует обёртки кода R с помощью интерфейса RPy2 и позволяет вызывать произвольные функции R из Python.