Мультиканальная атрибуция в маркетинге (Часть 1: Цепи Маркова)

Клиент обычно проходит некоторый путь (последовательность различных каналов / точек доступа) до покупки в e-commerce или конверсии в других областях. В Google Analytics мы можем найти некоторые точки или каналы, которые с большей вероятностью помогают конвертировать, чем другие.

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

Google Analytics помогает в этой задаче: модель атрибуции в GA – это правило или набор правил, определяющий, как расходы и конверсии присваиваются каналам / точкам касания в пути пользователя к конверсии.

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

Что мне не нравится в подходе GA:

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

Плюсы подхода GA:

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

Если вы относительно небольшая компания, логично использовать подход GA. Но если вы увидите, что результаты атрибуции окажут значительное влияние на маркетинговые бюджеты, цены на продукцию, понимание путей клиентов и т.д. Или у вас есть необходимые данные, вы можете изучить идеи, которыми я собираюсь поделиться.

Я сосредоточился на концепции цепей Маркова для атрибуции в этой статье. Во втором посте серии мы изучим практические аспекты ее реализации.

Модель атрибуции на основе концепции цепей Маркова

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

Начнем с простого примера графа Маркова первого порядка или «без памяти», чтобы лучше понять концепцию. Он называется «без памяти», потому что вероятность достижения одного состояния зависит только от предыдущего посещения.

Например, путь клиентов содержит три уникальных канала C1, C2 и C3. Кроме того, мы должны вручную добавить три специальных состояния к каждому графику: (start), (conversion) и (null). Эти дополнительные состояния представляют собой отправную точку, покупку или конверсию и неудачное преобразование. Возможны переходы с одинаковых каналов (например, C1 -> C1), но они могут быть опущены по разным причинам.

Предположим, у нас есть три пути для клиентов:
C1 -> C2 -> C3 -> покупка
C1 -> неудача
C2 -> C3 -> неудача

Добавим дополнительные состояния (см. Столбец 2 следующей таблицы) и разделим парами (см. Столбец 3):

Таблица пользовательских путей

После этого нам нужно вычислить вероятности перехода из состояния в состояние:

Таблица пользовательских путей по состояниям

Наконец, мы можем построить модель:

Модель путей пользователя

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

Еще один эффективный способ измерения эффекта удаления – проценты. Например, канал затронул вероятность конверсии на X%.

Давайте посмотрим, как это работает в нашем упрощенном примере. Удаление канала из графика означает, что мы должны заменить его на пары каналов. В случае, если канал существует в состоянии «from», мы заменим его на NA (и затем опустим эту пару), и мы заменим канал (null), если он находится в состоянии «to». Другими словами, у нас не будет путей от выбранного канала и будут переходы из других каналов в (null), когда канал находится в части «to».

Эффект удаления для канала C1 выглядит следующим образом:

Модель эффекта удаления канала С1

Таким образом, вероятность конверсии полной модели составляет 33,3% (0,677 * 0,5 * 1 * 0,5 + 0,333 * 1 * 0,5.) Вероятность конверсии после удаления канала C1 составляет 16,7% (0,333 * 1 * 0,5.) Поэтому , эффект удаления канала C1 равен 0.5 (1 – 0.167 / 0.333). Значит, если бы у нас не было канала C1 в пути клиентов, мы потеряли бы 50% конверсий.

Эффект удаления как C2, так и C3 равен 1, потому что мы потеряем все 100% конверсии (1 – 0 / 0.333).

Кроме того, нам нужно взвесить индексы и умножить их на общее количество конверсий (1 в нашем случае):
C1: 0.5 / (0.5 + 1 + 1) = 0.2 * 1 conversion = 0.2
C2: 1 / (0.5 + 1 + 1) = 0.4 * 1 conversion = 0.4
C3: 1 / (0.5 + 1 + 1) = 0.4 * 1 conversion = 0.4

Мы распределили 1 конверсию для всех каналов.

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

Хинт: есть отличный R-пакет (ChannelAttribution), доступный на CRAN. Это действительно приятно из-за его простоты и скорости, но я нашел некоторые проблемы на ранних этапах моего расследования. Вот почему я сделал всю работу вручную. К счастью, автор пакета (Davide Altomare) ответил на мои комментарии и исправил ошибки. Поэтому я получил равные результаты моего ручного подхода и пакет из версии 1.8. Убедитесь, что установлена ​​последняя версия пакета.

Ниже приведен код R для простого примера, который мы рассмотрели выше:

Код на R

library(dplyr)
library(reshape2)
library(ggplot2)
library(ggthemes)
library(ggrepel)
library(RColorBrewer)
library(ChannelAttribution)
library(markovchain)
 
##### simple example #####
# creating a data sample
df1 <- data.frame(path = c('c1 > c2 > c3', 'c1', 'c2 > c3'), conv = c(1, 0, 0), conv_null = c(0, 1, 1))
 
# calculating the model
mod1 <- markov_model(df1,
                    var_path = 'path',
                    var_conv = 'conv',
                    var_null = 'conv_null',
                    out_more = TRUE)
 
# extracting the results of attribution
df_res1 <- mod1$result
 
# extracting a transition matrix
df_trans1 <- mod1$transition_matrix
df_trans1 <- dcast(df_trans1, channel_from ~ channel_to, value.var = 'transition_probability')
 
### plotting the Markov graph ###
df_trans <- mod1$transition_matrix
 
# adding dummies in order to plot the graph
df_dummy <- data.frame(channel_from = c('(start)', '(conversion)', '(null)'),
                       channel_to = c('(start)', '(conversion)', '(null)'),
                       transition_probability = c(0, 1, 1))
df_trans <- rbind(df_trans, df_dummy)
 
# ordering channels
df_trans$channel_from <- factor(df_trans$channel_from,
                                levels = c('(start)', '(conversion)', '(null)', 'c1', 'c2', 'c3'))
df_trans$channel_to <- factor(df_trans$channel_to,
                                levels = c('(start)', '(conversion)', '(null)', 'c1', 'c2', 'c3'))
df_trans <- dcast(df_trans, channel_from ~ channel_to, value.var = 'transition_probability')
 
# creating the markovchain object
trans_matrix <- matrix(data = as.matrix(df_trans[, -1]),
                       nrow = nrow(df_trans[, -1]), ncol = ncol(df_trans[, -1]),
                       dimnames = list(c(as.character(df_trans[, 1])), c(colnames(df_trans[, -1]))))
trans_matrix[is.na(trans_matrix)] <- 0
trans_matrix1 <- new("markovchain", transitionMatrix = trans_matrix)
 
# plotting the graph
plot(trans_matrix1, edge.arrow.size = 0.35)

[свернуть]

Мы получили визуализацию марковского графа, матрицы перехода (фрейм данных df_trans1) и результаты атрибуции, которые выглядят примерно так же, как и наши расчеты (фрейм данных df_res1):

Марковский граф переходов между каналами
Таблица весов каналов

Давайте cмоделируем набор данных, который выглядит как реальные данные о путях клиентов. Мы предполагаем, что все пути были закончены покупкой / конверсией:

Код на R

# simulating the "real" data
set.seed(354)
df2 <- data.frame(client_id = sample(c(1:1000), 5000, replace = TRUE),
                  date = sample(c(1:32), 5000, replace = TRUE),
                  channel = sample(c(0:9), 5000, replace = TRUE,
                                   prob = c(0.1, 0.15, 0.05, 0.07, 0.11, 0.07, 0.13, 0.1, 0.06, 0.16)))
df2$date <- as.Date(df2$date, origin = "2015-01-01")
df2$channel <- paste0('channel_', df2$channel)
 
# aggregating channels to the paths for each customer
df2 <- df2 %>%
        arrange(client_id, date) %>%
        group_by(client_id) %>%
        summarise(path = paste(channel, collapse = ' > '),
                  # assume that all paths were finished with conversion
                  conv = 1,
                  conv_null = 0) %>%
        ungroup()
 
# calculating the models (Markov and heuristics)
mod2 <- markov_model(df2,
                     var_path = 'path',
                     var_conv = 'conv',
                     var_null = 'conv_null',
                     out_more = TRUE)
 
# heuristic_models() function doesn't work for me, therefore I used the manual calculations
# instead of:
#h_mod2 <- heuristic_models(df2, var_path = 'path', var_conv = 'conv')
 
df_hm <- df2 %>%
        mutate(channel_name_ft = sub('>.*', '', path),
               channel_name_ft = sub(' ', '', channel_name_ft),
               channel_name_lt = sub('.*>', '', path),
               channel_name_lt = sub(' ', '', channel_name_lt))
# first-touch conversions
df_ft <- df_hm %>%
        group_by(channel_name_ft) %>%
        summarise(first_touch_conversions = sum(conv)) %>%
        ungroup()
# last-touch conversions
df_lt <- df_hm %>%
        group_by(channel_name_lt) %>%
        summarise(last_touch_conversions = sum(conv)) %>%
        ungroup()
 
h_mod2 <- merge(df_ft, df_lt, by.x = 'channel_name_ft', by.y = 'channel_name_lt')
 
# merging all models
all_models <- merge(h_mod2, mod2$result, by.x = 'channel_name_ft', by.y = 'channel_name')
colnames(all_models)[c(1, 4)] <- c('channel_name', 'attrib_model_conversions')

[свернуть]

Результатом является датафрейм (all_models):
Таблица сравнения моделей атрибуции

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

Код на R

############## visualizations ##############
# transition matrix heatmap for "real" data
df_plot_trans <- mod2$transition_matrix
 
cols <- c("#e7f0fa", "#c9e2f6", "#95cbee", "#0099dc", "#4ab04a", "#ffd73e", "#eec73a",
          "#e29421", "#e29421", "#f05336", "#ce472e")
t <- max(df_plot_trans$transition_probability)
 
ggplot(df_plot_trans, aes(y = channel_from, x = channel_to, fill = transition_probability)) +
        theme_minimal() +
        geom_tile(colour = "white", width = .9, height = .9) +
        scale_fill_gradientn(colours = cols, limits = c(0, t),
                             breaks = seq(0, t, by = t/4),
                             labels = c("0", round(t/4*1, 2), round(t/4*2, 2), round(t/4*3, 2), round(t/4*4, 2)),
                             guide = guide_colourbar(ticks = T, nbin = 50, barheight = .5, label = T, barwidth = 10)) +
        geom_text(aes(label = round(transition_probability, 2)), fontface = "bold", size = 4) +
        theme(legend.position = 'bottom',
              legend.direction = "horizontal",
              panel.grid.major = element_blank(),
              panel.grid.minor = element_blank(),
              plot.title = element_text(size = 20, face = "bold", vjust = 2, color = 'black', lineheight = 0.8),
              axis.title.x = element_text(size = 24, face = "bold"),
              axis.title.y = element_text(size = 24, face = "bold"),
              axis.text.y = element_text(size = 8, face = "bold", color = 'black'),
              axis.text.x = element_text(size = 8, angle = 90, hjust = 0.5, vjust = 0.5, face = "plain")) +
        ggtitle("Transition matrix heatmap")
 
# models comparison
all_mod_plot <- melt(all_models, id.vars = 'channel_name', variable.name = 'conv_type')
all_mod_plot$value <- round(all_mod_plot$value)
# slope chart
pal <- colorRampPalette(brewer.pal(10, "Set1"))
ggplot(all_mod_plot, aes(x = conv_type, y = value, group = channel_name)) +
        theme_solarized(base_size = 18, base_family = "", light = TRUE) +
        scale_color_manual(values = pal(10)) +
        scale_fill_manual(values = pal(10)) +
        geom_line(aes(color = channel_name), size = 2.5, alpha = 0.8) +
        geom_point(aes(color = channel_name), size = 5) +
        geom_label_repel(aes(label = paste0(channel_name, ': ', value), fill = factor(channel_name)),
                         alpha = 0.7,
                         fontface = 'bold', color = 'white', size = 5,
                         box.padding = unit(0.25, 'lines'), point.padding = unit(0.5, 'lines'),
                         max.iter = 100) +
        theme(legend.position = 'none',
              legend.title = element_text(size = 16, color = 'black'),
              legend.text = element_text(size = 16, vjust = 2, color = 'black'),
              plot.title = element_text(size = 20, face = "bold", vjust = 2, color = 'black', lineheight = 0.8),
              axis.title.x = element_text(size = 24, face = "bold"),
              axis.title.y = element_text(size = 16, face = "bold"),
              axis.text.x = element_text(size = 16, face = "bold", color = 'black'),
              axis.text.y = element_blank(),
              axis.ticks.x = element_blank(),
              axis.ticks.y = element_blank(),
              panel.border = element_blank(),
              panel.grid.major = element_line(colour = "grey", linetype = "dotted"),
              panel.grid.minor = element_blank(),
              strip.text = element_text(size = 16, hjust = 0.5, vjust = 0.5, face = "bold", color = 'black'),
              strip.background = element_rect(fill = "#f0b35f")) +
        labs(x = 'Model', y = 'Conversions') +
        ggtitle('Models comparison') +
        guides(colour = guide_legend(override.aes = list(size = 4)))

[свернуть]

Мы получили тепловую карту матрицы переходов:

Тепловая карта матрицы переходов

Сравнение моделей показывает существенные отличия от существующих эвристик, таких как «первый клик» и «последний клик», и альтернативный подход к атрибуции:

Сравнение моделей атрибуции

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

  • определить ретроспективный период анализа,
  • управлять несколькими конверсиями за отчетный период,
  • иметь дело с путями без покупки,
  • заменить «прямой» (или любой другой неизвестный) канал непрямым или известным,
  • рассчитывать как конверсии, так и доходы,
  • и т.д.

Полезные ссылки (англ, pdf):

Три очерка по анализу и управлению потребительским поведением в Интернете
Анализ эффективности интернет-рекламы с использованием моделирования атрибуции
Аналогичный пост на Lunametrics

Автор: Сергей Брыль, английский оригинал тут.

Добавить комментарий

Ваш e-mail не будет опубликован.