Reference

Lookup documentation for the CLI, YAML schema, manifest schema, output layout, and related contracts.

Pages

  • CLI referencemeridian-tools provides a command-line interface with two subcommands: run and demo.
  • YAML configuration schema reference — This is the complete field-level reference for meridian-tools YAML configuration files. For usage guidance, see the configuration guide.
  • Manifest schema reference — The run_manifest.json file is the source of truth for every meridian-tools run. It lives at the root of the run directory and records identity, timing, versions, overall status, top-level artefact index, and per-stage records.
  • Output schema reference — This page documents the complete run directory layout produced by meridian-tools. Every successful pipeline run creates a timestamped directory containing the artefacts described below.
  • Validation spec schema reference — The validation_spec.json artefact is written to 10_validation/ for every validation-aware pipeline run. It records the concrete validation provenance for that specific run, including the holdout strategy, split geometry, and date windows.

Subsections of Reference

CLI reference

meridian-tools provides a command-line interface with two subcommands: run and demo.

Global usage

meridian-tools <subcommand> [options]

meridian-tools run

Execute a meridian-tools pipeline run from an authored YAML config.

meridian-tools run --config <path> [--output-dir <dir>] [--run-name <name>] [--traceback]

Arguments

Argument Required Default Description
--config Yes Path to the meridian-tools YAML configuration file.
--output-dir No runs Directory where dated run folders will be created.
--run-name No project.name from YAML Optional run name override.
--traceback No false Show the full Python traceback on failure.

Examples

# Basic run
meridian-tools run --config project.yml

# Custom output directory
meridian-tools run --config project.yml --output-dir output/model_runs

# Named run with traceback on failure
meridian-tools run --config project.yml --run-name client-q1-review --traceback

Exit codes

Code Meaning
0 Pipeline completed successfully.
1 Pipeline failed. Error details are printed to stderr. Use --traceback for the full stack trace.

Failure reporting

The CLI distinguishes five broad failure classes:

  • config loading or Pydantic validation failures before wrapper preflight
  • dependency preflight failures before run-directory creation
  • validation-execution contract failures before run-directory creation
  • wrapper-owned ConfigPreflightError failures before run-directory creation
  • PipelineRunFailure after the dated run directory already exists

Dependency preflight covers google-meridian[schema] support and optional plot-export support. Validation-execution contract failures cover incompatible single-run validation requests such as direct rolling_origin execution. Wrapper preflight covers only the closed config/data matrix documented in the configuration guide.

For PipelineRunFailure, the CLI prints the concrete failed run directory, manifest path, and stage name when available so the partial run can be inspected immediately. --traceback still shows the original underlying exception because it is preserved through __cause__.

Validation strategy restrictions

The CLI executes a single pipeline run. Configs with validation.strategy: rolling_origin cannot be run directly from the CLI because they require multiple sequential runs. Use the Python API for rolling-origin workflows.

Configs with strategy: none or strategy: blocked_tail work directly from the CLI.

meridian-tools demo

Run one of the bundled reference demos or list available demos.

meridian-tools demo [<name>] [--list] [--output-dir <dir>] [--run-name <name>] [--traceback]

Arguments

Argument Required Default Description
<name> Yes (unless --list) Bundled demo name to execute. One of: timeseries, geo_panel.
--list No false List supported demos and exit. Cannot combine with a demo name.
--output-dir No runs/demos/ (source checkout) or ./runs/demos/ (installed) Override the output root directory.
--run-name No None (uses project.name from the demo config) Optional run name override.
--traceback No false Show the full Python traceback on failure.

Examples

# List available demos
meridian-tools demo --list

# Run the timeseries demo
meridian-tools demo timeseries

# Run with a custom output directory
meridian-tools demo geo_panel --output-dir sandbox/demo-output

# Run with a custom name
meridian-tools demo timeseries --run-name demo-review-q2

Available demos

Name Description
timeseries National timeseries demo using bundled reference data.
geo_panel Geo-panel demo using bundled reference data.

Both demos exercise the full staged pipeline including response curves and optimisation.

Lightweight import

The CLI is designed for fast startup. Running meridian-tools --help or meridian-tools demo --list does not import TensorFlow, NumPy, Meridian, or ArviZ. Heavy imports are deferred until pipeline execution begins.

Entrypoints

The primary CLI entrypoint is the console script registered in pyproject.toml:

[project.scripts]
meridian-tools = "meridian_tools.cli:main"

The supported module-path equivalent is:

python -m meridian_tools.cli run --config project.yml

The package-level form below is not part of the supported contract in this milestone:

python -m meridian_tools run --config project.yml

Source-tree wrapper

When working from the source checkout, runme.py provides equivalent functionality:

python runme.py run --config project.yml --output-dir runs
python runme.py demo timeseries
python runme.py demo --list

See the demo guide for more details on the runme.py wrapper.

YAML configuration schema reference

This is the complete field-level reference for meridian-tools YAML configuration files. For usage guidance, see the configuration guide.

All configuration models use Pydantic extra="forbid" — any key not listed here will produce a validation error.

Top-level structure

project: ProjectConfig         # optional, has defaults
data: CsvDataConfig            # required
model_spec: ModelSpecConfig    # optional, has defaults
fit: FitConfig                 # optional, has defaults
validation: ValidationConfig   # optional, has defaults
exports: ExportsConfig         # optional, has defaults
response_curves: ResponseCurvesConfig | null   # optional
optimisation: OptimisationConfig | null         # optional

project

Field Type Default Description
name str "meridian-project" Human-readable project name. Used as the base for run directory names.

data

Field Type Default Description
path Path required Path to CSV data file. Relative paths resolve against the YAML file’s directory.
kpi_type "revenue" | "non-revenue" "revenue" KPI type for Meridian’s data loader.
coord_to_columns dict[str, Any] required Maps Meridian coordinate names to CSV column names. Must include time.
media_to_channel dict[str, str] | null null Optional media-to-channel mapping override.
media_spend_to_channel dict[str, str] | null null Optional media-spend-to-channel mapping override.
reach_to_channel dict[str, str] | null null Optional reach-to-channel mapping override.
frequency_to_channel dict[str, str] | null null Optional frequency-to-channel mapping override.
rf_spend_to_channel dict[str, str] | null null Optional RF-spend-to-channel mapping override.
organic_reach_to_channel dict[str, str] | null null Optional organic-reach-to-channel mapping override.
organic_frequency_to_channel dict[str, str] | null null Optional organic-frequency-to-channel mapping override.

model_spec

Field Type Default Description
kwargs dict[str, Any] {} Keyword arguments forwarded directly to Meridian ModelSpec(**kwargs).

Supported kwargs keys include any argument accepted by Meridian’s ModelSpec constructor: max_lag, media_prior_type, holdout_id, etc. If holdout_id is present, the run is treated as an authored-holdout validation run.

Array-valued keys (holdout_id, control_population_scaling_id, non_media_population_scaling_id, rf_roi_calibration_period, roi_calibration_period) are converted to NumPy arrays at runtime.


fit

Field Type Default Constraint Description
sample_prior_draws PositiveInt | null null >0 if set Number of prior predictive draws. null skips prior sampling.
n_chains PositiveInt | list[PositiveInt] 4 >0 Number of MCMC chains.
n_adapt PositiveInt 500 >0 Adaptation steps per chain.
n_burnin PositiveInt 500 >0 Burn-in steps per chain.
n_keep PositiveInt 1000 >0 Posterior samples to retain per chain.
seed int | list[int] | null null RNG seed for reproducibility.
max_tree_depth PositiveInt 10 >0 NUTS maximum tree depth.
max_energy_diff float 500.0 NUTS maximum energy difference.
unrolled_leapfrog_steps PositiveInt 1 >0 NUTS unrolled leapfrog steps.
parallel_iterations PositiveInt 10 >0 TensorFlow parallel iterations.

validation

Field Type Default Constraint Description
strategy "none" | "blocked_tail" | "rolling_origin" "none" Validation strategy.
holdout_size PositiveInt | null null Required for blocked_tail Number of tail time periods to hold out.
initial_train_size PositiveInt | null null Required for rolling_origin Initial training window size.
test_size PositiveInt | null null Required for rolling_origin Test window size per split.
step_size PositiveInt | null null Must equal test_size Step between rolling splits. Defaults to test_size.
max_splits PositiveInt | null null >=2 if set Maximum number of rolling splits.

Cross-field validation rules

  • strategy: none rejects all holdout and rolling-origin parameters.
  • strategy: blocked_tail requires holdout_size, rejects rolling-origin parameters.
  • strategy: rolling_origin requires initial_train_size and test_size, rejects holdout_size.
  • holdout_size without an explicit strategy is rejected (legacy shorthand removed).
  • Rolling-origin parameters without strategy: rolling_origin are rejected.

exports

Field Type Default Description
use_kpi bool false Use KPI-based metrics in Meridian analysis surfaces.
batch_size PositiveInt 1000 Batch size for Meridian Analyzer computations.
export_predictive_accuracy bool true Write predictive_accuracy.csv.
export_review_summary bool true Write review_summary.json.
export_model_selection bool true Write LOO/WAIC outputs (when compatible).
export_plots bool true Write PNG plot artefacts in each stage.

response_curves

Optional section. If omitted or null, the response curves stage is skipped.

Field Type Default Constraint Description
spend_multipliers list[float] required Non-empty, all >=0 Spend multiplier grid for response curve computation.
use_posterior bool true Use posterior (vs prior) for response curves.
by_reach bool true Compute reach-based response curves.
use_optimal_frequency bool false Use optimal frequency in computation.
confidence_level float 0.9 0 < x < 1 Confidence level for credible intervals.

optimisation

Optional section. If omitted or null, the optimisation stage is skipped.

Field Type Default Constraint Description
start_date str required ISO YYYY-MM-DD Start of the optimisation window.
end_date str required ISO YYYY-MM-DD, >= start_date End of the optimisation window.
budget OptimisationBudgetConfig required Budget specification (see below).
use_posterior bool true Use posterior (vs prior) for optimisation.
use_optimal_frequency bool true Use optimal frequency in optimisation.
confidence_level float 0.9 0 < x < 1 Confidence level for credible intervals.

optimisation.budget

Field Type Default Constraint Description
mode "fixed_total" | "relative_reference_window_total" required Budget mode.
value PositiveFloat required >0 Budget value. Absolute for fixed_total, multiplier for relative_reference_window_total.

When mode: relative_reference_window_total, the effective budget is value × total_spend_in_reference_window. The reference window is defined by start_date and end_date.

Manifest schema reference

The run_manifest.json file is the source of truth for every meridian-tools run. It lives at the root of the run directory and records identity, timing, versions, overall status, top-level artefact index, and per-stage records.

Current version

The current manifest version is 3. Versions 0, 1, and 2 are supported for backward compatibility when loading older run directories.

Top-level fields

Field Type Description
manifest_version int Schema version (0, 1, 2, or 3).
run_name str Human-readable run name.
config_path str Path to the source YAML used for this run. For refresh runs this points to the source run’s archived config.source.yaml.
output_dir str Path to the run directory.
started_at str UTC ISO-8601 timestamp when the run began.
status str Overall run status: "running", "completed", or "failed".
finished_at str | null UTC ISO-8601 timestamp when the run finished. null while running.
meridian_tools_version str Version of meridian-tools that produced the run.
meridian_version str | null Version of Google Meridian used. null if not yet recorded.
artifacts dict[str, str] Top-level artefact index. Key artefacts from stages are promoted here for quick lookup.
stages list[StageRecord] Ordered list of pipeline stage records (including skipped and failed stages).

Top-level artifacts index

The runner promotes key artefacts into the top-level artifacts dictionary after each stage completes. Promoted artefact names include:

  • config_source, config_resolved, input_data_provenance (from 00_run_metadata)
  • validation_spec (from 10_validation)
  • meridian_model (from 20_model_fit)
  • diagnostics_bundle, model_results_summary (from 30_model_assessment)
  • summary_metrics_csv, summary_metrics_nc (from 40_decomposition)

This index provides flat access to important artefacts without walking the stages array.

StageRecord fields

Each entry in the stages array represents one pipeline stage. Stages can have any of four statuses: "running", "completed", "skipped", or "failed".

Field Type Description
name str Stage identifier (for example, "00_run_metadata", "20_model_fit").
status str Stage status: "running", "completed", "skipped", or "failed".
started_at str | null UTC ISO-8601 timestamp when the stage began.
finished_at str | null UTC ISO-8601 timestamp when the stage finished.
elapsed_seconds float | null Wall-clock seconds for stage execution.
message str | null Human-readable message. Present for skipped stages (reason) and failed stages (error).
artifacts dict[str, str] Map of artefact names to relative file paths. Empty for skipped stages.

Artefact path convention

All artefact paths in the manifest are relative to the run directory. This makes run directories portable across machines and file systems. When you load a run record through load_run_record, the lifecycle layer resolves relative paths to absolute paths against the run directory.

Example stage record:

{
  "name": "30_model_assessment",
  "status": "completed",
  "started_at": "2026-04-02T07:40:30+00:00",
  "finished_at": "2026-04-02T07:41:00+00:00",
  "elapsed_seconds": 30.1,
  "message": null,
  "artifacts": {
    "diagnostics_bundle": "30_model_assessment/diagnostics_bundle.json",
    "review_summary": "30_model_assessment/review_summary.json",
    "model_results_summary": "30_model_assessment/model_results_summary.html"
  }
}

Stage names and ordering

All seven stages are always recorded in execution order. Stages that do not apply to a given run are recorded with status: "skipped".

Stage name Number Skippable Description
00_run_metadata 00 No Config archival and input-data provenance capture.
10_validation 10 Yes Validation spec (skipped when no validation applies).
20_model_fit 20 No Meridian model fitting.
30_model_assessment 30 No Diagnostics, model selection.
40_decomposition 40 No Media decomposition metrics.
60_response_curves 60 Yes Response curves (skipped when the config section is absent).
70_optimisation 70 Yes Budget optimisation (skipped when the config section is absent).

The numbering gap at 50 is intentional, reserving space for future stages.

Required artefacts

The lifecycle layer requires the following top-level artefacts to be present in the manifest for a run to be loadable:

  • config_source (promoted from 00_run_metadata)
  • config_resolved (promoted from 00_run_metadata)
  • input_data_provenance (promoted from 00_run_metadata) for manifest version 3 runs

These are enforced by _require_manifest_artifact in load_run_record. If a required entry is missing, a LifecycleError is raised.

The diagnostics_bundle artefact is treated as optional by the lifecycle loader. If it is absent from the manifest, RunRecord.diagnostics_bundle_path is None. However, diagnostics_bundle is listed in REQUIRED_MANIFEST_ARTIFACTS and validated at run completion time — so new runs always produce it, but older or partial runs can still be loaded without it.

Input-data provenance payload

Manifest version 3 introduces 00_run_metadata/input_data_provenance.json. This file records the pinned Phase 09 input-data contract:

  • provenance_version
  • authored_path
  • resolved_path
  • sha256
  • size_bytes
  • mtime_utc
  • row_count
  • column_count
  • ordered_columns

The lifecycle compare surface uses these fields to distinguish real dataset changes from older runs whose manifests predate provenance capture.

Example manifest

{
  "manifest_version": 3,
  "run_name": "my-project_blocked_tail",
  "config_path": "/workspace/configs/project.yml",
  "output_dir": "/workspace/runs/my-project_blocked_tail_20260402_073500",
  "started_at": "2026-04-02T07:35:00+00:00",
  "status": "completed",
  "finished_at": "2026-04-02T07:42:15+00:00",
  "meridian_tools_version": "0.3.0",
  "meridian_version": "1.5.3",
  "artifacts": {
    "config_source": "00_run_metadata/config.source.yaml",
    "config_resolved": "00_run_metadata/config.resolved.yaml",
    "input_data_provenance": "00_run_metadata/input_data_provenance.json",
    "validation_spec": "10_validation/validation_spec.json",
    "meridian_model": "20_model_fit/meridian_model.binpb",
    "diagnostics_bundle": "30_model_assessment/diagnostics_bundle.json",
    "model_results_summary": "30_model_assessment/model_results_summary.html",
    "summary_metrics_csv": "40_decomposition/summary_metrics.csv",
    "summary_metrics_nc": "40_decomposition/summary_metrics.nc"
  },
  "stages": [
    {
      "name": "00_run_metadata",
      "status": "completed",
      "started_at": "2026-04-02T07:35:00+00:00",
      "finished_at": "2026-04-02T07:35:01+00:00",
      "elapsed_seconds": 0.5,
      "message": null,
      "artifacts": {
        "config_source": "00_run_metadata/config.source.yaml",
        "config_resolved": "00_run_metadata/config.resolved.yaml",
        "input_data_provenance": "00_run_metadata/input_data_provenance.json"
      }
    },
    {
      "name": "10_validation",
      "status": "completed",
      "started_at": "2026-04-02T07:35:01+00:00",
      "finished_at": "2026-04-02T07:35:01+00:00",
      "elapsed_seconds": 0.1,
      "message": null,
      "artifacts": {
        "validation_spec": "10_validation/validation_spec.json"
      }
    },
    {
      "name": "20_model_fit",
      "status": "completed",
      "started_at": "2026-04-02T07:35:01+00:00",
      "finished_at": "2026-04-02T07:40:30+00:00",
      "elapsed_seconds": 329.0,
      "message": null,
      "artifacts": {
        "meridian_model": "20_model_fit/meridian_model.binpb",
        "fit_metadata": "20_model_fit/fit_metadata.json"
      }
    },
    {
      "name": "30_model_assessment",
      "status": "completed",
      "started_at": "2026-04-02T07:40:30+00:00",
      "finished_at": "2026-04-02T07:41:00+00:00",
      "elapsed_seconds": 30.1,
      "message": null,
      "artifacts": {
        "diagnostics_bundle": "30_model_assessment/diagnostics_bundle.json",
        "review_summary": "30_model_assessment/review_summary.json",
        "model_results_summary": "30_model_assessment/model_results_summary.html",
        "model_selection_status": "30_model_assessment/model_selection_status.json"
      }
    },
    {
      "name": "40_decomposition",
      "status": "completed",
      "started_at": "2026-04-02T07:41:00+00:00",
      "finished_at": "2026-04-02T07:42:00+00:00",
      "elapsed_seconds": 60.0,
      "message": null,
      "artifacts": {
        "summary_metrics_nc": "40_decomposition/summary_metrics.nc",
        "summary_metrics_csv": "40_decomposition/summary_metrics.csv"
      }
    },
    {
      "name": "60_response_curves",
      "status": "skipped",
      "started_at": "2026-04-02T07:42:00+00:00",
      "finished_at": "2026-04-02T07:42:00+00:00",
      "elapsed_seconds": 0.0,
      "message": "No `response_curves` section was authored in the YAML config.",
      "artifacts": {}
    },
    {
      "name": "70_optimisation",
      "status": "skipped",
      "started_at": "2026-04-02T07:42:00+00:00",
      "finished_at": "2026-04-02T07:42:00+00:00",
      "elapsed_seconds": 0.0,
      "message": "No `optimisation` section was authored in the YAML config.",
      "artifacts": {}
    }
  ]
}

Version history

Version 3 (current)

Added input_data_provenance.json and made provenance available to lifecycle loading and compare surfaces.

Version 2

Added export_plots support, top-level artifacts index, status field, config_path, output_dir, and per-stage status, elapsed_seconds, and message fields.

Version 1

Added meridian_version field and response_curves / optimisation stages.

Version 0

Initial manifest schema with core stages and artefact tracking.

All four versions are supported by RunManifest.from_dict. Missing fields in older versions are filled with defaults.

Output schema reference

This page documents the complete run directory layout produced by meridian-tools. Every successful pipeline run creates a timestamped directory containing the artefacts described below.

Run directory structure

<run_name>_<YYYYMMDD_HHMMSS>/
├── run_manifest.json                        # Source of truth for the run
├── 00_run_metadata/
│   ├── config.source.yaml                   # Verbatim copy of the authored YAML
│   ├── config.resolved.yaml                 # YAML after path resolution
│   └── input_data_provenance.json           # Pinned source/resolution/hash metadata
├── 10_validation/                           # Only for validation-aware runs
│   └── validation_spec.json                 # Validation provenance record
├── 20_model_fit/
│   ├── meridian_model.binpb                 # Serialised Meridian model
│   └── fit_metadata.json                    # Fit settings and Meridian version
├── 30_model_assessment/
│   ├── diagnostics_bundle.json              # Diagnostics export manifest
│   ├── predictive_accuracy.csv              # Per-observation accuracy metrics
│   ├── review_summary.json                  # Meridian review battery results
│   ├── model_results_summary.html           # Meridian HTML summary report
│   ├── plots/                               # When export_plots: true
│   │   ├── model_fit.png
│   │   └── rhat_boxplot.png
│   │
│   │  # Model selection outputs (compatible runs):
│   ├── loo_summary.json                     # LOO summary statistics
│   ├── waic_summary.json                    # WAIC summary statistics
│   ├── loo_pointwise.csv                    # Per-observation LOO + Pareto k
│   ├── waic_pointwise.csv                   # Per-observation WAIC
│   └── model_comparison.csv                 # Ranked comparison table
│   │
│   │  # Model selection status (incompatible runs):
│   └── model_selection_status.json          # Reason code for unavailability
├── 40_decomposition/
│   ├── summary_metrics.nc                   # NetCDF decomposition dataset
│   ├── summary_metrics.csv                  # Tabular decomposition
│   └── plots/                               # When export_plots: true
│       ├── channel_contribution_area_chart.png
│       ├── contribution_waterfall_chart.png
│       ├── spend_vs_contribution_chart.png
│       └── roi_bar_chart.png
├── 60_response_curves/                      # Only when response_curves configured
│   ├── response_curves.nc                   # NetCDF response curve dataset
│   ├── response_curves.csv                  # Tabular response curves
│   └── plots/                               # When export_plots: true
│       └── response_curves_plot.png
└── 70_optimisation/                         # Only when optimisation configured
    ├── optimisation_summary.html            # Meridian optimisation HTML report
    ├── optimised_data.nc                    # Optimised allocation (NetCDF)
    ├── optimised_data.csv                   # Optimised allocation (CSV)
    ├── nonoptimised_data.nc                 # Baseline allocation (NetCDF)
    ├── nonoptimised_data.csv                # Baseline allocation (CSV)
    ├── optimisation_grid.csv                # Full optimisation grid
    └── plots/                               # When export_plots: true
        ├── incremental_outcome_delta_plot.png
        ├── budget_allocation_optimised_plot.png
        ├── budget_allocation_nonoptimised_plot.png
        ├── spend_delta_plot.png
        └── optimisation_response_curves_plot.png

Stage details

00_run_metadata

Always present. Created first.

Artefact Format Description
config.source.yaml YAML Verbatim copy of the source config for this run. On refresh, this is copied from the source run’s archived config.source.yaml.
config.resolved.yaml YAML Config after relative path resolution. Does not include runtime-only fields (output_dir, run_name).
input_data_provenance.json JSON Pinned input-data provenance: authored path, resolved path, SHA-256, file size, mtime, row count, column count, and ordered columns.

10_validation

Present only for validation-aware runs (blocked tail, rolling origin, authored holdout, or final fit with validation provenance).

Artefact Format Description
validation_spec.json JSON Full validation provenance. See validation-spec-schema.md.

20_model_fit

Always present.

Artefact Format Description
meridian_model.binpb Protocol Buffers Serialised Meridian model (requires google-meridian[schema]).
fit_metadata.json JSON Records FitConfig values and Meridian version.

30_model_assessment

Always present. Content varies by compatibility.

Artefact Format Condition Description
diagnostics_bundle.json JSON Always Diagnostics export manifest with status of each sub-export.
predictive_accuracy.csv CSV export_predictive_accuracy: true Predictive accuracy per observation.
review_summary.json JSON export_review_summary: true Meridian review battery results.
model_results_summary.html HTML Always Meridian HTML model summary.
plots/model_fit.png PNG export_plots: true Model fit visualisation.
plots/rhat_boxplot.png PNG export_plots: true R-hat convergence diagnostic boxplot.
loo_summary.json JSON Compatible + export_model_selection: true LOO summary.
waic_summary.json JSON Compatible + export_model_selection: true WAIC summary.
loo_pointwise.csv CSV Compatible + export_model_selection: true Per-observation LOO values.
waic_pointwise.csv CSV Compatible + export_model_selection: true Per-observation WAIC values.
model_comparison.csv CSV Compatible + export_model_selection: true Ranked model comparison.
model_selection_status.json JSON Incompatible + export_model_selection: true Reason for unavailability.

40_decomposition

Always present.

Artefact Format Description
summary_metrics.nc NetCDF Full decomposition dataset with coordinates.
summary_metrics.csv CSV Flattened tabular decomposition.
plots/channel_contribution_area_chart.png PNG Channel contribution over time.
plots/contribution_waterfall_chart.png PNG Contribution waterfall breakdown.
plots/spend_vs_contribution_chart.png PNG Spend vs. contribution scatter.
plots/roi_bar_chart.png PNG ROI by channel bar chart.

60_response_curves

Present only when the response_curves YAML section is configured.

Artefact Format Description
response_curves.nc NetCDF Response curve dataset across spend multipliers.
response_curves.csv CSV Flattened tabular response curves.
plots/response_curves_plot.png PNG Response curve visualisation.

70_optimisation

Present only when the optimisation YAML section is configured.

Artefact Format Description
optimisation_summary.html HTML Meridian optimisation summary report.
optimised_data.nc NetCDF Optimised budget allocation.
optimised_data.csv CSV Tabular optimised allocation.
nonoptimised_data.nc NetCDF Baseline (non-optimised) allocation.
nonoptimised_data.csv CSV Tabular baseline allocation.
optimisation_grid.csv CSV Full optimisation grid dataset.
plots/incremental_outcome_delta_plot.png PNG Incremental outcome delta.
plots/budget_allocation_optimised_plot.png PNG Optimised allocation chart.
plots/budget_allocation_nonoptimised_plot.png PNG Baseline allocation chart.
plots/spend_delta_plot.png PNG Spend delta between optimised and baseline.
plots/optimisation_response_curves_plot.png PNG Optimisation response curves.

Reading order for analysts

For a quick assessment of a completed run:

  1. run_manifest.json — run identity, timing, stage completion
  2. 00_run_metadata/config.source.yaml — what was authored
  3. 00_run_metadata/input_data_provenance.json — dataset identity and shape
  4. 30_model_assessment/diagnostics_bundle.json — diagnostics export state
  5. 30_model_assessment/model_results_summary.html — visual model summary
  6. 40_decomposition/summary_metrics.csv — easiest tabular output to inspect

For model selection:

  1. 30_model_assessment/loo_summary.json or model_selection_status.json

For scenario analysis:

  1. 60_response_curves/response_curves.csv
  2. 70_optimisation/optimisation_summary.html

Validation spec schema reference

The validation_spec.json artefact is written to 10_validation/ for every validation-aware pipeline run. It records the concrete validation provenance for that specific run, including the holdout strategy, split geometry, and date windows.

Fields

Field Type Description
mode "validation" | "final_fit" Whether this is a validation split or the final production fit.
strategy "none" | "blocked_tail" | "rolling_origin" | "authored_holdout" Validation strategy that produced this run.
split_label str Human-readable identifier for the split (e.g. "blocked_tail", "split_01", "final_fit").
holdout_source "generated_validation" | "authored_model_spec" | "none" How the holdout mask was produced.
generated_holdout bool Whether the holdout mask was auto-generated by meridian-tools.
run_name_suffix str Suffix appended to the run name for this split.
holdout_shape list[int] | null Shape of the holdout mask array. null for final-fit runs.
train_indices list[int] Integer indices into the time axis used for training.
test_indices list[int] Integer indices into the time axis used for testing. Empty for final-fit runs.
train_dates list[str] Date values corresponding to train_indices.
test_dates list[str] Date values corresponding to test_indices. Empty for final-fit runs.

Mode and strategy combinations

Mode Strategy Holdout source Description
validation blocked_tail generated_validation Auto-generated contiguous tail holdout.
validation rolling_origin generated_validation One split from an expanding-window plan.
validation authored_holdout authored_model_spec User-provided holdout mask from YAML.
final_fit none none Full-sample production fit after validation.

Invariants

  • Validation-mode specs always have a non-null holdout_shape.
  • Final-fit specs always have holdout_shape: null, empty test_indices, and empty test_dates.
  • train_indices and train_dates always have matching lengths.
  • test_indices and test_dates always have matching lengths.
  • Authored-holdout specs have empty train_indices, test_indices, train_dates, and test_dates.

Example: blocked tail validation

{
  "mode": "validation",
  "strategy": "blocked_tail",
  "split_label": "blocked_tail",
  "holdout_source": "generated_validation",
  "generated_holdout": true,
  "run_name_suffix": "blocked_tail",
  "holdout_shape": [10],
  "train_indices": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
  "test_indices": [12, 13, 14, 15, 16, 17, 18, 19],
  "train_dates": ["2024-01-01", "2024-01-08", "..."],
  "test_dates": ["2024-03-25", "2024-04-01", "..."]
}

Example: rolling origin split

{
  "mode": "validation",
  "strategy": "rolling_origin",
  "split_label": "split_01",
  "holdout_source": "generated_validation",
  "generated_holdout": true,
  "run_name_suffix": "split_01",
  "holdout_shape": [60],
  "train_indices": [0, 1, 2, "...", 51],
  "test_indices": [52, 53, 54, 55],
  "train_dates": ["2024-01-01", "..."],
  "test_dates": ["2024-12-30", "2025-01-06", "2025-01-13", "2025-01-20"]
}

Example: final fit

{
  "mode": "final_fit",
  "strategy": "none",
  "split_label": "final_fit",
  "holdout_source": "none",
  "generated_holdout": false,
  "run_name_suffix": "final_fit",
  "holdout_shape": null,
  "train_indices": [0, 1, 2, "...", 59],
  "test_indices": [],
  "train_dates": ["2024-01-01", "...", "2025-02-24"],
  "test_dates": []
}

Note on holdout mask storage

The actual holdout mask array (boolean NumPy array) is not stored in validation_spec.json because it can be large for geo-panel models (n_geos × n_times). Only its holdout_shape is recorded. The mask is injected into the Meridian model at runtime and can be reconstructed from train_indices, test_indices, and the data geometry.

Python API

Public Python APIs exposed by meridian-tools.

Pages

Subsections of Python API

meridian_tools.config

Configuration models and YAML loading for meridian-tools.

Module: meridian_tools.config

Functions

load_yaml_config

def load_yaml_config(path: str | Path) -> MeridianToolsConfig

Load and validate a meridian-tools YAML file.

Parameters:

  • path — Path to the YAML configuration file.

Returns: A validated MeridianToolsConfig instance.

Raises: pydantic.ValidationError if the YAML content does not match the schema.

Example:

from meridian_tools.config import load_yaml_config

config = load_yaml_config("project.yml")
print(config.project.name)
print(config.data.path)
print(config.validation.strategy)

Classes

MeridianToolsConfig

class MeridianToolsConfig(BaseModel)

Full YAML configuration for one meridian-tools run. This is the top-level model returned by load_yaml_config.

Attribute Type Default
project ProjectConfig ProjectConfig()
data CsvDataConfig required
model_spec ModelSpecConfig ModelSpecConfig()
fit FitConfig FitConfig()
validation ValidationConfig ValidationConfig()
exports ExportsConfig ExportsConfig()
response_curves `ResponseCurvesConfig None`
optimisation `OptimisationConfig None`

PipelineRunConfig

@dataclass(frozen=True)
class PipelineRunConfig

Runtime options that sit outside the YAML file. Passed to run_pipeline.

Attribute Type Default Description
config_path Path required Path to the YAML config file.
output_dir Path Path("runs") Directory for run output.
run_name `str None` None
validation_spec `ValidationRunSpec None` None
apply_run_name_suffix bool True Whether to append validation-aware suffixes to the run name.
source_config_path `Path None` None

ProjectConfig

class ProjectConfig(BaseModel)
Attribute Type Default
name str "meridian-project"

CsvDataConfig

class CsvDataConfig(BaseModel)

CSV loader configuration compatible with Meridian’s CsvDataLoader.

Attribute Type Default
path Path required
kpi_type Literal["revenue", "non-revenue"] "revenue"
coord_to_columns dict[str, Any] required
media_to_channel `dict[str, str] None`
media_spend_to_channel `dict[str, str] None`
reach_to_channel `dict[str, str] None`
frequency_to_channel `dict[str, str] None`
rf_spend_to_channel `dict[str, str] None`
organic_reach_to_channel `dict[str, str] None`
organic_frequency_to_channel `dict[str, str] None`

ModelSpecConfig

class ModelSpecConfig(BaseModel)
Attribute Type Default
kwargs dict[str, Any] {}

FitConfig

class FitConfig(BaseModel)

Sampling configuration for Meridian posterior fitting.

Attribute Type Default
sample_prior_draws `PositiveInt None`
n_chains `PositiveInt list[PositiveInt]`
n_adapt PositiveInt 500
n_burnin PositiveInt 500
n_keep PositiveInt 1000
seed `int list[int]
max_tree_depth PositiveInt 10
max_energy_diff float 500.0
unrolled_leapfrog_steps PositiveInt 1
parallel_iterations PositiveInt 10

ValidationConfig

class ValidationConfig(BaseModel)

Validation and holdout orchestration settings.

Attribute Type Default
strategy Literal["none", "blocked_tail", "rolling_origin"] "none"
holdout_size `PositiveInt None`
initial_train_size `PositiveInt None`
test_size `PositiveInt None`
step_size `PositiveInt None`
max_splits `PositiveInt None`

See the validation guide for cross-field validation rules.


ExportsConfig

class ExportsConfig(BaseModel)
Attribute Type Default
use_kpi bool False
batch_size PositiveInt 1000
export_predictive_accuracy bool True
export_review_summary bool True
export_model_selection bool True
export_plots bool True

ResponseCurvesConfig

class ResponseCurvesConfig(BaseModel)
Attribute Type Default Constraint
spend_multipliers list[float] required Non-empty, all >= 0
use_posterior bool True
by_reach bool True
use_optimal_frequency bool False
confidence_level float 0.9 0 < x < 1

OptimisationConfig

class OptimisationConfig(BaseModel)
Attribute Type Default Constraint
start_date str required ISO YYYY-MM-DD
end_date str required ISO YYYY-MM-DD, >= start_date
budget OptimisationBudgetConfig required
use_posterior bool True
use_optimal_frequency bool True
confidence_level float 0.9 0 < x < 1

OptimisationBudgetConfig

class OptimisationBudgetConfig(BaseModel)
Attribute Type Default
mode Literal["fixed_total", "relative_reference_window_total"] required
value PositiveFloat required

meridian_tools.runner

Pipeline orchestration for meridian-tools.

Module: meridian_tools.runner

Functions

run_pipeline

def run_pipeline(
    run_config: PipelineRunConfig,
    *,
    progress_callback: Callable | None = None,
) -> PipelineRunResult

Execute the full meridian-tools staged pipeline.

The pipeline proceeds through the following stages in order:

  1. 00_run_metadata — Archive source and resolved configs and write input_data_provenance.json.
  2. 10_validation — Write validation spec (if validation-aware).
  3. 20_model_fit — Build input data, construct the Meridian model, sample prior and posterior.
  4. 30_model_assessment — Export diagnostics, model summary, and model selection outputs.
  5. 40_decomposition — Export summary metrics.
  6. 60_response_curves — Export response curves (if configured).
  7. 70_optimisation — Export optimisation results (if configured).

The manifest is written to disk after each stage, so a failure mid-pipeline leaves a readable partial manifest.

Before creating the dated run directory, the runner enforces three separate pre-run checks:

  1. dependency preflight (google-meridian[schema], optional plot support)
  2. validation-execution contract checks for incompatible single-run validation combinations
  3. a narrow wrapper-owned config/data preflight over the resolved input file and authored column mapping

The wrapper-owned preflight checks exactly:

  • resolved data.path exists and is a regular file
  • the CSV header row can be read
  • the parsed header is non-empty
  • no parsed header cell is blank after trimming whitespace
  • every authored scalar entry in data.coord_to_columns exists in the header
  • every authored list member in data.coord_to_columns exists in the header
  • every authored key in media_to_channel, media_spend_to_channel, reach_to_channel, frequency_to_channel, rf_spend_to_channel, organic_reach_to_channel, and organic_frequency_to_channel exists in the header
  • authored list-valued coord families are non-empty
  • authored mapping fields above are non-empty
  • supported media/RF family groups are complete when authored

Header matching is exact and case-sensitive. Anything outside this closed matrix remains Meridian-owned validation.

Parameters:

  • run_config — A PipelineRunConfig specifying the execution config path, output directory, run name, optional validation spec, and optional source_config_path for metadata archival.
  • progress_callback — Optional callable invoked on stage lifecycle events. The callback receives keyword arguments:
    • stage_name (str) — stage identifier.
    • event (str) — one of "started", "completed", "skipped", or "failed".
    • stage_index (int) — 1-based position in the pipeline.
    • stage_count (int) — total number of stages.
    • elapsed_seconds (float) — wall-clock time (present for "completed" and "failed" events).
    • message (str) — human-readable detail (present for "skipped" and "failed" events).

Returns: A PipelineRunResult with the run directory and manifest path.

Raises:

  • RuntimeError if Meridian schema support is unavailable (checked at preflight before the run directory is created).
  • RuntimeError if exports.export_plots is true but vl-convert-python is not installed (also checked at preflight).
  • ValidationExecutionContractError if the requested single-run validation execution path is incompatible with the authored config.
  • ConfigPreflightError if wrapper-owned config/data preflight fails before run-directory creation.
  • PipelineRunFailure if any exception occurs after the dated run directory already exists.

Example:

from pathlib import Path
from meridian_tools.config import PipelineRunConfig
from meridian_tools.runner import run_pipeline

result = run_pipeline(
    PipelineRunConfig(
        config_path=Path("project.yml"),
        output_dir=Path("runs"),
    )
)

print(result.run_dir)
print(result.manifest_path)

Classes

PipelineRunResult

@dataclass(frozen=True)
class PipelineRunResult

Disk locations for one completed meridian-tools run.

Attribute Type Description
run_dir Path Absolute path to the run directory.
manifest_path Path Absolute path to run_manifest.json.

ValidationExecutionContractError

class ValidationExecutionContractError(ValueError)

Raised when the requested single-run validation execution path is incompatible with the authored config. Current examples include direct rolling_origin execution through run_pipeline(...) and combining PipelineRunConfig.validation_spec with authored model_spec.kwargs.holdout_id.


ConfigPreflightError

class ConfigPreflightError(ValueError)

Raised when the wrapper-owned Phase 10 preflight fails before run-directory creation. This covers only the closed wrapper preflight boundary, not full Meridian model validation.


PipelineRunFailure

class PipelineRunFailure(RuntimeError)

Raised when a run fails after the dated run directory already exists. The original underlying exception is preserved via __cause__.

Attribute Type Description
run_dir Path Absolute failed run directory.
manifest_path Path Absolute path to the failed run manifest.
stage_name str | None Failing stage name when one is available.

Constants

Stage names

Constant Value
STAGE_RUN_METADATA "00_run_metadata"
STAGE_VALIDATION "10_validation"
STAGE_MODEL_FIT "20_model_fit"
STAGE_MODEL_ASSESSMENT "30_model_assessment"
STAGE_DECOMPOSITION "40_decomposition"
STAGE_RESPONSE_CURVES "60_response_curves"
STAGE_OPTIMISATION "70_optimisation"

PIPELINE_STAGE_ORDER

PIPELINE_STAGE_ORDER: tuple[str, ...] = (
    "00_run_metadata",
    "10_validation",
    "20_model_fit",
    "30_model_assessment",
    "40_decomposition",
    "60_response_curves",
    "70_optimisation",
)

The numbering gap at 50 is intentional, reserving space for future stages.

meridian_tools.cv

Cross-validation and holdout orchestration utilities.

Module: meridian_tools.cv

Functions

build_last_window_holdout_mask

def build_last_window_holdout_mask(
    time_index: Sequence[Any],
    holdout_size: int,
    geo_index: Sequence[Any] | None = None,
) -> np.ndarray

Build a blocked-tail holdout mask for Meridian’s holdout_id.

Returns a 1-D boolean mask for national data and a 2-D (n_geos, n_times) mask when geo_index is provided. The last holdout_size time periods are marked as True (held out).

Parameters:

  • time_index — Strictly increasing sequence of time period identifiers.
  • holdout_size — Number of tail periods to hold out. Must be positive and less than the length of time_index.
  • geo_index — Optional sequence of geo identifiers. If provided, the mask is broadcast across geos.

Returns: Boolean NumPy array.

Raises: ValueError for non-monotonic indices, undersized indices, or impossible holdout sizes.


build_rolling_origin_splits

def build_rolling_origin_splits(
    time_index: Sequence[Any],
    *,
    initial_train_size: int,
    test_size: int,
    step_size: int | None = None,
    max_splits: int | None = None,
) -> list[BlockedTimeSplit]

Create expanding-window blocked time splits for rolling-origin validation.

Parameters:

  • time_index — Strictly increasing sequence of time period identifiers.
  • initial_train_size — Size of the first training window.
  • test_size — Size of each test window.
  • step_size — Step between splits. Must equal test_size. Defaults to test_size.
  • max_splits — Maximum number of splits to generate. Must be >= 2 if set.

Returns: List of BlockedTimeSplit instances (at least 2).

Raises: ValueError for invalid parameters or if fewer than 2 splits can be generated.


build_validation_splits

def build_validation_splits(
    validation_config: ValidationConfig,
    time_index: Sequence[Any],
) -> list[BlockedTimeSplit]

Build deterministic split definitions from the typed validation config.

Dispatches to the appropriate split builder based on validation_config.strategy. Returns an empty list for strategy: none.

Parameters:

  • validation_config — A validated ValidationConfig instance.
  • time_index — Strictly increasing sequence of time period identifiers.

Returns: List of BlockedTimeSplit instances (empty for none).


build_validation_plan

def build_validation_plan(
    validation_config: ValidationConfig,
    time_index: Sequence[Any],
    geo_index: Sequence[Any] | None = None,
) -> ValidationPlan

Materialise concrete validation and final-fit run specs from one config.

For strategy: none, returns a plan with no validation runs and no final-fit run. For blocked_tail or rolling_origin, returns one ValidationRunSpec per split plus a final_fit_run spec that trains on the full time axis with no holdout.

Parameters:

  • validation_config — A validated ValidationConfig instance.
  • time_index — Strictly increasing sequence of time period identifiers.
  • geo_index — Optional sequence of geo identifiers for geo-panel models.

Returns: A ValidationPlan instance.

Example:

from meridian_tools.config import load_yaml_config
from meridian_tools.cv import build_validation_plan

config = load_yaml_config("project.yml")
plan = build_validation_plan(
    config.validation,
    time_index=["2024-01-01", "2024-01-08", "..."],
    geo_index=["US-CA", "US-NY"],
)

for run_spec in plan.validation_runs:
    print(run_spec.split_label, len(run_spec.train_indices), len(run_spec.test_indices))

if plan.final_fit_run:
    print("Final fit:", plan.final_fit_run.split_label)

Classes

BlockedTimeSplit

@dataclass(frozen=True)
class BlockedTimeSplit

One blocked time split for validation.

Attribute Type Description
label str Human-readable split label (e.g. "blocked_tail", "split_01").
train_indices tuple[int, ...] Integer indices into the time axis for training.
test_indices tuple[int, ...] Integer indices into the time axis for testing.
train_dates tuple[str, ...] Date values for training periods.
test_dates tuple[str, ...] Date values for test periods.

ValidationRunSpec

@dataclass(frozen=True)
class ValidationRunSpec

One concrete validation or final-fit run derived from a split plan. Passed to PipelineRunConfig.validation_spec to control a single pipeline execution.

Attribute Type Description
mode "validation" | "final_fit" Run mode.
strategy str Validation strategy.
split_label str Human-readable split identifier.
holdout_source str How the holdout mask was produced.
generated_holdout bool Whether the holdout was auto-generated.
holdout_id np.ndarray | None Concrete holdout mask (immutable).
train_indices tuple[int, ...] Training time indices.
test_indices tuple[int, ...] Test time indices.
train_dates tuple[str, ...] Training date values.
test_dates tuple[str, ...] Test date values.
run_name_suffix str Suffix for the run directory name.

Methods:

  • to_artifact_payload() — Returns the JSON-serialisable dictionary written to validation_spec.json.

ValidationPlan

@dataclass(frozen=True)
class ValidationPlan

Concrete validation runs and the separate final-fit run for one config.

Attribute Type Description
validation_runs tuple[ValidationRunSpec, ...] One spec per validation split.
final_fit_run ValidationRunSpec | None Full-sample final-fit spec. None for strategy: none.

meridian_tools.exports

Helpers for manifest-backed Meridian export families.

Module: meridian_tools.exports

Functions

export_model_fit_artifacts

def export_model_fit_artifacts(
    model: Any,
    output_dir: str | Path,
    *,
    fit_config: FitConfig,
    meridian_version: str | None,
) -> dict[str, Path]

Write the stable model-fit artefact set.

Produces:

  • meridian_model.binpb — Serialised Meridian model (Protocol Buffers).
  • fit_metadata.json — Records FitConfig values and Meridian version.

Parameters:

  • model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • fit_config — The FitConfig used for this run.
  • meridian_version — Meridian version string (or None).

Returns: Dictionary mapping artefact names to file paths.


export_model_assessment_artifacts

def export_model_assessment_artifacts(
    model: Any,
    output_dir: str | Path,
    *,
    exports_config: ExportsConfig,
    diagnostics_exporter: Callable,
    model_selection_exporter: Callable,
) -> dict[str, Path]

Write the stable assessment artefact set.

Produces diagnostics bundle, model results summary HTML, and optionally model selection outputs (LOO/WAIC) and diagnostic plots.

Parameters:

  • model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • exports_config — Export switches.
  • diagnostics_exporter — Callable for diagnostics bundle export (typically export_diagnostics_bundle).
  • model_selection_exporter — Callable for model selection export.

Returns: Dictionary mapping artefact names to file paths.


export_decomposition_artifacts

def export_decomposition_artifacts(
    model: Any,
    output_dir: str | Path,
    *,
    exports_config: ExportsConfig,
) -> dict[str, Path]

Write the stable decomposition artefact set.

Produces:

  • summary_metrics.nc — NetCDF decomposition dataset.
  • summary_metrics.csv — Flattened tabular decomposition.
  • plots/ — Channel contribution, waterfall, spend vs. contribution, and ROI charts (when export_plots: true).

Parameters:

  • model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • exports_config — Export switches.

Returns: Dictionary mapping artefact names to file paths.


export_response_curve_artifacts

def export_response_curve_artifacts(
    model: Any,
    output_dir: str | Path,
    *,
    response_curves_config: ResponseCurvesConfig,
    exports_config: ExportsConfig,
) -> dict[str, Path]

Write the stable response-curve artefact set.

Produces:

  • response_curves.nc — NetCDF response curve dataset.
  • response_curves.csv — Flattened tabular response curves.
  • plots/response_curves_plot.png — Response curve visualisation (when export_plots: true).

Parameters:

  • model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • response_curves_config — Response curves settings from YAML.
  • exports_config — Export switches.

Returns: Dictionary mapping artefact names to file paths.


export_optimisation_artifacts

def export_optimisation_artifacts(
    model: Any,
    output_dir: str | Path,
    *,
    optimisation_config: OptimisationConfig,
    exports_config: ExportsConfig,
) -> dict[str, Path]

Write the stable optimisation artefact set.

Produces:

  • optimisation_summary.html — Meridian optimisation summary report.
  • optimised_data.nc / .csv — Optimised budget allocation.
  • nonoptimised_data.nc / .csv — Baseline allocation.
  • optimisation_grid.csv — Full optimisation grid.
  • plots/ — Delta, allocation, spend, and response curve charts (when export_plots: true).

For budget.mode: relative_reference_window_total, the effective budget is computed as value × total_spend_in_reference_window using the model’s media and RF spend data within the start_dateend_date window.

Parameters:

  • model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • optimisation_config — Optimisation settings from YAML.
  • exports_config — Export switches.

Returns: Dictionary mapping artefact names to file paths.


ensure_meridian_schema_support

def ensure_meridian_schema_support() -> Callable

Return Meridian’s schema serialiser or raise a stable runtime error.

Checks for meridian.schema.serde.meridian_serde.save_meridian. If the import fails, raises RuntimeError with guidance to install google-meridian[schema].

Returns: The save_meridian callable.


ensure_altair_png_support

def ensure_altair_png_support() -> Any

Return the Altair PNG backend or raise a stable runtime error.

Checks for vl_convert. If the import fails, raises RuntimeError with guidance to install vl-convert-python.

Returns: The vl_convert module.

meridian_tools.diagnostics

Diagnostics extraction and export helpers for Meridian runs.

Module: meridian_tools.diagnostics

Functions

predictive_accuracy_frame

def predictive_accuracy_frame(
    meridian_model: Any,
    *,
    use_kpi: bool = False,
    selected_geos: Sequence[str] | None = None,
    selected_times: Sequence[str] | None = None,
    batch_size: int = 1000,
) -> pd.DataFrame

Return Meridian predictive accuracy as a flat DataFrame.

Uses Meridian’s Analyzer.predictive_accuracy internally and flattens the resulting xarray dataset into a pandas DataFrame.

Parameters:

  • meridian_model — Fitted Meridian model instance.
  • use_kpi — Use KPI-based metrics.
  • selected_geos — Optional subset of geos to evaluate.
  • selected_times — Optional subset of time periods to evaluate.
  • batch_size — Batch size for Meridian analysis.

Returns: A pandas DataFrame with one row per observation.


review_summary_dict

def review_summary_dict(
    meridian_model: Any,
    *,
    selected_geos: Sequence[str] | None = None,
    selected_times: Sequence[str] | None = None,
) -> dict[str, Any]

Run Meridian’s review battery and return a JSON-ready dictionary.

Uses Meridian’s ModelReviewer internally. All non-primitive values (dataclasses, enums, NumPy arrays) are recursively converted to JSON-serialisable types.

Parameters:

  • meridian_model — Fitted Meridian model instance.
  • selected_geos — Optional subset of geos.
  • selected_times — Optional subset of time periods.

Returns: A JSON-serialisable dictionary.


export_diagnostics_bundle

def export_diagnostics_bundle(
    meridian_model: Any,
    output_dir: str | Path,
    *,
    use_kpi: bool = False,
    export_predictive_accuracy: bool = True,
    export_review_summary: bool = True,
    selected_geos: Sequence[str] | None = None,
    selected_times: Sequence[str] | None = None,
    batch_size: int = 1000,
) -> dict[str, Path]

Write predictive accuracy, review summary, and bundle manifest to disk.

The bundle manifest (diagnostics_bundle.json) records the status of each sub-export ("exported" or "disabled") along with the file name and format. This provides a stable machine-readable contract for downstream consumers.

When an export is disabled, any pre-existing file from a previous run at the same path is removed to prevent stale data.

Parameters:

  • meridian_model — Fitted Meridian model instance.
  • output_dir — Directory to write artefacts to.
  • use_kpi — Use KPI-based metrics.
  • export_predictive_accuracy — Write predictive_accuracy.csv.
  • export_review_summary — Write review_summary.json.
  • selected_geos — Not supported in current scope (raises ValueError).
  • selected_times — Not supported in current scope (raises ValueError).
  • batch_size — Batch size for Meridian analysis.

Returns: Dictionary mapping artefact names to file paths. Always includes "diagnostics_bundle". Conditionally includes "predictive_accuracy" and "review_summary".

Example:

from meridian_tools.diagnostics import export_diagnostics_bundle

artifacts = export_diagnostics_bundle(
    fitted_model,
    "output/30_model_assessment",
    export_predictive_accuracy=True,
    export_review_summary=True,
)

print(artifacts["diagnostics_bundle"])
# Path("output/30_model_assessment/diagnostics_bundle.json")

meridian_tools.model_selection

Model-selection helpers layered on top of ArviZ and Meridian.

Module: meridian_tools.model_selection

Functions

has_log_likelihood

def has_log_likelihood(candidate: Any) -> bool

Return whether the candidate exposes a non-empty log_likelihood group.

Accepts either an ArviZ InferenceData or any object with an .inference_data attribute (e.g. a fitted Meridian model).

Parameters:

  • candidate — ArviZ InferenceData or fitted Meridian model.

Returns: True if a non-empty log_likelihood group exists.


compute_loo

def compute_loo(
    candidate: Any,
    *,
    pointwise: bool = False,
    scale: str = "log",
) -> InformationCriterionResult

Compute PSIS-LOO for a Meridian model or InferenceData.

If the candidate is a fitted Meridian model without a log_likelihood group, the function automatically reconstructs it through attach_log_likelihood.

Parameters:

  • candidate — Fitted Meridian model or ArviZ InferenceData with log_likelihood.
  • pointwise — Include per-observation LOO values and Pareto k diagnostics.
  • scale — Scale for ELPD computation ("log", "negative_log", or "deviance").

Returns: An InformationCriterionResult with kind="loo".

Raises: ModelSelectionError if log-likelihood cannot be obtained.


compute_waic

def compute_waic(
    candidate: Any,
    *,
    pointwise: bool = False,
    scale: str = "log",
) -> InformationCriterionResult

Compute WAIC for a Meridian model or InferenceData.

Same automatic log-likelihood reconstruction as compute_loo.

Parameters:

  • candidate — Fitted Meridian model or ArviZ InferenceData with log_likelihood.
  • pointwise — Include per-observation WAIC values.
  • scale — Scale for ELPD computation.

Returns: An InformationCriterionResult with kind="waic".

Raises: ModelSelectionError if log-likelihood cannot be obtained.


compare_models

def compare_models(
    candidates: Mapping[str, Any],
    *,
    ic: str = "loo",
    scale: str = "log",
) -> pd.DataFrame

Compare multiple models with ArviZ compare.

Parameters:

  • candidates — Dictionary mapping model names to fitted Meridian models or InferenceData objects.
  • ic — Information criterion to use: "loo" or "waic".
  • scale — Scale for ELPD computation.

Returns: A pandas DataFrame with columns: model, rank, elpd_{ic}, p_{ic}, elpd_diff, weight, se, dse, warning, scale. Ranked by ELPD (rank 0 is best).

For a single candidate, returns a one-row DataFrame with rank=0, elpd_diff=0.0, and weight=1.0.

Raises:

  • ValueError if ic is not "loo" or "waic", or if candidates is empty.
  • ModelSelectionError if any candidate lacks log-likelihood data.

Classes

ModelSelectionError

class ModelSelectionError(RuntimeError)

Raised when information criteria cannot be computed.

Property Type Description
reason_code str | None Structured code identifying the failure reason.

Known reason codes:

Code Meaning
missing_log_likelihood_group InferenceData has no log_likelihood group and cannot be reconstructed.
holdout_fit_unsupported Model was fitted with a holdout mask.
requires_fitted_meridian_model Missing posterior samples or ArviZ InferenceData.
meridian_internal_seam_incompatible Meridian version lacks required reconstruction methods.

InformationCriterionResult

@dataclass(frozen=True)
class InformationCriterionResult

Summary of one information-criterion computation.

Attribute Type Description
kind str "loo" or "waic".
summary dict[str, Any] Summary statistics (ELPD, p, SE, etc.).
pointwise pd.DataFrame | None Per-observation values (if pointwise=True).

meridian_tools.log_likelihood

Log-likelihood computation and attachment for Meridian models.

Module: meridian_tools.log_likelihood

Functions

compute_log_likelihood_dataset

def compute_log_likelihood_dataset(
    meridian_model: Any,
) -> xr.Dataset

Compute the pointwise log-likelihood dataset for a fitted Meridian model.

This function reconstructs the joint distribution from the posterior samples and computes observation-level log-likelihood values. It handles both geo-panel and national models.

The reconstruction recovers unsaved posterior parameters (e.g. geo deviations, tau_g_excl_baseline) that Meridian does not persist to InferenceData by default.

Parameters:

  • meridian_model — A fitted Meridian model with posterior samples and a compatible posterior_sampler_callable.

Returns: An xarray Dataset with a log_likelihood variable.

Raises: ModelSelectionError if the model does not expose the required internal reconstruction seams or lacks posterior samples.


attach_log_likelihood

def attach_log_likelihood(
    meridian_model: Any,
    *,
    in_place: bool = False,
) -> az.InferenceData

Attach a log_likelihood group to a Meridian model’s InferenceData.

If the model’s InferenceData already has a non-empty log_likelihood group, it is returned as-is (or the existing InferenceData is returned for in_place=True).

Parameters:

  • meridian_model — A fitted Meridian model.
  • in_place — If True, mutates meridian_model.inference_data directly. If False (default), returns a deep copy with the log_likelihood group attached. The original model is never modified.

Returns: An ArviZ InferenceData with a log_likelihood group.

Raises:

  • ModelSelectionError with reason_code="meridian_internal_seam_incompatible" if the Meridian version lacks the required private reconstruction methods.
  • ModelSelectionError with reason_code="requires_fitted_meridian_model" if the model has no posterior samples.
  • ModelSelectionError with reason_code="holdout_fit_unsupported" if the model was fitted with a holdout mask.

Example:

from meridian_tools.log_likelihood import attach_log_likelihood

# Non-mutating (default)
idata = attach_log_likelihood(fitted_model, in_place=False)
assert hasattr(idata, "log_likelihood")

# Mutating
attach_log_likelihood(fitted_model, in_place=True)
assert hasattr(fitted_model.inference_data, "log_likelihood")

Implementation notes

The reconstruction accesses three private methods on Meridian’s posterior_sampler_callable:

  • _get_joint_dist_unpinned
  • _prepare_latents_for_reconstruction
  • _reconstruct_posteriors

These are Meridian-internal and may change without notice. If any method is missing, a ModelSelectionError with reason_code="meridian_internal_seam_incompatible" is raised instead of crashing. See the Meridian integration notes for details on this coupling boundary.

meridian_tools.lifecycle

Post-run record management: loading, listing, comparing, and refreshing runs.

Module: meridian_tools.lifecycle

Functions

resolve_run_directory

def resolve_run_directory(path: str | Path) -> Path

Return the absolute resolved run directory for a run path or manifest path.

If path points to a file, it must be named run_manifest.json; the function returns its parent directory. If path is a directory, it must contain run_manifest.json.

Parameters:

  • path — Path to a run directory or to run_manifest.json directly.

Returns: Absolute Path to the run directory.

Raises: LifecycleError if the path does not exist, is an unexpected file, or the directory does not contain run_manifest.json.


load_run_record

def load_run_record(path: str | Path) -> RunRecord

Load one run directory through the versioned lifecycle contract.

Resolves the run directory, parses the manifest, and resolves artefact paths. Required artefacts (config_source, config_resolved) must be present in the manifest and exist on disk. Manifest version 3 runs must also include input_data_provenance. Optional artefacts (validation_spec, diagnostics_bundle, model_selection_status) are resolved when present and set to None when absent.

Parameters:

  • path — Path to a run directory or to run_manifest.json directly.

Returns: A validated RunRecord instance.

Raises: LifecycleError for missing required artefacts, malformed manifests, artefact path traversal, or claimed-but-missing artefacts.


list_run_records

def list_run_records(root: str | Path) -> list[RunRecord]

Discover direct child run directories under one output root.

Scans direct child directories of root for run_manifest.json files. Returns records sorted by started_at (most recent first), with directory name as a secondary sort key.

Parameters:

  • root — Directory to scan. Must be a directory, not a file.

Returns: List of RunRecord instances.

Raises: LifecycleError if root is not a directory or if any discovered run has an invalid manifest.


build_refresh_run_config

def build_refresh_run_config(
    path: str | Path,
    *,
    output_dir: str | Path | None = None,
    run_name: str | None = None,
) -> PipelineRunConfig

Build a runtime refresh config from one stored run directory.

The execution config path points to the source run’s config.resolved.yaml. The returned PipelineRunConfig.source_config_path preserves the source run’s archived config.source.yaml so the refresh can re-copy the original YAML into the new run metadata. The output directory defaults to the source run’s parent directory (creating a sibling run). For validation runs, the validation spec is reconstructed from the stored validation_spec.json.

Parameters:

  • path — Path to the run directory or manifest to refresh.
  • output_dir — Override the output directory (default: source parent).
  • run_name — Override the run name.

Returns: A PipelineRunConfig ready for run_pipeline.

Raises: LifecycleError if the source run cannot be loaded or if authored-holdout refresh requirements are not met.


refresh_run

def refresh_run(
    path: str | Path,
    *,
    output_dir: str | Path | None = None,
    run_name: str | None = None,
) -> PipelineRunResult

Execute a non-destructive refresh run from one stored lifecycle record.

This is a convenience function that calls build_refresh_run_config followed by run_pipeline. The original run directory is never modified.

Parameters:

  • path — Path to the run directory or manifest to refresh.
  • output_dir — Override the output directory (default: source parent).
  • run_name — Override the run name.

Returns: A PipelineRunResult for the new run.


compare_run_records

def compare_run_records(
    left: str | Path,
    right: str | Path,
) -> pd.DataFrame

Compare two run records at the pinned metadata layer.

Loads both run records and compares run name, status, versions, validation spec presence, diagnostics statuses, model selection availability, and input-data provenance.

Parameters:

  • left — Path to the first run directory or manifest.
  • right — Path to the second run directory or manifest.

Returns: A pandas DataFrame with columns field, left, right, status, and changed. Rows follow a fixed order:

Row (field) Description
run_name Human-readable run name.
status Overall run status.
meridian_tools_version meridian-tools version.
meridian_version Google Meridian version.
has_validation_spec Whether a validation spec is present.
has_diagnostics_bundle Whether a diagnostics bundle is present.
predictive_accuracy_status Status from the diagnostics bundle.
review_summary_status Status from the diagnostics bundle.
has_model_selection_outputs Whether LOO/WAIC outputs are present.
model_selection_reason_code Reason code if model selection is unavailable.
input_authored_path YAML-owned data.path string.
input_resolved_path Absolute runtime input path.
input_mtime_utc Input file mtime.
input_sha256 Input file SHA-256 digest.
input_size_bytes Input file size in bytes.
input_row_count Input row count.
input_column_count Input column count.
input_ordered_columns Input CSV column order.

For provenance rows, status is "legacy_unknown" and changed is None when either run predates manifest version 3 and therefore has no stored provenance payload.

Raises: LifecycleError if either run cannot be loaded or if diagnostics or model selection artefacts are malformed.


Classes

RunRecord

@dataclass(frozen=True)
class RunRecord

Resolved lifecycle view over one on-disk run directory.

Attribute Type Description
run_dir Path Absolute path to the run directory.
manifest_path Path Absolute path to run_manifest.json.
manifest RunManifest Parsed manifest with stages, timestamps, and versions.
config_source_path Path Absolute path to config.source.yaml. Always present.
config_resolved_path Path Absolute path to config.resolved.yaml. Always present.
input_data_provenance_path Path | None Path to input_data_provenance.json. Required for manifest version 3 runs, otherwise None.
validation_spec_path Path | None Path to validation_spec.json, or None if absent.
diagnostics_bundle_path Path | None Path to diagnostics_bundle.json, or None if absent.
model_selection_status_path Path | None Path to model_selection_status.json, or None if absent.

Required attributes (config_source_path, config_resolved_path) are always present. input_data_provenance_path is present for manifest version 3 runs. Other optional attributes are None when the corresponding artefact was not produced by the run or is absent from the manifest.

Example:

from meridian_tools.lifecycle import load_run_record

record = load_run_record("runs/my-project_blocked_tail_20260402_073500")

# Required — always available
print(record.config_source_path)
print(record.config_resolved_path)

# Optional — may be None
if record.diagnostics_bundle_path:
    print(f"Diagnostics: {record.diagnostics_bundle_path}")
if record.validation_spec_path:
    print(f"Validation spec: {record.validation_spec_path}")

LifecycleError

class LifecycleError(RuntimeError)

Raised when a run directory cannot be loaded through the lifecycle contract. All lifecycle functions raise this exception type instead of generic ValueError or RuntimeError.

meridian_tools.artifacts

Manifest and JSON helpers for run artefact management.

Module: meridian_tools.artifacts

Functions

write_json

def write_json(path: str | Path, payload: Any) -> None

Write a JSON-serialisable payload to disk with UTF-8 encoding and 2-space indentation. Creates parent directories if they do not exist.


write_manifest

def write_manifest(path: str | Path, manifest: RunManifest) -> None

Serialise and write a RunManifest to disk as JSON using write_json.


normalize_artifact_paths

def normalize_artifact_paths(
    run_dir: str | Path,
    artifacts: Mapping[str, str | Path],
) -> dict[str, str]

Convert artefact paths to relative paths against run_dir so the manifest stores portable references.

Parameters:

  • run_dir — The run directory root.
  • artifacts — Mapping of artefact names to file paths.

Returns: Dictionary mapping artefact names to relative path strings.


timestamp_utc

def timestamp_utc() -> str

Return the current time as a UTC ISO-8601 string with second precision.


Classes

RunManifest

@dataclass
class RunManifest

Machine-readable summary of one meridian-tools run.

Attribute Type Default Description
run_name str required Human-readable run name.
config_path Path required Path to the authored YAML config file.
output_dir Path required Path to the run directory.
started_at str required UTC ISO-8601 start timestamp.
manifest_version int CURRENT_MANIFEST_VERSION Schema version (0, 1, 2, or 3).
status str "running" Overall run status: "running", "completed", or "failed".
finished_at str | None None UTC ISO-8601 finish timestamp. None while the run is in progress.
meridian_tools_version str __version__ Version of meridian-tools.
meridian_version str | None None Version of Google Meridian.
artifacts dict[str, str] {} Top-level artefact index. Key artefacts from stages are promoted here.
stages list[StageRecord] [] Ordered list of stage records (completed, skipped, and failed).

Class methods:

  • from_dict(payload: Mapping[str, Any]) -> RunManifest — Deserialise from a JSON-parsed dictionary. Supports manifest versions 0, 1, 2, and 3 with default values for missing fields in older versions. Raises ValueError for unsupported versions or missing required fields.

Instance methods:

  • to_dict() -> dict[str, Any] — Serialise to a JSON-compatible dictionary.

StageRecord

@dataclass
class StageRecord

One pipeline stage entry in the run manifest.

Attribute Type Default Description
name str required Stage identifier (for example, "00_run_metadata").
status str "pending" Stage status: "pending", "running", "completed", "skipped", or "failed".
started_at str | None None UTC ISO-8601 start timestamp.
finished_at str | None None UTC ISO-8601 finish timestamp.
elapsed_seconds float | None None Wall-clock seconds for stage execution.
message str | None None Human-readable message (skip reason or error detail).
artifacts dict[str, str] {} Map of artefact names to relative paths. Empty for skipped stages.

Class methods:

  • from_dict(payload: Mapping[str, Any]) -> StageRecord — Deserialise from a JSON-parsed dictionary. Raises ValueError if name is missing.

InputDataProvenance

@dataclass(frozen=True)
class InputDataProvenance

Pinned input-data provenance payload used by manifest version 3 runs.

Attribute Type Default Description
authored_path str required Exact data.path string from the source YAML.
resolved_path str required Absolute runtime path used for input loading.
sha256 str required SHA-256 digest of the resolved input file.
size_bytes int required Input file size in bytes.
mtime_utc str required Input file modification time in UTC ISO-8601 format.
row_count int required Number of CSV data rows.
column_count int required Number of CSV columns.
ordered_columns tuple[str, ...] required CSV header order.
provenance_version int INPUT_DATA_PROVENANCE_VERSION Payload schema version.

Class methods:

  • from_dict(payload: Mapping[str, Any]) -> InputDataProvenance — Validates the exact pinned Phase 09 key set and types.

Instance methods:

  • to_dict() -> dict[str, Any] — Serialise to the exact JSON payload written into input_data_provenance.json.

Constants

CURRENT_MANIFEST_VERSION

CURRENT_MANIFEST_VERSION: int = 3

SUPPORTED_MANIFEST_VERSIONS

SUPPORTED_MANIFEST_VERSIONS: tuple[int, ...] = (0, 1, 2, 3)

INPUT_DATA_PROVENANCE_VERSION

INPUT_DATA_PROVENANCE_VERSION: int = 1

REQUIRED_MANIFEST_ARTIFACTS

REQUIRED_MANIFEST_ARTIFACTS: tuple[str, ...] = (
    "config_resolved",
    "config_source",
    "input_data_provenance",
    "diagnostics_bundle",
)

These artefact entries are validated at run completion time by the runner. New runs must produce all four to complete successfully.

The lifecycle loader enforces config_source and config_resolved as required for all supported manifests. It also enforces input_data_provenance for manifest version 3 runs. diagnostics_bundle remains optional, so older or partial runs can still be loaded without it.