step_adstock.Rdfunction to add saturation trasnformations to a recipe
a recipes::recipe
variables in recipe to be effected by the step
defaults to NA, set to a string to change the role of the transformed variables
defaults to .5, the amount of impact retained from the previous time period
default value will work most often, but can be set manually for use with tuning fuctions
a recipe with the new adstock step appended
suppressMessages(suppressWarnings(library(tidyverse)))
suppressMessages(suppressWarnings(library(recipes)))
suppressMessages(suppressWarnings(library(tune)))
suppressMessages(suppressWarnings(library(dials)))
suppressMessages(suppressWarnings(library(mostlytidyMMM)))
#create two datasets:
mktdata<-rbind(tibble(prod='brand',store='store1',
sales=c(100.,100.,100.,100.,100.),
tv=c(10.,100.,0.,0.,100),
search=c(0,10,20,50.,50.),
week=c(1,2,3,4,5)) ,
tibble(prod='brand',store='store2',
sales=c(10.,10,10,10,10),tv=c(0.,0,0,0,0),
search=c(0.,2,2,0,0) ,
week=c(1,2,3,4,5)) ) |>
group_by(prod,store)
mktdata2<-tibble(prod='brand',store='all',sales=100,tv=1000,search=1000,week=6) |> group_by(prod,store)
#build a recipe with two different adstock steps -- could be done in one step
rec_obj <- recipe(sales ~ ., data = mktdata) |> step_adstock(tv,retention=.5,groups=c('prod','store')) |>
step_adstock(search,retention=.5,groups=c('prod','store'),time_id='week') |>
prep(training = mktdata)
#showing off the custom print function
print(rec_obj)
#> Recipe
#>
#> Inputs:
#>
#> role #variables
#> outcome 1
#> predictor 5
#>
#> Training data contained 10 data points and no missing data.
#>
#> Operations:
#>
#> Adstock Transformation with retention 0.5 ontv [trained]
#> Adstock Transformation with retention 0.5 onsearch [trained]
#see the final transformed output
bake(rec_obj,mktdata)
#> # A tibble: 10 x 6
#> # Groups: prod, store [2]
#> prod store tv search week sales
#> <fct> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 brand store1 10 0 1 100
#> 2 brand store1 105 10 2 100
#> 3 brand store1 52.5 25 3 100
#> 4 brand store1 26.2 62.5 4 100
#> 5 brand store1 113. 81.2 5 100
#> 6 brand store2 0 0 1 10
#> 7 brand store2 0 2 2 10
#> 8 brand store2 0 3 3 10
#> 9 brand store2 0 1.5 4 10
#> 10 brand store2 0 0.75 5 10
#note what happens to store = 'all' in mktdata2:
bake(rec_obj,mktdata2)
#> Warning: There were 2 warnings in `summarise()`.
#> The first warning was:
#> i In argument: `min = min(delta, na.rm = T)`.
#> i In group 1: `prod = "brand"`, `store = "all"`.
#> Caused by warning in `min()`:
#> ! no non-missing arguments to min; returning Inf
#> i Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
#> Warning: There were 2 warnings in `summarise()`.
#> The first warning was:
#> i In argument: `min = min(delta, na.rm = T)`.
#> i In group 1: `prod = "brand"`, `store = "all"`.
#> Caused by warning in `min()`:
#> ! no non-missing arguments to min; returning Inf
#> i Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
#> # A tibble: 1 x 6
#> # Groups: prod, store [1]
#> prod store tv search week sales
#> <fct> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 brand NA 1000 1000 6 100
#check to make sure that per-variable application of the step_adstock and step-saturation do not break the grouping structure even though
#the output tibble from bake is ungrouped (irritatingly)
rec_both_steps<-recipe(sales~.,data=mktdata) |>
step_adstock(tv,retention=.1) |> step_saturation(tv,asymptote=1000,saturation_speed=.001) |>
step_adstock(search,retention=.1,time_id='week') |> prep()
bake(rec_both_steps,mktdata)
#> # A tibble: 10 x 6
#> # Groups: prod, store [2]
#> prod store tv search week sales
#> <fct> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 brand store1 9.95 0 1 100
#> 2 brand store1 96.1 10 2 100
#> 3 brand store1 10.0 21 3 100
#> 4 brand store1 1.01 52.1 4 100
#> 5 brand store1 95.3 55.2 5 100
#> 6 brand store2 0 0 1 10
#> 7 brand store2 0 2 2 10
#> 8 brand store2 0 2.2 3 10
#> 9 brand store2 0 0.22 4 10
#> 10 brand store2 0 0.022 5 10
#And adstock steps throw an error if time series isn't regular -- last point is two weeks
mktdata3<-rbind(tibble(prod='brand',store='store1',
sales=c(100.,100.,100.,100.,100.),
tv=c(10.,100.,0.,0.,100),
search=c(0,10,20,50.,50.),
week=c(1,2,3,4,6)) ,
tibble(prod='brand',store='store2',
sales=c(10.,10,10,10,10),tv=c(0.,0,0,0,0),
search=c(0.,2,2,0,0) ,
week=c(1,2,3,4,6)) ) |>
group_by(prod,store)
#not run to allow vignette to knit -- uncomment to see error
# rec1<-recipe(mktdata3) |> step_adstock(search,retention=.1,groups=c('prod','store'),
# time_id='week')|>prep()
#bake(rec1,mktdata3)