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 ammm.scenarios for new statistical scenario code. ammm.scenario_planner contains the statistical implementation and exports the same objects. Application wrappers are separate projects.

Output objects

ObjectProduced byTables
ScenarioResultplanner.evaluate(spec)totals, channels, contributions_over_time, allocation, metadata
ScenarioComparisonplanner.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 ammm.scenarios, so downstream clients can check whether they understand the result shape before rendering or importing it.

This payload is the versioned integration contract for external application wrappers. Consumers must validate the contract version and retained evidence before interpreting or displaying results.

Retained recipe artefacts

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

FilePurpose
scenario_recipe.resolved.yamlversioned request after validation
scenario_validation.jsonscenario IDs, estimator, fitted-unit scope, estimand, and pass state
estimator_manifest.yamlfitted estimator contract copied with the scenario evidence
scenario_totals.csvtotal spend, contribution, efficiency, and uncertainty by scenario
scenario_channels.csvchannel summaries and uncertainty
scenario_contributions_over_time.csvdate-channel contribution summaries and uncertainty
scenario_allocations.csvrequested allocation and realised spend at the original allocation grain
scenario_metadata.csvscenario semantics, dates, history policy, scale, and estimand
scenario_payload.jsonversioned five-table payload for machine consumers
scenario_artifact_manifest.jsonsource run ID, file sizes, and SHA-256 checksums

The target directory must not exist before evaluation. ammm 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