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
| 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_idscenario_namescenario_typetotal_spendcontribution_meancontribution_mediancontribution_hdi_94_lowercontribution_hdi_94_upperefficiency_metricefficiency_meanefficiency_medianefficiency_hdi_94_lowerefficiency_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
channelspendspend_sharespend_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
datechannelcontribution_meancontribution_mediancontribution_hdi_94_lowercontribution_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, orbrand allocationrealized_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_idscenario_namescenario_typestart_dateend_dateevaluated_start_dateevaluated_end_datenum_periodstarget_typeefficiency_metric
Additional fields depend on scenario type.
Current scenario metadata
Current scenarios add:
reference_window_dates
Manual scenario metadata
Manual scenarios add:
requested_total_budgettotal_budgetreference_window_datesbudget_unit
Fixed-budget optimised metadata
Optimised scenarios add:
requested_total_budgettotal_budgetoptimization_successoptimization_statusoptimization_messageoptimization_objective_valuereference_window_datesbudget_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:
| 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. ammm validates the comparison before it creates the directory and never overwrites an earlier bundle.
Common pitfalls
- Reading
channelsas if it retained non-channel panel dims - Ignoring
metadatawhen carryover is enabled - Comparing requested allocation with realised spend without checking the
allocationstable