ROAS and Metrics

Use this page for channel-efficiency outputs and aggregate predictive metrics.

Abacus separates these into two surfaces:

  • mmm.summary and mmm.data for ROAS and cost-per-target outputs
  • mmm.diagnostics for RMSE, MAE, NRMSE, NMAE, and CRPS

For contribution tables that feed these ratios, see Contributions and Decomposition.

Element-wise ROAS and cost per target

The lowest-level efficiency accessors live on mmm.data:

roas_samples = mmm.data.get_elementwise_roas(original_scale=True)
cost_per_target_samples = mmm.data.get_elementwise_cost_per_target(
    original_scale=True,
)

These are direct ratios built from fitted media contributions and channel spend:

  • ROAS = contribution / spend
  • cost per target = spend / contribution

The arrays are element-wise over time, channel, and any panel dims, with posterior sample dimensions on top.

Abacus returns NaN when it would otherwise divide by zero.

Summarise ROAS

Use mmm.summary.roas(...) for a tidy summary table:

roas_df = mmm.summary.roas(
    hdi_probs=[0.80, 0.94],
    frequency="monthly",
    start_date="2024-01-01",
    end_date="2024-06-30",
)

Abacus applies start_date and end_date before any optional aggregation.

The returned table includes:

  • identifying columns such as date, channel, and any panel dims
  • mean
  • median
  • HDI bound columns such as abs_error_94_lower and abs_error_94_upper

Summarise cost per target

For conversion-style targets, use cost_per_target(...):

cpa_df = mmm.summary.cost_per_target(frequency="monthly")

This is the same retained summary surface that mmm.summary.efficiency() uses for target_type="conversion".

Use the default efficiency metric

Abacus chooses the default efficiency metric from the target type:

target_type mmm.summary.efficiency() returns Label
revenue roas() ROAS
conversion cost_per_target() CPA

You can inspect the selected metric with:

metric_key = mmm.summary.efficiency_metric
metric_label = mmm.summary.efficiency_metric_label

Export channel spend

Use channel_spend() when you want the raw spend table with no posterior aggregation:

spend_df = mmm.summary.channel_spend()

This returns the observed channel spend with columns such as date, channel, panel dims, and channel_data.

Predictive error metrics

Predictive metrics live under mmm.diagnostics.predictive_summary():

mmm.sample_posterior_predictive(
    X=X,
    random_seed=42,
    progressbar=False,
)

predictive_metrics = mmm.diagnostics.predictive_summary()

The returned one-row DataFrame includes:

  • rmse
  • mae
  • nrmse
  • nmae
  • crps
  • residual_mean
  • residual_std

These metrics are calculated from the stored posterior predictive samples and the observed target.

Practical guidance

  • Use roas() for revenue targets.
  • Use cost_per_target() for conversion targets.
  • Use efficiency() when you want target-type-aware reporting.
  • Sample posterior predictive values before using predictive metrics.

Common pitfalls

  • Interpreting Abacus ROAS as something other than contribution divided by spend
  • Forgetting that zero spend or zero contribution produces NaN
  • Using predictive diagnostics before calling sample_posterior_predictive(...)