Time-Varying Parameters

Abacus supports time-varying intercept and media effects through SoftPlusHSGP, a Hilbert Space Gaussian Process approximation.

Two ways to enable time variation

For both time_varying_intercept and time_varying_media, you can pass:

  • True to use the config-driven default path
  • a custom HSGPBase instance such as SoftPlusHSGP(...)

Boolean mode

The simplest entry point is a boolean flag:

mmm = PanelMMM(
    date_column="date",
    target_column="sales",
    channel_columns=["tv", "search"],
    adstock=GeometricAdstock(l_max=8),
    saturation=LogisticSaturation(),
    time_varying_intercept=True,
    time_varying_media=True,
)

When you do this, Abacus builds a SoftPlusHSGP internally from:

  • model_config["intercept_tvp_config"]
  • model_config["media_tvp_config"]

The default config keys are HSGPKwargs with:

  • m=200
  • L=None
  • eta_lam=1
  • ls_mu=5
  • ls_sigma=10
  • cov_func=None

What the boolean defaults mean

With boolean mode:

  • time_varying_intercept=True creates intercept_latent_process over ("date", *dims)
  • time_varying_media=True creates media_temporal_latent_multiplier over ("date", *dims)

That second point matters:

boolean time_varying_media=True gives you one shared temporal multiplier per panel slice, not a different time-varying multiplier per channel

If you want channel-specific time variation, pass a custom HSGP with channel in its dims.

Custom SoftPlusHSGP

Use a custom HSGP instance when you need precise control over dims, covariance, or priors.

Example: channel-specific time-varying media in a simple timeseries model.

import numpy as np

from abacus.mmm import SoftPlusHSGP

n_dates = X["date"].nunique()

media_hsgp = SoftPlusHSGP.parameterize_from_data(
    X=np.arange(n_dates),
    dims=("date", "channel"),
    cov_func="matern32",
)

mmm = PanelMMM(
    date_column="date",
    target_column="sales",
    channel_columns=["tv", "search"],
    adstock=GeometricAdstock(l_max=8),
    saturation=LogisticSaturation(),
    time_varying_media=media_hsgp,
)

For a panel model with dims=("geo",), valid media HSGP dims include:

  • ("date", "geo")
  • ("date", "geo", "channel")

For the intercept, the custom dims should align with the target axes, typically ("date",) or ("date", *dims).

Supported covariance choices

For SoftPlusHSGP.parameterize_from_data(...), the supported covariance keywords are:

  • "expquad"
  • "matern32"
  • "matern52"

Config formats for boolean mode

The *_tvp_config entries in model_config support two formats.

HSGPKwargs style

from abacus.hsgp_kwargs import HSGPKwargs

model_config = {
    "intercept_tvp_config": HSGPKwargs(
        m=50,
        L=None,
        eta_lam=1.0,
        ls_mu=5.0,
        ls_sigma=10.0,
        cov_func=None,
    )
}

Equivalent dict form is also accepted.

parameterize_from_data style

You can also pass a dict that is forwarded to SoftPlusHSGP.parameterize_from_data(...):

model_config = {
    "intercept_tvp_config": {
        "ls_lower": 1.0,
        "ls_upper": 10.0,
    }
}

Abacus preserves that dict and uses it when constructing the HSGP.

How the latent process enters the model

Time-varying intercept

Abacus creates:

intercept_contribution = intercept_baseline * intercept_latent_process

Time-varying media

Abacus first creates a baseline transformed media contribution, then multiplies it by the temporal latent process:

channel_contribution =
  baseline_channel_contribution * media_temporal_latent_multiplier

If the custom media HSGP dims include channel, the multiplier can vary by channel. Otherwise it is broadcast across channels.

Save and load

Custom SoftPlusHSGP instances round-trip through PanelMMM.save(...) and PanelMMM.load(...).

That includes custom dims such as:

  • ("date",)
  • ("date", "channel")
  • ("date", "geo")
  • ("date", "geo", "channel")

Common pitfalls

  • Expecting time_varying_media=True to create channel-specific media multipliers
  • Using custom HSGP dims that do not align with the model dims
  • Forgetting that boolean mode uses model_config["intercept_tvp_config"] and model_config["media_tvp_config"]

Next steps