Composes a carryover transform with a saturation curve to produce a single model-ready regressor. The point of the function is the order: adstock first, saturation second.
Arguments
- x
Numeric vector of media spend in time order.
- adstock
A named list of arguments for the carryover step, including a
kernelelement of"geometric"(the default),"weibull","delayed"or"none". Remaining elements are passed toadstock_geometric(),adstock_weibull()oradstock_delayed().- saturation
A named list of arguments for the saturation step, including a
typeelement of"hill","exponential","michaelis_menten","power"or"none". Remaining elements are passed to the correspondingsaturate_*()function – for"hill"that meanshalf_max, which has no default because a sensible value depends entirely on the scale of your spend.Saturation defaults to
"none", so callingmedia_transform()with only anadstockargument applies carryover alone.- by
Optional grouping vector, or data frame of grouping vectors, the same length as
x.- order
Transform order.
"adstock_then_saturate"is the convention and the default."saturate_then_adstock"is permitted but warns, because it is nearly always a mistake rather than a choice.
Details
The order is not arbitrary. Saturation represents a ceiling on what a given weight of media can achieve in a period. Applying it before adstock caps each period's spend in isolation and then lets carryover accumulate those capped values past the cap, so the composed transform is no longer bounded by the ceiling you specified. Applying it after adstock caps the total media pressure in each period, which is what a saturation curve is meant to mean.
This function will do it backwards if you insist, but it will not do it backwards quietly.
Examples
spend <- c(0, 500, 800, 300, 0, 0, 1200, 400)
# Carryover only -- saturation is off by default
round(media_transform(spend, adstock = list(decay = 0.5)), 4)
#> [1] 0.0000 250.0000 525.0000 412.5000 206.2500 103.1250 651.5625 525.7812
# Carryover and saturation together, in the conventional order
round(media_transform(
spend,
adstock = list(decay = decay_from_half_life(2)),
saturation = list(half_max = 300)
), 4)
#> [1] 0.0000 0.3280 0.5297 0.5214 0.4351 0.3526 0.6089 0.5986
# A delayed-peak kernel with an S-shaped response
round(media_transform(
spend,
adstock = list(kernel = "weibull", shape = 2, scale = 2, max_lag = 6,
type = "pdf"),
saturation = list(type = "hill", half_max = 300, shape = 2)
), 4)
#> [1] 0.0000 0.3149 0.7485 0.7445 0.4412 0.0706 0.7383 0.8131
# Grouped: geo-level panels transform within each geography
spend_panel <- c(100, 50, 25, 200, 100, 50)
geo <- rep(c("north", "south"), each = 3)
round(media_transform(spend_panel, adstock = list(decay = 0.5),
by = geo), 4)
#> [1] 50.0 50.0 37.5 100.0 100.0 75.0