Comparison Outputs

ScenarioPlanner returns structured planning tables rather than a single optimiser object.

This page explains the output objects and the meaning of each table.

Import these objects from abacus.scenarios for new statistical scenario code. abacus.scenario_planner remains a compatibility namespace for existing statistical imports and legacy dashboard app-layer paths.

Output objects

Object Produced by Tables
ScenarioResult planner.evaluate(spec) totals, channels, contributions_over_time, allocation, metadata
ScenarioComparison planner.compare(specs) totals, channels, contributions_over_time, allocations, metadata

ScenarioComparison is a row-wise concatenation of the individual scenario results, with scenario identifiers added to every table.

totals

totals has one row per scenario.

It includes:

  • scenario_id
  • scenario_name
  • scenario_type
  • total_spend
  • contribution_mean
  • contribution_median
  • contribution_hdi_94_lower
  • contribution_hdi_94_upper
  • efficiency_metric
  • efficiency_mean
  • efficiency_median
  • efficiency_hdi_94_lower
  • efficiency_hdi_94_upper

efficiency_metric is ROAS for revenue targets and CPA for conversion targets.

channels

channels has one row per (scenario, channel).

It includes:

  • scenario identifiers
  • channel
  • spend
  • spend_share
  • spend_per_period
  • contribution summary columns
  • contribution-per-period columns
  • efficiency summary columns
  • efficiency_metric

The planner aggregates non-channel panel dims before it builds this table. For example, a (geo, channel) model still returns one row per channel here.

contributions_over_time

contributions_over_time has one row per (scenario, date, channel).

It includes:

  • scenario identifiers
  • date
  • channel
  • contribution_mean
  • contribution_median
  • contribution_hdi_94_lower
  • contribution_hdi_94_upper

Like channels, this table aggregates non-channel panel dims before summarising.

allocations

allocations keeps the original allocation grain.

It includes:

  • scenario identifiers
  • the allocation dims, such as channel, geo, or brand
  • allocation
  • realized_spend

For current scenarios, allocation is the summed historical spend over the reference window. For simulated scenarios, allocation is the requested total horizon allocation and realized_spend is the realised spend from the response simulation.

metadata

metadata is the audit table for each scenario.

Shared fields include:

  • scenario_id
  • scenario_name
  • scenario_type
  • start_date
  • end_date
  • evaluated_start_date
  • evaluated_end_date
  • num_periods
  • target_type
  • efficiency_metric

Additional fields depend on scenario type.

Current scenario metadata

Current scenarios add:

  • reference_window_dates

Manual scenario metadata

Manual scenarios add:

  • requested_total_budget
  • total_budget
  • reference_window_dates
  • budget_unit

Fixed-budget optimised metadata

Optimised scenarios add:

  • requested_total_budget
  • total_budget
  • optimization_success
  • optimization_status
  • optimization_message
  • optimization_objective_value
  • reference_window_dates
  • budget_unit

Requested versus evaluated windows

The metadata table is the best place to check whether the evaluated window matches the requested window.

When include_carryover=True, the evaluated end date can be later than the requested end_date.

Example inspection

comparison = planner.compare(specs)

totals = comparison.totals
metadata = comparison.metadata

optimised_metadata = metadata.loc[
    metadata["scenario_type"] == "fixed_budget_optimized"
].iloc[0]

print(optimised_metadata["optimization_success"])
print(optimised_metadata["optimization_message"])

to_store_payload()

ScenarioComparison.to_store_payload() converts the comparison tables into a JSON-friendly dict:

payload = comparison.to_store_payload()

The payload contains a scalar contract_version and record lists for totals, channels, contributions_over_time, allocations, and metadata. The current contract value is exported as SCENARIO_CONTRACT_VERSION from abacus.scenarios, so downstream clients can check whether they understand the result shape before rendering or importing it.

The deprecated experimental abacus-dashboard application consumed this payload format. The versioned contract remains available to machine consumers: Abacus owns the statistical result shape; clients own rendering and interaction.

Retained recipe artefacts

run_scenario_recipe(...) and evaluate_scenario_recipe(...) persist the comparison as an immutable bundle:

File Purpose
scenario_recipe.resolved.yaml versioned request after validation
scenario_validation.json scenario IDs, estimator, fitted-unit scope, estimand, and pass state
estimator_manifest.yaml fitted estimator contract copied with the scenario evidence
scenario_totals.csv total spend, contribution, efficiency, and uncertainty by scenario
scenario_channels.csv channel summaries and uncertainty
scenario_contributions_over_time.csv date-channel contribution summaries and uncertainty
scenario_allocations.csv requested allocation and realised spend at the original allocation grain
scenario_metadata.csv scenario semantics, dates, history policy, scale, and estimand
scenario_payload.json versioned five-table payload for machine consumers
scenario_artifact_manifest.json source run ID, file sizes, and SHA-256 checksums

The target directory must not exist before evaluation. Abacus validates the comparison before it creates the directory and never overwrites an earlier bundle.

Common pitfalls

  • Reading channels as if it retained non-channel panel dims
  • Ignoring metadata when carryover is enabled
  • Comparing requested allocation with realised spend without checking the allocations table