Saturation or Fatigue? Your MMM Cannot Tell the Difference
A channel's contribution per dollar has been sliding for two years, and there are exactly two stories. The first is diminishing returns: the response curve is concave, you have climbed onto its flat part, and the fix is to move dollars somewhere steeper. The second is declining effectiveness. Here the response stays roughly linear in spend while the coefficient itself fades (worn creative, a maturing platform, an auction that has turned against you), and the fix is to repair the channel or wind it down. Different diagnoses, different levers, and one of them is wrong. What makes the choice hard is that both models fit the same weekly series to within a thousandth of R², their predictive means never separate by more than half a residual standard deviation in any week, and they disagree by a factor of 2.3 about the single number your budget optimizer consumes. Worse, the mechanism that makes them indistinguishable is one you installed deliberately. Adstock is a first-order autoregressive filter. Running spend through it manufactures precisely the autocorrelation under which a nonlinear response and a drifting linear one become the same function. The more carryover you model, the less identified the saturation curve you then optimize against.
Two Stories, One Series
Write the two models down side by side, because their similarity is the whole argument. Both explain the same weekly KPI with the same baseline (intercept, trend, annual seasonality) and the same adstocked media variable \( a_t \). They differ in one place only:
$$ \text{A (static nonlinear):}\quad c_t = \beta\, S(a_t), \qquad\qquad \text{B (linear dynamic):}\quad c_t = \beta_t\, a_t . $$Model A is the standard MMM: a fixed coefficient in front of a concave saturation function \( S \). Model B is the time-varying-coefficient MMM: no curvature at all, but effectiveness \( \beta_t \) drifts smoothly across the window. Family A is what every commercial MMM ships by default, and family B is what you reach for when a client says "our TV isn't working like it used to." Both are respectable, both are in the literature, and almost nobody fits them against each other.
Each side is respectable, which is how practitioners came to hold two incompatible explanations at once without a rule for choosing between them. Model A in its Bayesian form is Jin, Wang, Sun, Chan and Koehler (2017), the paper that put geometric and delayed adstock next to a Hill saturation curve, and the ancestor of essentially every Bayesian MMM built since. Model B in its Bayesian form is Ng, Wang and Dai (2021), a time-varying-coefficient model developed to improve marketing mix modeling at Uber, in which the coefficient at each week is a weighted blend of local latent variables. Both are now in shipping software, and they are not even the only two options. This framework exposes drifting effectiveness per channel. PyMC-Marketing documents a time-varying media multiplier alongside a time-varying intercept. Google's Meridian takes a third route and spends its temporal flexibility on the baseline through knots while holding the media coefficients fixed. Three different places to put the same degrees of freedom, no guidance in any of them about which is right for your data, and in practice the choice is made by habit.
The figure below simulates a three-year weekly panel where one of the two stories is true by construction, fits both families, and prints what each would tell the budget meeting. The toggle switches which story is the truth. In neither direction does the fit reveal it.
Same fit, opposite prescription
156 weeks, one channel, geometric adstock with retention 0.70, realistic noise (R² near 0.87). One story is true by construction, and the button switches which. Both families are then fit to the same simulated series. The top panel shows the two predictive means over the data, and the bottom shows what each model answers to three questions a budget meeting actually asks, with the ground truth marked as a diamond. Drag the spend trend to see the fabricated story change direction.
At the defaults (truth = diminishing returns, spend up 40%), the two fits differ by 0.001 in R² and their predictive means never separate by more than half a residual standard deviation. Yet the drift model reports that the channel lost 57% of its effectiveness, which did not happen. Flip the toggle and the failure reverses: on a world with no saturation whatsoever, the static model invents a curve steep enough to claim your marginal dollar is worth half your average dollar, and promises a 16% efficiency gain from a 30% budget cut that would in fact change efficiency by nothing.
Read the bottom panel carefully, because the disagreement is not a matter of degree. The first question (what is the marginal return on the next dollar, as a fraction of the average return on the dollars already spent) is the number a budget optimizer runs on. Model A says 43%. Model B says 100%, necessarily, because a linear response has marginal equal to average at every level. That is a 2.3× disagreement about the quantity that determines whether this channel gets the next increment of budget. The second question asks whether cutting spend restores efficiency. Model A says a 30% cut raises efficiency 20%, because you slide back down the concave curve. Model B says a 30% cut changes efficiency by exactly zero, because level is irrelevant when the response is linear. The third asks whether the channel is wearing out: model A says no by construction, model B says it has lost more than half its power. One model is telling you to re-level the budget and leave the channel alone. The other is telling you the level is fine and the channel is dying.
And they cannot both be right, because in this simulation one of them is looking at a world the other has never seen.
This Is Not the Ridge You Know
Measurement teams have an immediate and wrong reaction to that figure: yes, we know, adstock and saturation trade off. They do, and that trade-off has its own post. Carryover and shape effects in Bayesian MMM shows two different (half-saturation, slope, coefficient) triples tracing the same curve inside the observed spend range, while the adstock parameters themselves recover fairly well. This framework's own adstock module extends the ridge to the decay: its docstring says the decay shape, the saturation strength, and the coefficient "trade off against one another… This is inherent to additive MMM, not a bug."
That is a different failure, and confusing the two is how this one stays invisible. Within-family weak identification announces itself. The posterior for \( \alpha \) is wide, the marginal for the saturation parameter is wide, the joint is a banana, and the pair-plot has a diagonal smear that any careful analyst notices. The estimates are unstable, but the uncertainty is honest. The model tells you it does not know.
What happens between families is the opposite. Inside model B, given the specification, \( \beta_t \) is perfectly well identified: there is no ridge, the chains mix, the intervals are tight. Inside model A, given the specification, the saturation parameter is identified too. Each posterior is sharp. They are sharp about incompatible things, and nothing inside either fit is aware the other exists. The failure mode here is a narrow posterior in the wrong model class, which is worse in the only way that matters: a wide interval invites caution, and a tight interval invites action.
Definition: conflation of model families
Two model families are observationally equivalent over a dataset when, for any member of one family, there exists a member of the other producing (near-)identical likelihoods on that data. No amount of data of the same kind separates them. No diagnostic computed within either family detects the problem, because within each family everything is well-behaved. The two families are distinguished only where they make different predictions, which for saturation versus drift means under a spend schedule the data has never contained. This is a statement about the design, not about the estimator, which is why the resolution is an experiment and not a better sampler.
Dew, Padilla and Shchetkina (2024) established the general result and named the trigger. Their abstract is worth quoting exactly, because both halves matter: they show that "nonlinear and time-varying effects are often not identifiable from standard marketing mix data: while certain data patterns may be suggestive of nonlinear effects, such patterns may also emerge under simpler models that incorporate dynamics in marketing effectiveness," and that "conflating the two types of effects is especially likely in the presence of autocorrelated marketing variables, which are common in practice, especially given the widespread use of stock variables to capture long-run effects of advertising." That last clause is the uncomfortable part, and the rest of this post is about what it implies for a transform nearly every MMM applies as a matter of course.
What the Drift Actually Estimates
The reason the two families collapse into each other is a single line of algebra, and it also explains why the fabricated story changes direction when you drag the trend slider. Set the two contributions equal and solve for the drifting coefficient:
$$ \beta_t\, a_t \;=\; \beta\, S(a_t) \qquad\Longrightarrow\qquad \beta_t \;=\; \beta\,\frac{S(a_t)}{a_t}. $$The quantity \( S(a)/a \) is the secant slope of the response curve from the origin to the operating point, and for any concave \( S \) with \( S(0)=0 \) it falls strictly as \( a \) rises. So the time-varying-coefficient model estimates productivity rather than effectiveness. It recovers the average productivity of a dollar along the true concave curve, evaluated wherever spend happened to be that week, and then plotted in time order. The "trajectory" is the response curve, re-indexed by the calendar.
Everything follows from that. A channel being scaled up walks rightward along the curve, its secant slope falls, and the drift model reports fatigue. A channel being wound down walks leftward, its secant slope rises, and the same model reports that the channel is getting better, which is what the figure above shows when you drag the trend to −60%. The direction of the fabricated story is set by the direction of the media plan, not by anything about the media. Nobody in the room will read it that way. They will read a downward-sloping effectiveness line next to a chart of rising spend and conclude the two are related, which they are, in the exact opposite of the causal direction assumed.
The symmetric failure is at least as common and gets far less attention. Take a world with no saturation at all (a genuinely linear channel whose coefficient is fading) and fit the standard static MMM to it. The model has no way to represent the fade, so it spends its only flexibility on the curvature parameter. It invents a saturation curve whose secant slope declines at the rate the true effectiveness declined. In the simulation above that invented curve is steep enough to report a marginal-to-average ratio of 0.52 in a world where the true ratio is exactly 1.00, and to promise a 16% efficiency gain from a budget cut that would deliver none. The client is told they are over-invested when they are actually under-creative, and the resulting cut looks like it worked, because efficiency per dollar rises mechanically whenever a fading channel shrinks.
Deep diveWhen exactly can the drift track the curve?
The identity \( \beta_t = \beta\,S(a_t)/a_t \) holds pointwise for any spend path, so on its face a time-varying coefficient can always reproduce a static nonlinear model exactly. What stops it is smoothness. Every practical implementation restricts \( \beta_t \) to a smooth class: a Gaussian random walk with a small innovation scale, a spline over knots, a Gaussian process with a long length-scale. Call that class \( \mathcal{B} \). The conflation condition is then simply
$$ t \;\mapsto\; \frac{S(a_t)}{a_t} \;\in\; \mathcal{B}. $$If the adstocked regressor \( a_t \) is itself a smooth function of time, so is any fixed function of it, and the condition holds. If \( a_t \) jumps week to week, then \( S(a_t)/a_t \) jumps week to week, and no smooth coefficient can follow it. The families separate. Autocorrelation in the media regressor is exactly the property that makes a nonlinearity in spend look like a smooth trend in time.
Two consequences follow that are easy to miss. First, the aliasing is a property of the regressor's time structure, not of its range: a channel that visits a wide range of spend levels slowly is just as conflated as one that never moves, because slowness is the whole problem. Second, the same argument covers a third family. A flexible time-varying baseline (a knot-based intercept, a Hilbert-space GP trend) is also a smooth function of time, so it competes for the same variation. Saturation, drifting media coefficients, and a wiggly baseline are three ways to spend the same degrees of freedom, and adding flexibility to any one of them takes identification away from the other two.
Attribution note: the identification result and the autocorrelation condition are established by Dew, Padilla and Shchetkina (2024) through theory and simulation with a Bayesian nonparametric response model. The secant-slope derivation and the smoothness-class framing above are my own compact restatement of the mechanism, not results quoted from that paper.
Adstock Is the Autocorrelation Generator
The transform itself is the culprit, and the argument takes two lines. Geometric adstock with retention \( \alpha \) and an infinite kernel is, written as a recursion,
$$ a_t \;=\; \sum_{k \ge 0} \alpha^k x_{t-k} \;=\; \alpha\, a_{t-1} + x_t , $$which is the definition of an AR(1) process driven by \( x_t \). If weekly spend were white noise, the adstocked regressor would have lag-1 autocorrelation of exactly \( \alpha \). That is the recursion itself, not an approximation and not an asymptotic result. Simulating twenty thousand weeks of white-noise spend and measuring the autocorrelation of the adstocked series returns 0.312, 0.511, 0.708, 0.806 and 0.903 for retentions of 0.3, 0.5, 0.7, 0.8 and 0.9. The identity, to sampling error.
Normalizing the kernel to sum to one, which most implementations do, rescales the series and leaves the autocorrelation alone. Truncating it at a finite maximum lag shaves a little off. The point survives both: the transform whose entire purpose is to encode memory is the same operation that installs autocorrelation into the regressor, and autocorrelation is the documented condition under which the saturation curve stops being identified. A device introduced to capture carryover manufactures the exact regressor property that dissolves the shape you were going to optimize against.
How much carryover buys how much confusion
Weekly spend is drawn near-independently week to week, so the raw series carries almost no autocorrelation (flat dashed line), though it does drift about 30% across the window, as real budgets do, which is why the adstocked series reads a little above α. Adstock is then applied at retention α. The upper curve is the lag-1 autocorrelation the filter creates. The lower curve is the share of the true saturation curvature (the part of the response left after the linear-in-spend component is removed) that a smoothly drifting coefficient can reproduce. Averaged over four spend paths.
With no carryover, a drifting coefficient can mimic only 12% of the saturation curvature and the two families are genuinely distinguishable. At α = 0.70, an unremarkable retention for weekly TV, it mimics 73%. Raise the retention to 0.85 and the share reaches 79%. The effective-sample-size readout uses the same Newey–West design effect this framework applies elsewhere: a 156-week window at ρ = 0.80 is worth about 18 independent weeks.
The effective-sample-size readout deserves a moment, because it is the second, quieter cost. Autocorrelated regressors do not merely alias the shape. They also shrink the information content of the window. The variance design effect for an autocorrelated series, \( \mathrm{deff} = 1 + 2\sum_{k=1}^{L-1}(1 - k/L)\,\rho^k \), is the same quantity this framework computes in planning/identification.py::ar1_design_effect to stop a correlated experiment window from being labelled powered on an independent-observation standard error. At \( \rho = 0.8 \) it equals 8.7, so three years of weekly data carry about eighteen weeks' worth of independent information about the media variable. Fitting a flexible response curve and a drifting coefficient on eighteen effective observations is not a modeling problem you can regularize your way out of.
⚠️ More carryover modeled means less trustworthy saturation
The retention parameter is itself only weakly identified from observational data, so in practice it is substantially prior-driven. That means the smoothness of the regressor whose curvature you are trying to estimate is partly a modeling choice. Pushing a channel's retention prior up (because it feels right for brand TV, because someone's benchmark says 0.8) smooths the regressor and hands the saturation curve's variation to any smooth-in-time component in the model. The response curve does not warn you that it is now being pinned by the prior. It just gets flatter or steeper and stays tight.
The Diagnostics That Will Not Save You
The natural next move is to reach for the standard workflow. It will not help, and each tool in it fails for its own separate reason.
Convergence diagnostics are silent by construction. R-hat, effective sample size and divergences interrogate whether the sampler explored the posterior of the model you wrote. Both models here are well-specified, well-behaved and cleanly sampled. Everything that sampling diagnostics are designed to catch is absent, which is exactly the trap: a clean fit is evidence that the geometry is fine, not that the family is right.
Posterior predictive checks pass both. They must. The two families produce nearly the same predictive distribution on the observed spend path, which is what "observationally equivalent" means. A PPC that separated them would be a refutation of the premise.
Prior predictive checks and simulation-based calibration pass both. SBC verifies that your inference recovers parameters drawn from your own prior under your own model. Run it on model A and it passes. Run it on model B and it passes. Self-consistency within a family is orthogonal to the question of which family generated the data.
Cross-validation is nearly indifferent, and honestly so. In the simulations above the two families' in-sample R² differ by about a thousandth, and out-of-sample they differ only insofar as the future spend path departs from the past one. Expected log predictive density will hand you a difference well inside its own standard error. The criterion is doing its job. Predictive equivalence is the problem, and a predictive criterion cannot resolve a question the predictions do not encode. The two models agree about everything except the counterfactual you are about to enact.
Priors regularize the wrong thing. A tight prior on the innovation scale of a random-walk coefficient does shrink toward a constant \( \beta \), and this framework's default innovation scale of 0.15 does exactly that. But the shrinkage is a statement of belief about drift, not evidence about it. If you are the sort of analyst who believes in saturation, your priors will encode saturation and your posterior will be sharp about saturation, and the same is true in reverse. Two teams with different priors will get confidently different answers from identical data, and neither posterior will be wide enough to reveal that this happened.
There is one diagnostic that does fire, and it is worth running: fit both families and compare the decisions, not the fits. If the marginal-to-average ROAS ratio moves from 0.4 to 1.0 across two specifications that fit equally well, you have not learned that one is right. You have learned that your data does not contain the answer, which is a finding, and one that should be reported.
The Schedule That Breaks the Tie
Because this is an identification problem rather than an estimation problem, the fix has to come from the design of the data, not the design of the model. And because the families differ precisely in what they predict under spend paths the data has never contained, the resolution is to go contain one. Dew and coauthors reach the same conclusion: their closing contribution is showing how marketers can avoid the conflation by designing experiments that manipulate spending in ways that pin down model form.
The requirement has two parts, and they are separate. To identify curvature, the schedule has to visit distinct spend levels and hold them. You learn the shape of \( S \) by observing the response at several points along it, and that is a statement about the range of \( a_t \). To identify carryover, the schedule has to change sharply, because you learn \( \alpha \) from the decay after a drop, which is a statement about the time structure of \( a_t \). This framework's design module states both requirements in its own docstring for the multi-level flighting schedule: "≥3 distinct levels let the experiment trace the response CURVE… not just a single on/off contrast," and "Block length should be >= the channel's adstock memory so carryover doesn't smear the contrast."
That second constraint is the one teams get wrong, and the simulation below shows why it is not a detail. Adstock does not only alias your history. It also smooths your experiment. Pulsing a channel with a 3.1-week half-life in two-week blocks means the regressor never reaches either level before it is moved again, and most of your designed contrast is filtered away before it reaches the response curve.
Designed variation, and the washout that eats it
The same channel (retention 0.80, so a 3.1-week half-life and a 14-week 95% washout), now under a budget-neutral flighting schedule: total spend is held at the business-as-usual level, only its distribution across weeks changes. The top panel shows the weekly schedule and what adstock does to it. The bottom shows the recovered curvature parameter and its spread across eight independent re-runs, against the business-as-usual baseline and the true value. Setting levels to 1 restores the smooth historical schedule.
Business as usual recovers the curvature at 1.96 ± 0.96 against a truth of 2.20, a spread so wide that a re-run could land anywhere from "nearly linear" to "severely saturated." The curvature is undetectable once drift is allowed (t = 1.0). Three levels held eight weeks each, at identical total spend, cut the spread to ±0.41 and lift detection to t = 4.0. Five levels reach ±0.24. Shorten the blocks to two weeks, below the 3.1-week half-life, and the spread widens back to ±0.71 with detection at t = 2.4. The design is level count and dwell time against the washout, never the level count on its own.
Two design lessons fall out that are worth stating plainly. First, most of the detection power arrives with the second level: going from a smooth historical schedule to a two-level pulse cuts the curvature spread from ±0.96 to ±0.37. Additional levels buy less than practitioners expect for detecting curvature and more than they expect for pinning its shape. That distinction matters, because the shape is what extrapolates to a spend level you have not tried. Second, the dwell time interacts with the very parameter that caused the problem. A channel with retention \( \alpha \) needs roughly \( \ln(0.05)/\ln(\alpha) \) weeks to wash out to 5%: three weeks at \( \alpha=0.3 \), nine at 0.7, fourteen at 0.8, twenty-nine at 0.9. Design your blocks below that and adstock filters your experiment for you, which is the same arithmetic that governs spacing sequential geo tests.
💡 Where this lives in the framework
Both families are available, which is what makes the comparison runnable rather than rhetorical. A channel opts into drifting effectiveness with MediaChannelConfigBuilder(…).with_time_varying() (or MediaChannelConfig.time_varying=True). The graph then builds a demeaned random walk on \( \log \beta_t \) with a HalfNormal innovation scale defaulting to 0.15, emits the trajectory as beta_tv_<channel> alongside the time-average beta_<channel>, and surfaces it for reporting as bundle.time_varying_betas. The multi-level schedule comes from planning.design.flighting_design(levels=…, block_weeks=…), and planning.identification.structural_identification scores how much a candidate schedule would contract the structural parameters. It will refuse to claim the saturation parameter unless the design offers at least three distinct in-support levels. The synthetic world synth.dgp.make_time_varying_beta plants a fatiguing channel and a mid-series structural break for exactly this kind of test.
An honest boundary: the identification module scores the within-family block (coefficient, carryover, saturation) and reports an explicitly optimistic upper bound on what a refit achieves. It does not compute a between-family Bayes factor, and nothing in the framework currently automates the saturation-versus-drift comparison. Running both specifications and diffing their decisions is still a manual act.
What This Does Not Establish
Several things, and they matter for how far you should carry the argument.
It does not establish that time-varying coefficients are wrong, or that saturation is. Both mechanisms are real. Creative does wear out, and response curves are concave. The claim is narrower and stranger: over a typical observational window, the data does not adjudicate, and whichever you fit will come back confident.
The simulations here are demonstrations, not evidence about your data. They use a single channel, a known baseline, additive Gaussian noise, a polynomial-in-time stand-in for a random-walk coefficient, and an exponential saturation form. Real panels have correlated channels, misspecified baselines, and heavier tails, all of which make the conflation worse rather than better. But "worse in simulation" is not a measurement of severity in the wild. Dew and coauthors document prevalence across real datasets. I have not.
The autocorrelation-to-conflation link is not a theorem I proved. It is the paper's result, restated. What I have added is the observation that adstock is an AR(1) filter and therefore a deliberate generator of the triggering condition, plus the secant-slope identity that makes the aliasing explicit. Both are elementary, and neither is quoted from the paper.
Designed variation resolves the question locally, not globally. A flighting schedule spanning 0.1× to 2× historical spend identifies the curve over that range. It says nothing about the shape at 5×, which is precisely where an aggressive budget recommendation would like to go. Extrapolation beyond the designed support remains a prior, however the curve was estimated.
And the fix costs something. A budget-neutral pulse schedule is neutral in total spend, not in outcome: flighting a channel with real carryover through low weeks sacrifices some contribution relative to a smooth plan. That is a genuine short-term cost paid for a genuine long-term identification gain, and it should be priced as such rather than waved through as free.
What to Do About It
The practical program is short, and none of it requires new methodology.
Start by fitting both families and comparing decisions rather than fits. If the marginal-to-average ratio, the recommended budget change and the "is this channel dying" verdict all move materially between two specifications with indistinguishable R², the honest report is a range with a stated reason, not a point estimate from whichever specification the team prefers. This is a specification-curve exercise in the narrow, load-bearing sense: a deliberate comparison of the two families the data cannot separate, rather than a robustness parade over arbitrary choices.
Then check the autocorrelation of your adstocked regressors before you trust any response curve. It takes one line, it is directly interpretable through the design effect, and a channel whose adstocked series sits above \( \rho \approx 0.8 \) should be assumed to have a prior-driven saturation curve until an experiment says otherwise. Treat a high retention prior as what it is (a decision to reduce the identification of the shape) and set it deliberately rather than by benchmark.
Where a channel's curve actually drives money, buy the variation. A budget-neutral multi-level schedule with dwell time above the washout is the cheapest instrument available, because it manufactures the exogenous spend variation your media plan never produced and it does so without changing the annual budget. Where a full schedule is impractical, a geo lift test still helps, though less than one might hope: calibrating to a single experiment pins the coefficient at one operating point and thereby anchors the level, but a single point does not identify the shape. Two operating points do considerably more than twice as much.
Finally, do not let the drift trajectory be reported as a finding without a schedule chart beside it. A falling \( \beta_t \) plotted next to a rising spend plan is the signature of the secant slope, not of creative wear-out, and the two are visually identical. If effectiveness genuinely fell, it should have fallen at a moment tied to something real (a creative change, a platform change, a competitive entry) and not merely tracked the budget monotonically for three years. That is a weak test. It is also the only one you get for free.
Takeaways
- A static concave response and a linear response with a drifting coefficient fit the same weekly series to within 0.001 of R² and never separate by more than half a residual standard deviation, while disagreeing 2.3× about marginal-to-average ROAS, the number the budget optimizer consumes.
- The aliasing has an exact form: a drifting coefficient recovers \( \beta_t = \beta\,S(a_t)/a_t \), the secant slope of the true curve read in time order. A channel being scaled up therefore always looks like it is fatiguing, and one being wound down looks like it is improving, regardless of what is really happening.
- Geometric adstock is an AR(1) filter: \( a_t = \alpha a_{t-1} + x_t \), so it installs lag-1 autocorrelation of exactly \( \alpha \) into the regressor. Autocorrelated media variables are the documented condition under which the two families conflate (Dew, Padilla and Shchetkina, 2024). More modeled carryover means less identified saturation.
- This is not the within-family carryover–shape ridge. That one produces wide, honest posteriors. This one produces a sharp posterior in the wrong family, and no convergence diagnostic, posterior predictive check, SBC run or cross-validation score detects it, because each is computed inside a family that is internally fine.
- Designed variation is the only tiebreaker, and it has two independent requirements: distinct spend levels to trace the curve, and dwell time above the adstock washout so the carryover does not filter your own experiment. Three levels held eight weeks cut the curvature spread from ±0.96 to ±0.41 at identical total spend, and the same three levels in two-week blocks only reach ±0.71.
- When two families that fit equally well recommend opposite actions, the finding is that the data does not contain the answer. Report the range and the experiment that would close it, not the specification the team likes.
References
- Dew, R., Padilla, N., & Shchetkina, A. (2024). Your MMM is Broken: Identification of Nonlinear and Time-varying Effects in Marketing Mix Models. arXiv:2408.07678 [econ.EM].
- Ng, E., Wang, Z., & Dai, A. (2021). Bayesian Time Varying Coefficient Model with Applications to Marketing Mix Modeling. arXiv:2106.03322 [stat.AP].
- Jin, Y., Wang, Y., Sun, Y., Chan, D., & Koehler, J. (2017). Bayesian Methods for Media Mix Modeling with Carryover and Shape Effects. Google Inc. Research publication.