TransferFunctionLinearRegression#
- class causalpy.pymc_models.TransferFunctionLinearRegression[source]#
Bayesian Transfer Function model for Graded Intervention Time Series.
This model jointly estimates transform parameters (adstock, saturation) and regression coefficients within a Bayesian framework using PyMC.
The model applies transforms to treatment variables using pymc-marketing functions, allowing full Bayesian inference on all parameters including the transform parameters themselves.
- Parameters:
saturation_type (str or None) – Type of saturation transform. Options: “hill”, “logistic”, “michaelis_menten”, None. If None, no saturation is applied.
adstock_config (dict or None) – Configuration for adstock transform. Required keys: - “half_life_prior”: dict with prior specification (e.g., {“dist”: “Gamma”, “alpha”: 4, “beta”: 2}) - “l_max”: int, maximum lag - “normalize”: bool, whether to normalize weights If None, no adstock is applied.
saturation_config (dict or None) – Configuration for saturation transform. Structure depends on saturation_type: - For “hill”: {“slope_prior”: {…}, “kappa_prior”: {…}} - For “logistic”: {“lam_prior”: {…}} - For “michaelis_menten”: {“alpha_prior”: {…}, “lam_prior”: {…}}
coef_constraint (str, default="unconstrained") – Constraint on treatment coefficients: “nonnegative” or “unconstrained”.
sample_kwargs (dict, optional) – Additional kwargs passed to pm.sample().
Notes
The current implementation uses independent Normal errors.
Autocorrelation in Errors: Implementation Challenges
For time series with autocorrelated residuals, see
TransferFunctionARRegression, which implements AR(1) errors via quasi-differencing. This note explains why AR errors require special handling and the design decisions made.Why Standard PyMC AR Approaches Fail:
The fundamental issue is that observed data cannot depend on model parameters in PyMC’s computational graph. For AR errors, we want:
\[y[t] = \mu[t] + \epsilon[t] \epsilon[t] = \rho \cdot \epsilon[t-1] + \nu[t]\]But
\epsilon[t] = y[t] - \mu[t]depends on\mu(which depends on\beta,\theta,half_life, etc.), so this fails:# This FAILS with TypeError residuals = y - mu # depends on parameters! pm.AR("epsilon", rho, observed=residuals) # ❌
Implemented Solution (TransferFunctionARRegression):
Uses quasi-differencing following Box & Tiao (1975):
\[y[t] - \rho \cdot y[t-1] = \mu[t] - \rho \cdot \mu[t-1] + \nu[t]\]This transforms to independent innovations
\nu[t], allowing manual log-likelihood viapm.Potential. This is the theoretically correct Box & Tiao intervention analysis model where AR(1) represents the noise structure itself.Alternative: AR as Latent Component (Not Implemented):
An alternative that avoids the
observed=issue would be:\[y[t] = \mu_{baseline}[t] + ar[t] + \epsilon_{obs}[t] ar[t] = \rho \cdot ar[t-1] + \eta[t] \epsilon_{obs}[t] \sim N(0, \sigma^2_{obs})\]This could use PyMC’s built-in
pm.ARsincear[t]is latent (unobserved):# This WOULD work (but changes model interpretation) ar = pm.AR("ar", rho=[rho_param], sigma=sigma_ar, shape=n_obs) mu_total = mu_baseline + ar pm.Normal("y", mu=mu_total, sigma=sigma_obs, observed=y_data) # ✅
Why We Don’t Use This Approach:
Different model class: Represents AR + white noise, not pure AR errors. Has two variance parameters (
\sigma_{ar},\sigma_{obs}) vs. one (\sigma).Theoretical mismatch: Box & Tiao’s intervention analysis models autocorrelation in the noise process itself, not as an additional latent component. The AR process IS the residual structure, not a separate mean component.
Identifiability concerns: With both AR and white noise, parameters may be poorly identified unless
\sigma_{obs} \approx 0(which defeats the purpose).Interpretation: The latent AR component would represent a time-varying offset rather than residual autocorrelation, changing the causal interpretation.
When to Use Each Model:
TransferFunctionLinearRegression (this class): When residuals show minimal autocorrelation, or computational efficiency is critical.
TransferFunctionARRegression: When residual diagnostics show significant autocorrelation (e.g., ACF plots, Durbin-Watson test), and you want to follow the classical Box & Tiao specification.
Prior Customization:
Priors are managed using the
Priorclass frompymc_extrasand can be customized via thepriorsparameter:from pymc_extras.prior import Prior model = cp.pymc_models.TransferFunctionLinearRegression( saturation_type=None, adstock_config={...}, priors={ "beta": Prior( "Normal", mu=0, sigma=100, dims=["treated_units", "coeffs"] ), "sigma": Prior("HalfNormal", sigma=50, dims=["treated_units"]), }, )
By default, data-informed priors are set automatically via
priors_from_data():Baseline coefficients (
beta):Normal(0, 5 * std(y))Treatment coefficients (
theta_treatment):Normal(0, 2 * std(y))orHalfNormal(2 * std(y))Error std (
sigma):HalfNormal(2 * std(y))
This adaptive approach ensures priors are reasonable regardless of data scale.
Examples
Basic usage:
import causalpy as cp model = cp.pymc_models.TransferFunctionLinearRegression( saturation_type=None, adstock_config={ "half_life_prior": {"dist": "Gamma", "alpha": 4, "beta": 2}, "l_max": 8, "normalize": True, }, sample_kwargs={"chains": 4, "draws": 2000, "tune": 1000}, )
Methods
Initialize TransferFunctionLinearRegression model.
Register a dimension coordinate with the model.
Vectorized version of
Model.add_coord.Add a random graph variable to the named variables of the model.
Build the PyMC model with transforms.
TransferFunctionLinearRegression.calculate_cumulative_impact(impact)Calculate the causal impact as the difference between observed and predicted values.
Check that the logp is defined and finite at the starting point.
Compiled log probability density hessian function.
Compiled log probability density gradient function.
Compiles a PyTensor function.
Compiled log probability density function.
Clone the model.
Create a
TensorVariablethat will be used as the random variable's "value" in log-likelihood graphs.Hessian of the models log-probability w.r.t.
Debug model function at point.
Gradient of the models log-probability w.r.t.
Evaluate shapes of untransformed AND transformed free variables.
TransferFunctionLinearRegression.fit(X, y, ...)Fit the Transfer Function model.
Compute the initial point of the model.
Elemwise log-probability of the model.
Compile a PyTensor function that computes logp and gradient.
Create a TensorVariable for an observed random variable.
Check if name has prefix and adds if needed.
Check if name has prefix and deletes if needed.
Compute the log probability of point for all random variables in the model.
Predict data given input data X
Generate data-informed priors that scale with outcome variable.
Compile and profile a PyTensor function which returns
outsand takes values of model vars as a dict as an argument.Register a data variable with the model.
Register an (un)observed random variable with the model.
Clone and replace random variables in graphs with their value variables.
Score the Bayesian \(R^2\) given inputs
Xand outputsy.Change the values of a data variable in the model.
Update a mutable dimension.
Set an initial value (strategy) for a random variable.
Produce a graphviz Digraph from a PyMC model.
Attributes
basic_RVsList of random variables the model is defined in terms of.
continuous_value_varsAll the continuous value variables in the model.
coordsCoordinate values for model dimensions.
datalogpPyTensor scalar of log-probability of the observed variables and potential terms.
default_priorsdim_lengthsThe symbolic lengths of dimensions in the model.
discrete_value_varsAll the discrete value variables in the model.
isrootobservedlogpPyTensor scalar of log-probability of the observed variables.
parentpotentiallogpPyTensor scalar of log-probability of the Potential terms.
prefixrootunobserved_RVsList of all random variables, including deterministic ones.
unobserved_value_varsList of all random variables (including untransformed projections), as well as deterministics used as inputs and outputs of the model's log-likelihood graph.
value_varsList of unobserved random variables used as inputs to the model's log-likelihood (which excludes deterministics).
varlogpPyTensor scalar of log-probability of the unobserved random variables (excluding deterministic).
varlogp_nojacPyTensor scalar of log-probability of the unobserved random variables (excluding deterministic) without jacobian term.
- __init__(saturation_type=None, adstock_config=None, saturation_config=None, coef_constraint='unconstrained', sample_kwargs=None, priors=None)[source]#
Initialize TransferFunctionLinearRegression model.
- classmethod __new__(*args, **kwargs)#