Анализ микро- и макроцелей

Проект МастерСлух™

Автор

Александр Коршаков

Дата публикации

4 четверг апр. 2024

Обзор данных

Первые строки набора данных
Источник данных: Директ
Date Calls B24 Forms EMails Goals page2 page3 page4 min3 min6 min8 dl1 dl2 dl3 dl4 dl5 dl6 dl7 dl8 dl9 dl10
2024-01-10 2 0 2 0 4 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2024-01-11 0 1 0 0 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2024-01-15 0 0 3 0 3 1 1 NA NA NA NA 1 1 1 1 1 NA NA NA NA NA
2024-01-16 0 1 0 1 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2024-01-17 1 0 0 0 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2024-01-18 0 1 0 0 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Последние строки набора данных
Источник данных: Директ
Date Calls B24 Forms EMails Goals page2 page3 page4 min3 min6 min8 dl1 dl2 dl3 dl4 dl5 dl6 dl7 dl8 dl9 dl10
2024-03-27 2 1 0 0 3 18 12 7 10 5 3 26 22 18 15 12 10 9 8 7 5
2024-03-28 1 0 0 1 2 13 11 8 7 6 5 25 21 16 15 13 13 12 12 10 9
2024-03-29 0 1 0 0 1 9 4 2 5 3 2 19 17 10 9 6 6 2 2 2 2
2024-03-30 0 0 0 1 1 12 8 5 6 3 2 20 16 14 12 10 9 9 8 7 7
2024-03-31 NA NA NA NA NA 7 4 3 5 4 4 20 16 13 8 8 6 6 5 3 3
2024-04-01 NA NA NA NA NA 14 10 6 7 5 4 25 18 15 13 9 6 6 5 4 4


Глосарий по макро и микроцелям
Goals Calls B24 Forms EMails page2 page3 page4 min3 min6 min8 dl
Сумма главных целей Главная цель. Звонок Главная цель. Отправка сообщения через виджит Б24 Главная цель. Отправка формы с сайта Главная цель. Клик или копирование email Микроцель. Просмотрено 2 страницы Микроцель. Просмотрено 3 страницы Микроцель. Просмотрено 4 страницы Микроцель. Проведено на сайте от 3 минут Микроцель. Проведено на сайте от 6 минут Микроцель. Проведено на сайте от 8 минут Микроцель. Специальная многосоставная цель

Из-за того, что дополнительные микроцели были настроены в конце февраля при соединении данных в один набор, где ключом был столбец «Дата», образовалось множество пропущенных значений.

Рассмотрим данные на графике.

Код
# преобразуем данные в длинный форма
plot_df <- df |> 
    pivot_longer(
    cols = 2:ncol(df),
    names_to = "Name",
    values_to = "Value"
    )


# строим график
ggplot(
    plot_df, 
    aes(
      x = Date , 
      y = Value, 
      group = Name, 
      fill = Name)
    ) +
  
  geom_col()+
  
    scale_x_date(
    date_breaks = "2 day", 
    date_labels = "%d.%m"
    ) +
    
   theme_minimal() +
  
  labs(
    title = "Данные по макро- и микроцелям",
    subtitle = "Источник: Директ",
    x = NULL,
    y = "Кол-во целей",
    fill = NULL
    ) +
  
  theme(
    plot.title  = element_text(size = 12, color = "gray40"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.title = element_text(size = 8, color = "gray50"),
    axis.text = element_text(size = 7, color = "gray50"),
    axis.text.x = element_text(angle = 90),
    panel.grid = element_blank(),
    legend.position = "top",
    legend.justification = "left",
    legend.box.spacing = unit(0.1, "cm"),
    legend.text = element_text(size = 8, color = "gray50"), 
    plot.margin = ggplot2::margin(
      t = 0.3,
      r = 0.3, 
      b = 0.3,
      l = 0.3, 
      "cm"
      )
  )

Данные начинают поступать в нормальном объеме с 24.02.24. С этой даты сформируем новый набор данных.

Проверим по колонкам кол-во пропущенных значений.

Код
# проверка на NA по столбцам
na_df <- df |> 
  reframe(
    across(
      where(is.numeric), 
      list(na = function(x, ...) sum(is.na(x)))
      )
    )

# строим график для анализа NA
na_df_plt <- na_df |> 
  pivot_longer(
    cols = ends_with("na"),
    names_to = "Name",
    values_to = "Value"
  ) |> 
  ggplot(aes(x = Name, y = Value)) +
  geom_col(fill = '#ff79c6') +
  
  theme_minimal() +
  
  labs(
    title = " NA по макро- и микроцелям",
    subtitle = "Источник: Директ",
    x = NULL,
    y = "Кол-во целей"
    ) +
  
  theme(
    plot.title  = element_text(size = 12, color = "gray40"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.title = element_text(size = 8, color = "gray50"),
    axis.text = element_text(size = 8, color = "gray50"),
    axis.text.x = element_text(angle = 90),
    panel.grid = element_blank(),
    plot.margin = ggplot2::margin(
      t = 0.3,
      r = 0.3, 
      b = 0.3,
      l = 0.3, 
      "cm"
      )
  )

# создаем интерактивный график
plotly::ggplotly(na_df_plt)

Итог

  • Необходимо уменьшить объем данных.
  • Либо удалить NA значения либо заменить их на 0. Замена не совсем верное решение, однако мы и не бозон Хиггса исследуем. Для наших целей приемлемое решение.

Все манипуляции делаются для минимизации ошибок в расчетах.

Препроцессинг данных

Уменьшим объем данных.

Код
sf <- df |> 
  filter(Date >= "2024-02-24")

gt(head(sf)) |> 
  tab_header(
    title = "Обработанные данные",
    subtitle = "Источник данных: Директ"
  ) |> 
  tab_options(table.font.size = "small")
Обработанные данные
Источник данных: Директ
Date Calls B24 Forms EMails Goals page2 page3 page4 min3 min6 min8 dl1 dl2 dl3 dl4 dl5 dl6 dl7 dl8 dl9 dl10
2024-02-24 NA NA NA NA NA 21 2 NA 62 19 16 6 3 2 2 2 2 2 1 1 1
2024-02-25 1 1 0 0 2 10 2 2 35 12 8 16 9 6 5 4 4 3 3 3 3
2024-02-26 NA NA NA NA NA 9 1 NA 53 20 16 19 10 6 6 5 2 2 2 2 1
2024-02-27 NA NA NA NA NA 8 4 2 2 1 1 18 12 12 8 5 3 2 2 2 2
2024-02-28 1 0 0 0 1 9 5 3 7 4 2 27 21 15 10 7 6 4 2 2 2
2024-02-29 1 1 0 0 2 7 4 1 12 4 3 15 11 8 6 3 3 3 3 3 2


Проверяем на пропущенные значения.

Код
# проверка на NA по столбцам
na_df <- sf |> 
  reframe(
    across(
      where(is.numeric), 
      list(na = function(x, ...) sum(is.na(x)))
      )
    )

# строим график для анализа NA
na_df_plt <- na_df |> 
  pivot_longer(
    cols = ends_with("na"),
    names_to = "Name",
    values_to = "Value"
  ) |> 
  ggplot(aes(x = Name, y = Value)) +
  geom_col(fill = '#ff79c6') +
  
  theme_minimal() +
  
  labs(
    title = " NA по макро- и микроцелям",
    subtitle = "Источник: Директ",
    x = NULL,
    y = "Кол-во целей"
    ) +
  
  theme(
    plot.title  = element_text(size = 12, color = "gray40"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.title = element_text(size = 8, color = "gray50"),
    axis.text = element_text(size = 8, color = "gray50"),
    axis.text.x = element_text(angle = 90),
    panel.grid = element_blank(),
    plot.margin = ggplot2::margin(
      t = 0.3,
      r = 0.3, 
      b = 0.3,
      l = 0.3, 
      "cm"
      )
  )

# создаем интерактивный график
plotly::ggplotly(na_df_plt)


Так намного лучше.

Разбить данные по неделям и найти подходящие цели.

Код
sf <- df |> 
  filter(Date >= "2024-02-24")

micro_sf <- sf |> 
  mutate(Week = clock::date_format(Date, format = "%W")) |> 
  reframe(
    across(c(2:ncol(sf)), sum),
    .by = c(Week)
    ) |> 
  select(1,7:ncol(sf)) |> 
  slice(2:6)

micro_sf[is.na(micro_sf)] <- 0

gt(micro_sf, rowname_col = "Week") |> 
  tab_header(
    title = "Микроцели сгруппированные по неделям",
    subtitle = "Источник данных: Директ"
  ) |> 
  data_color(
    columns = 1:ncol(micro_sf),
    fn = \(v) lapply(v, \(x) if (x < 30) "darkred" else if (x > 100) "darkgreen" else "grey"),
    apply_to = "text"
  )
Микроцели сгруппированные по неделям
Источник данных: Директ
page2 page3 page4 min3 min6 min8 dl1 dl2 dl3 dl4 dl5 dl6 dl7 dl8 dl9 dl10
09 77 28 0 133 56 41 187 121 94 71 50 37 29 25 19 17
10 89 51 29 75 27 0 172 122 88 72 56 40 36 32 28 26
11 87 50 32 64 36 26 190 153 119 98 84 71 61 55 43 37
12 89 51 40 66 34 27 186 146 119 97 85 68 58 52 43 38
13 93 59 38 56 36 28 179 148 117 99 78 71 62 54 44 39
Код
nano_tbl <- micro_sf |> 
  pivot_longer(
    cols = -Week,
    names_to = "Goals",
    values_to = "Value"
  ) |> 
  pivot_wider(
    names_from = "Week",
    values_from = "Value"
  )


gt(nano_tbl, rowname_col = "Goals") |>
  tab_header(
    title = "Nanoplot",
  ) |>
cols_nanoplot(
    columns = 2:ncol(nano_tbl),
    plot_type = "bar",
    missing_vals = "zero",
    new_col_name = "Значения неделям",
    plot_height = "2.5em",
    options = nanoplot_options(
      data_bar_stroke_color = "gray",
      data_bar_fill_color = "#ff79c6")
    )|>
  tab_options(
    table.width = px(300),
    column_labels.hidden = TRUE
  ) |>
  cols_align(
    align = "center",
    columns = everything()
  )
Nanoplot
page2
93 0 77 89 87 89 93
page3
59 0 28 51 50 51 59
page4
40 0 0 29 32 40 38
min3
133 0 133 75 64 66 56
min6
56 0 56 27 36 34 36
min8
41 0 41 0 26 27 28
dl1
190 0 187 172 190 186 179
dl2
153 0 121 122 153 146 148
dl3
119 0 94 88 119 119 117
dl4
99 0 71 72 98 97 99
dl5
85 0 50 56 84 85 78
dl6
71 0 37 40 71 68 71
dl7
62 0 29 36 61 58 62
dl8
55 0 25 32 55 52 54
dl9
44 0 19 28 43 43 44
dl10
39 0 17 26 37 38 39

Итог

Особое внимание уделить микроцелям:

  • dl1
  • dl2
  • dl3

Остальные микроцели тоже могут подойти, но на выделенные обращаем внимание в первую очередь.

Микроцель «min8» может не подойти, но надо смотреть уровень корреляции и значение объясняющего коэффициента \(R^2\) в регрессионном анализе.

Корреляционный анализ

Попробуем удалить строки с NA и провести корранализ.

Код
# создаем матрицу p-value
df_psych <- cor_df |>  
  psych::corr.test(adjust = "BH")


# создаем график
ggcorrplot::ggcorrplot(
  corr = df_psych$r,
  hc.order = FALSE,
  type     = "lower",
  method   = "square",
  lab      = TRUE,
  lab_size = 3,
  p.mat    = df_psych$p,
  pch.cex = 8,
  tl.cex   = 8,
  pch.col  = "red",
  title = "Корреляционная матрица",
  outline.color = "white",
  lab_col = "gray20",
  show.diag = TRUE
  ) + 
  
  theme(
    plot.title  = element_text(size = 12, color = "gray40"),
    axis.text = element_text(color = "gray50"),
    axis.text.x = element_text(angle = 90),
    panel.grid = element_blank()
    )

Анализ без удаленных строк.

Код
# создаем матрицу p-value
df_psych <- cor_df |>  
  psych::corr.test(adjust = "BH")


# создаем график
ggcorrplot::ggcorrplot(
  corr = df_psych$r,
  hc.order = FALSE,
  type     = "lower",
  method   = "square",
  lab      = TRUE,
  lab_size = 3,
  p.mat    = df_psych$p,
  pch.cex = 8,
  tl.cex   = 8,
  pch.col  = "red",
  title = "Корреляционная матрица",
  outline.color = "white",
  lab_col = "gray20",
  show.diag = TRUE
  ) + 
  
  theme(
    plot.title  = element_text(size = 12, color = "gray40"),
    axis.text = element_text(color = "gray50"),
    axis.text.x = element_text(angle = 90),
    panel.grid = element_blank()
    )

Итог

При втором варианте обработки данных (замена NA значений на 0) убивал силу корреляции. Это следовало ожидать, так как мы добавили больше нулей в данные, поэтому этот способ не лучший для анализа, так как NA не равно 0.

Тут нужно быть внимательным к интерпретации NA. Как объяснить причину их появления? Можно ли удалить строки с NA?

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

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

В любом случаи наши корр. матрицы не отличаются критично. Поэтому берем в работу любую.

Цветовым кодированием обозначена сила корреляции и её направление. Красные крестики в квадратах означают, что мы не прошли проверку уровня \(\alpha\). Т.е. не можем принять альтернативную гипотезу о том, что данные имеют взаимосвязь между собой.

Проверку проходили с поправкой на множественные сравнения Бенджамини — Хохберга.

Проблемой множественных сравнений (multiple comparisons problem), заключается в следующем. Если мы проверяем сразу несколько гипотез, то у нас возрастает групповая вероятность ошибки первого рода (Family-wise error rate) — вероятность ошибки первого рода для хотя бы одной из множества гипотез.

Например, если вы коррелируете 10 переменных друг с другом, то вы проверяете 45 гипотез о связи. Пять процентов из этих гипотез, т.е. в среднем 2-3 гипотезы у вас будут статистически значимыми даже если никаких эффектов на самом деле нет!

Поэтому если проверяется сразу много гипотез, то необходимо применять поправки на множественные сравнения. Эти поправки позволяют контролировать групповую вероятность ошибки первого рода на желаемом уровне.

Микроцели для тестирования

Отметим, что у нас высокий уровень конверсии между суммой целей «Goals» и входящими звонками «Calls». Это означает, что большая часть суммированной цели состоит из звонков. По нашим фактическим данным это действительно так. В мае поступило больше обращений через звонки, чем через другие каналы. А это значит, с большой долей вероятности, тестируя микроцели мы будем увеличивать кол-во входящих звонков.

Статистически достоверной корреляции между главными целями Б24, Forms, Emails и микроконверсиями не обнаружено.

Тестируем следующие цели в порядки очереди:

  1. dl3
  2. dl6
  3. min6
  4. page2

Дополнительно тестируем (при отрицательных результатах)

  1. dl5
  2. dl4

Проведем регрессионый анализ с выбранными целями для окончательного утверждения на тестирование.

Возможно, стоит провести многофакторный анализ для создания специальных целей типа dl3 + page2.