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

Маркетологи сервиса просят вас помочь им оптимизировать рекламные расходы. У вас в распоряжении есть данные о посещениях сервиса, заказах и рекламных расходах.

library(dbplyr)
library(dplyr)
library(tidyr)
library(lubridate)
library(zoo)
library(ggplot2)

setwd("~/mipt-course/pavlov")
costs_df <- read.csv(file = 'data/costs.csv', header = T, sep = ",")
visits_df  <- read.csv(file = 'data/visits_log.csv', header = T, sep = ",")
orders_df  <- read.csv(file = 'data/orders_log.csv', header = T, sep = ",")

costs_df$dt <- strptime(costs_df$dt, "%Y-%m-%d")
visits_df$Start.Ts <- strptime(visits_df$Start.Ts, "%d.%m.%Y %H:%M")
visits_df$End.Ts <- strptime(visits_df$End.Ts, "%d.%m.%Y %H:%M")
orders_df$Buy.Ts <- strptime(orders_df$Buy.Ts, "%Y-%m-%d %H:%M:%S")

colnames(costs_df) <- c("Source.Id", "Date", "Costs")

1. Сколько в среднем людей посещают сервис ежедневно, еженедельно, ежемесячно?

DAU:

dau_df <- visits_df %>% 
  mutate(date = as.Date(Start.Ts, "%Y-%m-%d")) %>% 
  group_by(date) %>% 
  summarise(DAU = n_distinct(Uid))

mean(dau_df$DAU)
## [1] 907.9918

WAU

wau_df <- visits_df %>% 
  mutate(date = as.Date(Start.Ts, "%Y-%m-%d")) %>% 
  mutate(year_week = format(date, "%Y-%W"))  %>% 
  group_by(year_week) %>% 
  summarise(WAU = n_distinct(Uid))

mean(wau_df$WAU)
## [1] 5716.245

MAU:

mau_df <- visits_df %>% 
  mutate(date = as.Date(Start.Ts, "%Y-%m-%d")) %>% 
  mutate(year_month = format(date, "%Y-%m"))  %>% 
  group_by(year_month) %>% 
  summarise(MAU = n_distinct(Uid))

mean(mau_df$MAU)
## [1] 23228.42

2. Сколько в среднем сессий приходится на одного пользователя в день?

mean_sessions_count_df <- visits_df %>%
  mutate(date = as.Date(Start.Ts, "%Y-%m-%d")) %>% 
  group_by(Uid, date) %>% 
  summarise(count = n()) %>% 
  group_by(date) %>% 
  summarise(mean_sessions_count = mean(count))
## `summarise()` has grouped output by 'Uid'. You can override using the `.groups`
## argument.
mean(mean_sessions_count_df$mean_sessions_count)
## [1] 1.07959

3. Сколько в среднем длится одна сессия?

avg_session_length_df <- visits_df %>% 
  mutate(session_length = End.Ts - Start.Ts) %>% 
  mutate(date = as.Date(Start.Ts, "%Y-%m-%d")) %>% 
  group_by(date) %>% 
  summarise(avg_session_length = mean(session_length))

mean(avg_session_length_df$avg_session_length)
## Time difference of 623.0082 secs

4. Сколько дней обычно (мода) проходит между первым посещением и первой покупкой?

get_mode_in_sec <- function(x) {
 a <- table(x)
 ans <- paste(c(as.numeric(names(a)[a == max(a)]), "sec"), collapse = " ")
 return(ans)
}

first_visits_df <- visits_df %>% 
  select(Uid, Start.Ts) %>% 
  group_by(Uid) %>% 
  summarise(first_visit.Ts = min(Start.Ts))

first_purchase_df <- orders_df %>% 
  select(Uid, Buy.Ts) %>% 
  group_by(Uid) %>% 
  summarise(first_purchase.Ts = min(Buy.Ts))

visit_first_purchase_df <- first_visits_df %>%
  inner_join(first_purchase_df, by = "Uid") %>% 
  mutate(time_to_purchase = first_purchase.Ts - first_visit.Ts)

get_mode_in_sec(visit_first_purchase_df$time_to_purchase)
## [1] "60 sec"

5. Какая доля посетителей что-то покупает?

Первое приближение. Бывают сессии, когда покупка происходит раньше начала сессии.

purchase_per_visit <- function(x, y) {
  a = length(which(!is.na(visits_orders_df$Revenue)))
  b = length(which(is.na(visits_orders_df$Revenue)))
  return(a / (a + b))
}

visits_with_orders_df <- visits_df %>%
  left_join(orders_df, by = "Uid") %>%
  filter(Buy.Ts <= End.Ts) %>%
  filter(Buy.Ts >= Start.Ts)

visits_without_orders_df <- visits_df %>%
  left_join(orders_df, by = "Uid") %>%
  filter(is.na(Buy.Ts)) 

visits_orders_df <- rbind(visits_with_orders_df, visits_without_orders_df)

purchase_per_visit(visits_orders_df)
## [1] 0.1180179

6. Сколько раз в среднем один человек покупает билеты в течение 6 месяцев с момента первой покупки?

first_purchase_df <- orders_df %>% 
  select(Uid, Buy.Ts) %>% 
  group_by(Uid) %>% 
  summarise(first_purchase.Ts = min(Buy.Ts))

six_months_in_sec <- 365.25 / 2 * 24 * 60 * 60

TBU_first_6_months_df <- first_purchase_df %>%
  left_join(orders_df, by = "Uid") %>%
  mutate(time_delta = Buy.Ts - first_purchase.Ts) %>%
  filter(time_delta < six_months_in_sec) %>%
  group_by(Uid) %>% 
  summarise(TBU_first_6_months = n())

mean(TBU_first_6_months_df$TBU_first_6_months)
## [1] 1.332804

7. Какой средний чек сервиса?

Первое приближение. Бывают сессии, когда покупка происходит раньше начала сессии.

visits_x_orders_df <- visits_df %>%
  inner_join(orders_df, by = "Uid") %>%
  filter(Buy.Ts <= End.Ts) %>%
  filter(Buy.Ts >= Start.Ts)

mean(visits_x_orders_df$Revenue)
## [1] 4.664603

8. Сколько денег обычно приносит в среднем один покупатель каждый месяц в течение первых полугода с момента первой покупки?

first_purchase_df <- orders_df %>% 
  select(Uid, Buy.Ts) %>% 
  group_by(Uid) %>% 
  summarise(first_purchase.Ts = min(Buy.Ts))

six_months_in_sec <- 365.25 / 2 * 24 * 60 * 60

first_purchase_df %>%
  inner_join(orders_df, by = "Uid") %>%
  mutate(Date = as.Date(Buy.Ts)) %>%
  mutate(Time_delta = Buy.Ts - first_purchase.Ts) %>%
  mutate(Epoch = as.integer(floor(Time_delta / 2592000)) + 1) %>%
  filter(Epoch <= 6) %>%
  group_by(Epoch) %>%
  summarise(Avg_check = mean(Revenue))
## # A tibble: 6 × 2
##   Epoch Avg_check
##   <dbl>     <dbl>
## 1     1      4.31
## 2     2      6.55
## 3     3     14.6 
## 4     4     10.6 
## 5     5      7.85
## 6     6      8.74

9. Сколько денег потратили маркетологи всего и на каждый рекламный канал?

sum(costs_df$Costs)
## [1] 329131.6
costs_per_source_df <- costs_df %>%
  group_by(Source.Id) %>%
  summarise(Costs_per_Source.Id = sum(Costs))

costs_per_source_df
## # A tibble: 7 × 2
##   Source.Id Costs_per_Source.Id
##       <int>               <dbl>
## 1         1              20833.
## 2         2              42806.
## 3         3             141322.
## 4         4              61074.
## 5         5              51757.
## 6         9               5517.
## 7        10               5822.

10. Как окупаются расходы на каждый рекламный канал?

visits_with_orders_df <- visits_df %>%
  left_join(orders_df, by = "Uid") %>%
  filter(Buy.Ts <= End.Ts) %>%
  filter(Buy.Ts >= Start.Ts)

visits_with_orders_Revenue_per_Source.Id_df <- visits_with_orders_df %>%
  group_by(Source.Id) %>%
  summarise(Revenue_per_Source.Id = sum(Revenue))

visits_with_orders_Revenue_per_Source.Id_df %>%
  left_join(costs_per_source_df) %>%
  mutate(Profit = Revenue_per_Source.Id - Costs_per_Source.Id)
## Joining, by = "Source.Id"
## # A tibble: 8 × 4
##   Source.Id Revenue_per_Source.Id Costs_per_Source.Id   Profit
##       <int>                 <dbl>               <dbl>    <dbl>
## 1         1              27949.                20833.    7115.
## 2         2              33171.                42806.   -9635.
## 3         3              29767.               141322. -111555.
## 4         4              34027.                61074.  -27047.
## 5         5              32955.                51757.  -18802.
## 6         7                  1.22                 NA       NA 
## 7         9               2688.                 5517.   -2829.
## 8        10               2979.                 5822.   -2844.

11. Какие рекламные каналы следует выключить, а в какие нужно инвестировать больше? Ответ аргументируйте.

Расходы на маркетинг в неделю:

costs_per_source_df <- costs_df %>%
  mutate(yw =  format(Date, "%Y-%W"))  %>% 
  group_by(Source.Id, yw) %>%
  summarise(Costs_per_Source.Id = sum(Costs))
## `summarise()` has grouped output by 'Source.Id'. You can override using the
## `.groups` argument.
costs_source <- costs_per_source_df %>% 
  pivot_wider(names_from = yw, values_from = Costs_per_Source.Id)

costs_source
## # A tibble: 7 × 54
## # Groups:   Source.Id [7]
##   Source.Id `2017-22` `2017-23` `2017-24` `2017-25` `2017-26` `2017-27`
##       <int>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
## 1         1     229.      300.      253.      156.      221.      239. 
## 2         2     421.      653.      359.      506.      594.      515. 
## 3         3    1250.     2480.     1401.     1537.     1447.     1965. 
## 4         4     357.      958.      763.      981.      660.      654. 
## 5         5     315.      806.      562.      529.      524.      654. 
## 6         9      42.9      71.4      66.9      57.2      60.3      67.4
## 7        10      53.7     105.       57.8      56.0      56.3      63.8
## # … with 47 more variables: `2017-28` <dbl>, `2017-29` <dbl>, `2017-30` <dbl>,
## #   `2017-31` <dbl>, `2017-32` <dbl>, `2017-33` <dbl>, `2017-34` <dbl>,
## #   `2017-35` <dbl>, `2017-36` <dbl>, `2017-37` <dbl>, `2017-38` <dbl>,
## #   `2017-39` <dbl>, `2017-40` <dbl>, `2017-41` <dbl>, `2017-42` <dbl>,
## #   `2017-43` <dbl>, `2017-44` <dbl>, `2017-45` <dbl>, `2017-46` <dbl>,
## #   `2017-47` <dbl>, `2017-48` <dbl>, `2017-49` <dbl>, `2017-50` <dbl>,
## #   `2017-51` <dbl>, `2017-52` <dbl>, `2018-01` <dbl>, `2018-02` <dbl>, …

Прибыль за неделю:

visits_with_orders_df <- visits_df %>%
  left_join(orders_df, by = "Uid") %>%
  filter(Buy.Ts <= End.Ts) %>%
  filter(Buy.Ts >= Start.Ts) %>% 
  mutate(yw =  format(Buy.Ts, "%Y-%W")) 

visits_with_orders_Revenue_per_Source.Id_df <- visits_with_orders_df %>%
  group_by(Source.Id, yw) %>%
  summarise(Revenue_per_Source.Id = sum(Revenue))
## `summarise()` has grouped output by 'Source.Id'. You can override using the
## `.groups` argument.
revenue_source <- visits_with_orders_Revenue_per_Source.Id_df %>%
  pivot_wider(names_from = yw, values_from = Revenue_per_Source.Id)

revenue_source
## # A tibble: 8 × 54
## # Groups:   Source.Id [8]
##   Source.Id `2017-22` `2017-23` `2017-24` `2017-25` `2017-26` `2017-27`
##       <int>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
## 1         1     244.      355.      205.      237.      216.      319. 
## 2         2      91.1     301.      670.       92.7     201.      177. 
## 3         3     196.      616.      229.      309.      376.      426. 
## 4         4     119.      327.      357.      304.      429.      408. 
## 5         5     169.      515.      232.      370.      588.      340. 
## 6         7      NA        NA        NA        NA        NA        NA  
## 7         9      28.8      33.3      21.9      29.9      21.0      45.4
## 8        10      20.7      84.4      34.4      23.5      12.8      19.8
## # … with 47 more variables: `2017-28` <dbl>, `2017-29` <dbl>, `2017-30` <dbl>,
## #   `2017-31` <dbl>, `2017-32` <dbl>, `2017-33` <dbl>, `2017-34` <dbl>,
## #   `2017-35` <dbl>, `2017-36` <dbl>, `2017-37` <dbl>, `2017-38` <dbl>,
## #   `2017-39` <dbl>, `2017-40` <dbl>, `2017-41` <dbl>, `2017-42` <dbl>,
## #   `2017-43` <dbl>, `2017-44` <dbl>, `2017-45` <dbl>, `2017-46` <dbl>,
## #   `2017-47` <dbl>, `2017-48` <dbl>, `2017-49` <dbl>, `2017-50` <dbl>,
## #   `2017-51` <dbl>, `2017-52` <dbl>, `2018-01` <dbl>, `2018-02` <dbl>, …

Видим, что на момент последней недели окупаются 1, 2, 10 каналы.

Для наглядности построим графики, срвним недельные затраты на маркетинг и прибыль за неделю в каждом из каналов.

compare_dd <- visits_with_orders_Revenue_per_Source.Id_df %>%
  left_join(costs_per_source_df) %>%
  gather(key="Type", value="Value", 3:4)
## Joining, by = c("Source.Id", "yw")
ggplot(data = compare_dd, 
       mapping = aes(x = yw, y = Value)) +
  geom_line(aes(colour = Type, group = Type)) +
  facet_grid(rows = vars(Source.Id), scales = "free_y")
## geom_path: Each group consists of only one observation. Do you need to adjust
## the group aesthetic?

Выводы:

1. Первый канал. Добиться большей стабильности работы первого канала, например за счет увеличения объемов продаж. Видно, что была попытка увеличения затрат на маркетинг, это привело к хорошему росту прибыли. Нужно увеличить инвестиции до 500 в неделю.

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

3. Третий канал нужно заморозить. Видно, что была попытка увеличить инвестиции в этот канал, но продажи не выросли.

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

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

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