Modelling the Marketing Funnel #1958
TeemuSailynoja
started this conversation in
Ideas
Replies: 1 comment 1 reply
-
|
Here is my implementation of a funnel using the MaskedPrior class from #1945: class FunnelComponent:
def __init__(
self,
lower_funnel_channels: list[str],
saturation_upper_funnel,
saturation_lower_funnel,
likelihood_lower_funnel: Prior | None = None,
):
self.lower_funnel_channels = lower_funnel_channels
self.saturation_upper_funnel = saturation_upper_funnel
self.saturation_lower_funnel = saturation_lower_funnel
self.lower_funnel_likelihood = likelihood_lower_funnel
def create_data(self, mmm):
# Add lower funnel coordinate to the model
mmm.model.add_coord("lower_funnel", self.lower_funnel_channels)
def create_effect(self, mmm):
# Create likelihood for lower funnel channels if not provided,
# would be clearer to implement a default_likelihood for the class.
if not self.lower_funnel_likelihood:
self.lower_funnel_likelihood = Prior(
"Normal",
sigma=Prior("HalfNormal", sigma=1, dims="lower_funnel"),
dims=(mmm.date_column, "lower_funnel"),
)
scaled_upper_spend = pt.expand_dims(
mmm.model["channel_data"] / mmm.scalers._channel.values, -2
)
# Each upper funnel channel contributes to each lower funnel channel,
# the MaskedPrior allows also creating separate masks per lower funnel channel.
upper_to_lower = pm.Deterministic(
"upper_to_lower_spend_contribution",
self.saturation_upper_funnel.apply(scaled_upper_spend),
dims=("date", "country", "lower_funnel", "channel"),
)
# Sum contributions from all upper funnel channels to get total spend
# going into each lower funnel channel.
# This is the mu for the lower funnel likelihood.
lf_spend = self.lower_funnel_likelihood.create_likelihood_variable(
"lower_funnel_spend",
mu=upper_to_lower.sum(axis=-1),
observed=(
mmm.xarray_dataset._channel.sel(
channel=self.lower_funnel_channels
)
/ mmm.scalers._channel
).values,
)
# Apply saturation to get contribution to sales from the lower funnel channels.
lower_contribution = pm.Deterministic(
"lower_funnel_contribution",
self.saturation_lower_funnel.apply(lf_spend),
dims=("date", "country", "lower_funnel"),
)
return lower_contribution.sum(axis=-1)
def set_data(self, mmm, pymc_model, dataset_xarray):
# No extra data to set
passWith a minimal MMM mmm = MMM(
target_column="sales",
date_column="date",
dims=("country",),
channel_columns=upper_funnel + lower_funnel,
adstock=NoAdstock(l_max=1),
saturation=saturation_direct,
)
mmm.mu_effects.append(
FunnelComponent(
lower_funnel,
saturation_upper_funnel,
saturation_lower_funnel,
Prior(
"Normal",
sigma=Prior("HalfNormal", sigma=1, dims="lower_funnel"),
dims=("date", "country", "lower_funnel"),
),
)
)
mmm.build_model(X, X["sales"])where I mask away the lower funnel channels from |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Context
Model the causal relationships of the demand generating (upper funnel) and demand capturing (lower funnel) channels in an MMM.
Often, the lower funnel spend is increased by demand generated in the upper funnel channels, and throttled by budget limits set for the lower funnel channels.
@lfiaschi requested a feature like this is #1840. This has been implemented in the past using the MuEffect class, but it required quite heavy modifiimplemecations to the plotting functions as wells as tot the MultidimensionalBudgetOptimizerWrapper.
With @cetagostini 's PR introducing the MaskedPrior class (#1945), this implementation gets simpler, but there are still a couple of hurdles.
Hurdles
The BudgetOptimizer doesn't expect observed RVs in the graph for the target variable, so the marketing spend on the demand capturing channels would need to be replaced with for example a deterministic computed from the posterior.
Often, the demand marketing spend on the demand generation channels includes a maximum budget per month or week, this would then require adding a censored likelihood into the model, which offers an additional challenge, as one needs a separate component to draw posterior predictive samples from.
Now we would like to update the deterministics for channel contributions, or create a set of new ones for the indirect contribution to sales, upper funnel contribution to lower funnel spend, and total channel contribution (direct + indirect). Also the
total_media_contributiondeterministic would need to get redefined.Beta Was this translation helpful? Give feedback.
All reactions