GeoSC Documentation

GeoSC is an end-to-end Python toolkit and installed CLI for planning, running, and interpreting geo-level marketing lift tests with synthetic-control methods. It supports data-driven market selection, power analysis, lift estimation, diagnostics, plots, and reporting from one reproducible workflow.

This docs tree is organised according to the Diataxis framework.

Choose Your Install Path

  • Start with Quick Start if you want the supported install paths and a first CLI run.
  • Use the installed geosc CLI as the primary operational interface.
  • Use repo-local example assets such as data-config/ only when you are working from a source checkout.
  • For maintainer release and artifact-building workflow, see the repository RELEASE.md.

Tutorials

Learning-oriented. Start here for the supported install modes, packaged vs source-checkout usage, and a first CLI run.

How-To Guides

Task-oriented. Step-by-step guides for achieving specific goals:

Reference

Information-oriented. Technical descriptions of the machinery and how to operate it:

Explanation & Methodology

Understanding-oriented. Deep dives into the mathematics, statistics, and methodology:

Subsections of GeoSC Documentation

Tutorials

Learning-oriented guides for getting GeoSC installed and running end to end.

Pages

  • Quick Start — supported install paths, packaged vs source-checkout usage, and a first CLI run.

Subsections of Tutorials

Quick Start Guide

Get GeoSC running quickly through the installed geosc CLI.

Installation

Option A: Install from a built artifact

Use this path if you are consuming a local release candidate or a maintainer- provided build outside the repo tree.

python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install /path/to/geosc-0.3.0-py3-none-any.whl
# or:
# pip install /path/to/geosc-0.3.0.tar.gz

Built artifacts install the package and the geosc CLI. They do not include the repo’s data-config/, recipes/, or shapemap/ directories, so packaged usage should point to your own YAML config files and input data.

Option B: Install from a source checkout

Use this path if you want the shipped example configs under data-config/ or you are developing locally.

git clone https://github.com/tandpds/GeoSC.git
cd GeoSC

python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
pip install -e .

Optional CuPy-backed acceleration is available only for unseeded power-analysis runs. Main SparseSC inference remains CPU-based.

pip install cupy-cuda12x

Your First Analysis

Step 1: Choose your config source

Packaged-install example:

geosc infer --config /path/to/geolift_analysis_config.yaml --create-plots

Source-checkout example with shipped demo configs:

GeoSC ships canonical example configs under data-config/ in the source tree:

  • power_analysis_config.yaml
  • donor_eval_config.yaml
  • geolift_analysis_config.yaml

For the full pipeline, point geosc pipeline at any one canonical YAML file from that directory. The recommended anchor is the inference config:

geosc pipeline --config data-config/geolift_analysis_config.yaml

Step 2: Inspect Outputs

The pipeline writes:

  • outputs/multicell_power_analysis/
  • outputs/multicell_donor_eval/
  • outputs/multicell_geolift_analysis/
  • outputs/geolift_pipeline_report.md
  • outputs/geolift_pipeline_report.html

If you override the pipeline root:

geosc pipeline \
  --config data-config/geolift_analysis_config.yaml \
  --output-dir results/demo_run

GeoSC preserves the same legacy stage/report names under results/demo_run/.

Step 3: Run Individual Stages When Needed

geosc power --config data-config/power_analysis_config.yaml --use-gpu --jobs -1
geosc donors --config data-config/donor_eval_config.yaml --jobs -1
geosc infer --config data-config/geolift_analysis_config.yaml --create-plots

Useful pipeline selectors:

geosc pipeline --config data-config/geolift_analysis_config.yaml --only-inference
geosc pipeline --config data-config/geolift_analysis_config.yaml --skip-power
geosc pipeline --config data-config/geolift_analysis_config.yaml --no-report

Command Notes

  • --config is required on every command.
  • On pipeline, --config is a config-directory anchor through one canonical YAML path, not a separate pipeline schema.
  • --jobs is meaningful for power and donor evaluation.
  • --use-gpu requests optional CuPy-backed power acceleration only; seeded power runs use CPU simulation paths for reproducibility, and inference remains CPU-based.
  • --data, --create-plots, and --no-create-plots are infer-only flags.

Compatibility Note

python runme.py still works as a compatibility wrapper. The preferred path is:

geosc pipeline --config data-config/geolift_analysis_config.yaml

Legacy recipe scripts remain usable for migration, but they are not the primary quick-start path anymore.

Next Steps

Subsections of How-To Guides

How to Evaluate Donor Quality

Selecting the right control (donor) pool is critical for Synthetic Control methods. This guide shows how to evaluate potential donors.

Running Donor Evaluation via CLI

Packaged-install example:

geosc donors --config /path/to/donor_eval_config.yaml --jobs -1

Source-checkout example with the shipped demo config:

geosc donors --config data-config/donor_eval_config.yaml --jobs -1

Built wheel and sdist installs do not include data-config/, so packaged users should provide their own YAML path.

Expected Outputs

  • donor_eval_results.csv: Ranks potential donor regions based on correlation, RMSE, MAPE, and DTW (Dynamic Time Warping) against the treatment unit(s) during the pre-treatment period. Includes quality band, warning flags, per-treatment rank, and normalized recommendation weight.
  • donor_pool_quality.json: Summarizes recommended donor-pool quality and concentration.
  • donor_map_*.png: Visualizations mapping the geographical distribution and quality of the donors (if shapefiles are configured).

selected_weight is a design-stage recommendation weight, not the final SparseSC donor weight used during treatment-effect estimation.

For geography setup, including UK postcode area and postcode district examples, see How to Use Shapemaps.

How to Run a Power Analysis

This guide explains how to use GeoSC to evaluate test strength before running an experiment.

Running Power Analysis via CLI

Use the power command to simulate experiments with known treatment effects and calculate the statistical power:

Packaged-install example:

geosc power --config /path/to/power_analysis_config.yaml --use-gpu --jobs -1

Source-checkout example with the shipped demo config:

geosc power --config data-config/power_analysis_config.yaml --use-gpu --jobs -1

Key Flags

  • --config: Path to the YAML configuration file.
  • --use-gpu: (Optional) Accelerates computation using compatible GPUs.
  • --jobs -1: Uses all available CPU cores for parallel processing.

Built wheel and sdist installs do not include data-config/, so packaged users should provide their own YAML path.

Expected Outputs

  • power_analysis_results.csv: Detailed grid of simulated lifts, sample sizes, power, Monte Carlo uncertainty, simulation failures, seed, DGP rank, feasible placebo support, and SparseSC/backend parameters.
  • power_curves.png: Visualization of the power curve across different minimal detectable effects (MDE).

Rows with valid: false exceeded the configured simulation failure tolerance or had no successful simulations. Do not use those rows for feasibility decisions. Seeded runs use CPU generation for reproducible simulation draws, so the shipped demo config sets use_gpu: false.

Power DGP

The factor-model power DGP is estimated from pre-treatment outcomes with SVD. GeoSC then estimates VAR(1) dynamics on the extracted factor series and uses those temporal parameters when simulating post-treatment periods. This preserves shared cross-sectional movement through the factor loadings and lag-1 temporal dependence through the VAR(1) transition and innovation covariance.

Covariance matrices are symmetrized and regularized with a small diagonal floor when needed. If VAR(1) dynamics or factor draws cannot be estimated safely, power analysis fails with an actionable error instead of silently switching to a diagonal-covariance DGP. Legacy direct calls to the generator without VAR(1) metadata still use independent factor draws from the supplied F_mean and F_cov.

Current VAR(1) post-period generation runs on CPU so seeded and unseeded power simulations preserve the same temporal contract. GPU acceleration may still be used for eligible DGP estimation, but generated post-period panels from the VAR(1) DGP are produced by NumPy.

Design Validity and Plot Filtering

The power calculator fails fast at construction with a ValueError if the design has no treatment units, or if there are fewer control units than treatment units. In that case placebo inference is infeasible and no power analysis is run.

plot_power_curves filters rows where valid is false out of the decision plots (logging the number of excluded rows), and skips plot creation entirely if no valid rows remain. Inspect power_analysis_results.csv directly to see why rows were marked invalid (high failure_rate, zero n_successful, etc.).

Reproducibility

When random_seed is set, repeated runs of the same config produce identical power_analysis_results.csv content (excluding timestamp/path metadata), in both sequential and parallel modes. This is covered by both unit and CLI integration tests.

How to Run Causal Inference

This guide explains how to measure lift (the actual causal effect) post-experiment using SparseSC.

Running Inference via CLI

Packaged-install example:

geosc infer --config /path/to/geolift_analysis_config.yaml --create-plots

Source-checkout example with the shipped demo config:

geosc infer --config data-config/geolift_analysis_config.yaml --create-plots

Key Flags

  • --data: (Optional) Override the CSV path specified in the YAML.
  • --create-plots / --no-create-plots: Toggle generation of visualization artifacts.

Built wheel and sdist installs do not include data-config/, so packaged users should provide their own YAML path.

Expected Outputs

  • geolift_results.json: Top-level statistical results including status, ATT, p-value, and confidence interval metadata.
  • geolift_diagnostics.json: Schema-shaped diagnostic metrics for the synthetic control fit. Raw synthetic-control frames and raw weight matrices are not persisted.
  • data_validation.json: Input contract report covering duplicate rows, raw outcome value checks, input/model panel missingness, finite-value checks, and any explicit handling policy.
  • assumption_validation.json: Gate-eligible parallel-trends diagnostic and advisory interference-screen report, or a skipped-status artifact when checks are disabled.
  • uplift_timeseries.png: Visual comparison of the treatment unit vs. the synthetic control over time.

Check status before interpreting lift. A partial result means the estimator returned some core quantities but uncertainty fields are incomplete. A failed result causes the CLI to exit non-zero. Also check assumption_status: skipped means interpretability checks were not run; fail or error means the causal interpretation needs review even if the effect estimate exists.

How to Run the End-to-End Pipeline

This guide explains how to orchestrate the full GeoSC workflow using the installed geosc pipeline command.

Prerequisites

  • A prepared dataset (CSV format)
  • A configuration YAML file
  • GeoSC installed

Running the Pipeline

Packaged-install example:

geosc pipeline --config /path/to/geolift_analysis_config.yaml

Source-checkout example with the shipped demo configs:

geosc pipeline --config data-config/geolift_analysis_config.yaml

Pipeline Config Behaviour

  • --config must point to one canonical stage YAML file
  • in a source checkout, GeoSC resolves the sibling canonical YAMLs from the same directory
  • GeoSC does not use a separate pipeline_config.yaml
  • built artifacts do not include the repo’s data-config/; packaged users should supply their own YAML path instead

Stage Selection

You can run partial pipelines by skipping specific stages:

  • --skip-power: Skips the power analysis stage
  • --skip-donor: Skips the donor evaluation stage
  • --only-inference: Runs only the final inference stage

Report Generation

Control report generation with:

  • --report (enabled by default)
  • --no-report

Output Directory

Specify where results should be saved:

geosc pipeline \
  --config data-config/geolift_analysis_config.yaml \
  --output-dir results/campaign_a

This will generate the following structure:

  • results/campaign_a/multicell_power_analysis/
  • results/campaign_a/multicell_donor_eval/
  • results/campaign_a/multicell_geolift_analysis/
  • results/campaign_a/geolift_pipeline_report.md
  • results/campaign_a/geolift_pipeline_report.html

For a source-tree walkthrough using the shipped demo configs, start with Quick Start.

How to Use Shapemaps

GeoSC donor maps use shapemap_file to read a GeoPandas-compatible vector file and shapemap_id_column to choose the geography ID used for joins. The values in that ID column must match the unit IDs used in your experiment data, such as DMA IDs, postcode districts, postcode areas, or another market key.

Built wheel and sdist installs do not include the repository-local shapemap/ directory. Packaged users should provide map files from their own runtime environment.

Basic Config

shapemap_file: "/path/to/markets.geojson"
shapemap_id_column: "id"

GeoSC can read formats supported by GeoPandas, including GeoJSON, TopoJSON, Shapefile, and GeoPackage inputs when the local geospatial stack supports them.

Repository-local shapemap examples are available for common markets:

  • shapemap/uk/postcode_areas_approx_2026.geojson
  • shapemap/uk/postcode_districts_approx_2026.geojson
  • shapemap/us/dma_legacy_normalized.geojson
  • shapemap/fr/nuts3_2024.geojson
  • shapemap/de/nuts3_2024.geojson
  • shapemap/<uk|us|fr|de>/meta_commuting_zones.geojson

These are source-checkout assets. They are not included in built wheel or sdist artifacts.

Meta Commuting Zones are also available as optional geographies for Meta-aligned study design. They are commuting/economic zones, not Meta ad-targeting boundaries.

UK Postcode District Example

For UK production workflows, postcode district is often the practical market geography:

location_col_name: "postcode_district"
treatment_locations: ["SW1A", "M1"]
shapemap_file: "/secure/maps/uk_postcode_districts.geojson"
shapemap_id_column: "id"

The input data must use the same IDs as the shapemap:

postcode_district,date,Y
SW1A,01/01/2025,125.3
SW1A,02/01/2025,128.7
M1,01/01/2025,98.2

Use scripts/prepare_uk_postcode_shapemap.py from a source checkout to normalize licensed or user-supplied postcode polygon data:

python scripts/prepare_uk_postcode_shapemap.py \
  --input /path/to/licensed-postcode-polygons.gpkg \
  --output shapemap/uk/postcode_districts.geojson \
  --level district \
  --postcode-column postcode \
  --metadata-output shapemap/uk/postcode_districts.metadata.json

The generated file exposes:

  • id: postcode district, such as SW1A, M1, or BT7
  • name: display name, defaulting to the same value as id
  • country: UK
  • geography_level: postcode_district
  • geometry: dissolved polygon geometry

The repository includes an approximate version built from ONSPD live postcode centroids:

location_col_name: "postcode_district"
treatment_locations: ["SW1A", "M1"]
shapemap_file: "shapemap/uk/postcode_districts_approx_2026.geojson"
shapemap_id_column: "id"

Validate the generated file before using it in donor evaluation:

python scripts/validate_shapemap.py \
  --shapemap shapemap/uk/postcode_districts.geojson \
  --id-column id \
  --expected-ids SW1A,M1 \
  --json

UK Postcode Area Example

For broader UK markets, use postcode areas:

location_col_name: "postcode_area"
treatment_locations: ["SW", "M"]
shapemap_file: "/secure/maps/uk_postcode_areas.geojson"
shapemap_id_column: "id"

Prepare the map with:

python scripts/prepare_uk_postcode_shapemap.py \
  --input /path/to/licensed-postcode-polygons.gpkg \
  --output shapemap/uk/postcode_areas.geojson \
  --level area \
  --postcode-column postcode

Provenance and Licensing

Do not commit or distribute postcode geometry unless the source license permits that use. The repository records shapemap provenance policy in shapemap/README.md and shapemap/manifest.yaml.

For UK data:

  • OS Code-Point Open is an open postcode unit point dataset, not polygon geometry.
  • OS Code-Point with Polygons is the appropriate official polygon source when you have the necessary access and licensing.
  • ONS Postcode Directory is useful for postcode-to-geography lookup workflows.

Troubleshooting

If donor maps do not highlight treatment or donor units:

  • confirm the shapemap contains the configured shapemap_id_column
  • confirm the shapemap ID values exactly match the experiment unit IDs
  • normalize both sides to the same case and spacing convention
  • confirm the file can be opened by geopandas.read_file

scripts/validate_shapemap.py reports missing expected IDs, duplicate IDs, blank IDs, null or empty geometry, invalid geometry, row counts, unique ID counts, and the detected CRS.

How to Use the Python API

While the CLI is the preferred operational path, the Python API supports programmatic control and direct-data initialization without CSV/YAML files.

Basic Usage

Packaged-install example:

from geolift import GeoLiftAnalyzer

analyzer = GeoLiftAnalyzer(
    config_path="/path/to/geolift_analysis_config.yaml",
    data_path="/path/to/input.csv",
)

results = analyzer.run_analysis()

print(f"ATT: {results['att']}")
print(f"P-value: {results['p_value']}")

Source-checkout example with the shipped demo configs:

from geolift import GeoLiftAnalyzer

analyzer = GeoLiftAnalyzer(
    config_path="data-config/geolift_analysis_config.yaml",
    data_path="data-config/synthetic_geolift_multi.csv",
)

Subsections of Reference

API Reference

Technical reference for the shipped GeoSC interfaces.

Interface Hierarchy

Primary operational interface:

  • installed geosc CLI and compatibility geolift CLI

Programmatic interface:

  • GeoLiftAnalyzer
  • load_config
  • load_and_prepare_data
  • process_sparse_sc_results

Compatibility interfaces:

  • runme.py
  • recipes/

For the exact CLI command surface, see CLI Reference.

Installed CLI

Installed commands:

  • geosc power
  • geosc donors
  • geosc infer
  • geosc pipeline

Shared flags on all commands:

  • --config
  • --output-dir
  • --jobs
  • --use-gpu
  • --no-progress
  • --quiet

Infer-only flags:

  • --data
  • --create-plots
  • --no-create-plots

Pipeline-only flags:

  • --skip-power
  • --skip-donor
  • --only-inference
  • --report
  • --no-report

Pipeline config contract:

  • pipeline --config accepts one canonical stage YAML path
  • GeoSC resolves sibling canonical YAMLs from the same directory
  • there is no separate pipeline config schema

Core Classes

GeoLiftAnalyzer

Main orchestration class for causal inference analysis using SparseSC-backed estimation.

Initialization modes:

  1. File-based: config_path plus data_path
  2. Direct data: prepared outcomes_df plus unit_treatment_periods

File-based example with user-owned config and data paths:

from geolift import GeoLiftAnalyzer

analyzer = GeoLiftAnalyzer(
    config_path="/path/to/geolift_analysis_config.yaml",
    data_path="/path/to/input.csv",
)

Source-checkout example:

from geolift import GeoLiftAnalyzer

analyzer = GeoLiftAnalyzer(
    config_path="data-config/geolift_analysis_config.yaml",
    data_path="data-config/synthetic_geolift_multi.csv",
)

Direct-data example:

import pandas as pd
from geolift import GeoLiftAnalyzer

outcomes_df = pd.DataFrame(...)
unit_treatment_periods = pd.Series(...)

analyzer = GeoLiftAnalyzer(
    outcomes_df=outcomes_df,
    unit_treatment_periods=unit_treatment_periods,
    intervention_date="2023-06-01",
    config={
        "estimator": "sparsesc",
        "sparse_sc_model_type": "retrospective",
        "sparse_sc_max_n_pl": 1000,
        "sparse_sc_placebo_seed": 110011,
        "sparse_sc_level": 0.95,
    },
)

run_geolift_analysis()

Executes the full inference workflow and returns the compact top-level results dictionary.

Returned fields include:

  • schema_version
  • status
  • att
  • p_value
  • p_value_method
  • n_placebos
  • max_n_placebos
  • possible_placebos
  • p_value_placebo_mode
  • placebo_seed
  • ci_lower
  • ci_upper
  • ci
  • se
  • se_available
  • se_approx_from_ci
  • se_approx_available
  • se_approximation_method
  • significant
  • significance_available
  • assumption_status
  • assumption_required
  • assumption_checks_run
  • assumption_checks_completed

Detailed diagnostics remain on analyzer.diagnostics. Assumption validation is available on analyzer.assumption_validation and, when output_dir is set, in assumption_validation.json.

analyzer.diagnostics["inference"] records the estimator method, placebo mode, seed, requested placebo count, possible combinations, and effective placebo count. The seed is material only when the placebo mode is sampled.

For the 0.3.0 beta, these public wrapper semantics are release-certified by the statistical certification gate in scripts/verify_release_candidate.py. That gate covers SparseSC parity, deterministic effect recovery, timing/orientation, artifact parity, uncertainty and placebo metadata, donor design-stage semantics, power failure accounting, assumption-validator status metadata, and release-preflight schema regressions. It does not certify that a specific campaign satisfies causal-design assumptions.

run_analysis()

Primary convenience entry point that delegates to run_geolift_analysis(). Either method is valid on the shipped class.

plot_results(output_path: str | None = None) -> str

Generates and saves the uplift time-series plot. When output_path is omitted, the plot is written to <output_dir>/uplift_timeseries.png.

Utility Functions

load_and_prepare_data(...)

from geolift.data_handler import load_and_prepare_data

File-based data preparation helper used by the analyser path.

load_config(path)

from geolift.config_manager import load_config

Loads one YAML config file.

process_sparse_sc_results(sparse_sc_results, config)

from geolift.results_processor import process_sparse_sc_results

Transforms SparseSC outputs into the compact top-level results plus the richer diagnostics payload.

Output Artifacts

Inference outputs:

  • geolift_results.json
  • geolift_diagnostics.json
  • data_validation.json
  • assumption_validation.json
  • uplift_timeseries.png when plot generation is enabled

Power outputs:

  • power_analysis_results.csv
  • power_curves.png

Power CSV rows include reproducibility and failure fields such as random_seed, n_failed, failure_rate, valid, dgp_rank, and requested_max_n_pl, possible_placebos, effective_max_n_pl, dgp_backend, and generation_backend.

Donor outputs:

  • donor_eval_results.csv
  • donor_pool_quality.json
  • donor_map_*.png

Pipeline reports:

  • geolift_pipeline_report.md
  • geolift_pipeline_report.html

Compatibility Surfaces

runme.py and the scripts in recipes/ remain available for compatibility. They are not the primary documented interface.

Migration map:

  • source-checkout: python runme.py -> geosc pipeline --config data-config/geolift_analysis_config.yaml
  • source-checkout: python recipes/power_calculator_sparsesc.py ... -> geosc power ...
  • source-checkout: python recipes/donor_evaluator.py ... -> geosc donors ...
  • source-checkout: python recipes/geolift_multi_cell.py ... -> geosc infer ...

CLI Reference

Canonical reference for the installed geosc command surface. The legacy geolift command remains available as a compatibility alias during the beta.

Examples below use source-tree config paths such as data-config/... when that helps illustrate the shipped demo workflow. Built wheel and sdist installs do not include data-config/; packaged users should supply their own YAML config paths.

Top-Level Command

geosc --help

Installed subcommands:

  • geosc power
  • geosc donors
  • geosc infer
  • geosc pipeline

Shared Flags

All commands accept:

  • --config CONFIG
  • --output-dir OUTPUT_DIR
  • --jobs JOBS
  • --use-gpu
  • --no-progress
  • --quiet

Semantics:

  • --config points to the YAML configuration file for the command
  • --output-dir overrides the configured output directory for stage commands
  • --jobs overrides n_jobs
  • --use-gpu is meaningful for power analysis
  • --no-progress disables progress output
  • --quiet reduces non-critical output without hiding errors

geosc power

Source-checkout example:

geosc power --config data-config/power_analysis_config.yaml --use-gpu --jobs -1

Purpose:

  • run GeoSC power-analysis workflows through the installed CLI

Typical outputs:

  • power_analysis_results.csv
  • power_curves.png

power_analysis_results.csv includes failure_rate and valid; invalid rows should not be used for recommendations.

geosc donors

Source-checkout example:

geosc donors --config data-config/donor_eval_config.yaml --jobs -1

Purpose:

  • run donor-evaluation workflows through the installed CLI

Typical outputs:

  • donor_eval_results.csv
  • donor_pool_quality.json
  • donor_map_*.png

geosc infer

Packaged-install example:

geosc infer --config /path/to/geolift_analysis_config.yaml --create-plots

Source-checkout example:

geosc infer --config data-config/geolift_analysis_config.yaml --create-plots

Infer-only flags:

  • --data DATA
  • --create-plots
  • --no-create-plots

Semantics:

  • --data overrides the CSV path defined in YAML
  • --create-plots forces plot generation
  • --no-create-plots disables plot generation

Typical outputs:

  • geolift_results.json
  • geolift_diagnostics.json
  • uplift_timeseries.png when plots are enabled

geosc pipeline

Packaged-install example:

geosc pipeline --config /path/to/geolift_analysis_config.yaml

Source-checkout example:

geosc pipeline --config data-config/geolift_analysis_config.yaml

Pipeline-only flags:

  • --skip-power
  • --skip-donor
  • --only-inference
  • --report
  • --no-report

Pipeline config contract:

  • --config is an anchor to one canonical stage YAML file
  • in a source checkout, GeoSC resolves sibling canonical YAMLs from the same directory:
    • power_analysis_config.yaml
    • donor_eval_config.yaml
    • geolift_analysis_config.yaml
  • there is no pipeline --stage
  • there is no separate pipeline config schema

Stage-selection semantics:

  • --skip-power skips power analysis
  • --skip-donor skips donor evaluation
  • --only-inference skips both earlier stages

Report semantics:

  • reports are generated by default
  • --report forces report generation explicitly
  • --no-report suppresses report generation

Output-root semantics:

geosc pipeline \
  --config data-config/geolift_analysis_config.yaml \
  --output-dir results/campaign_a

This preserves the legacy stage/report layout under the supplied root:

  • results/campaign_a/multicell_power_analysis/
  • results/campaign_a/multicell_donor_eval/
  • results/campaign_a/multicell_geolift_analysis/
  • results/campaign_a/geolift_pipeline_report.md
  • results/campaign_a/geolift_pipeline_report.html

Migration From Legacy Entry Points

Legacy source-checkout command Preferred command
python runme.py geosc pipeline --config data-config/geolift_analysis_config.yaml
python recipes/power_calculator_sparsesc.py --config data-config/power_analysis_config.yaml --use-gpu --jobs -1 geosc power --config data-config/power_analysis_config.yaml --use-gpu --jobs -1
python recipes/donor_evaluator.py --config data-config/donor_eval_config.yaml geosc donors --config data-config/donor_eval_config.yaml
python recipes/geolift_multi_cell.py --config data-config/geolift_analysis_config.yaml geosc infer --config data-config/geolift_analysis_config.yaml

runme.py remains available as a compatibility wrapper. recipes/ remain available for migration and legacy stage-specific workflows.

Configuration Reference

GeoSC uses flat YAML keys for its configuration.

The example below reflects the supported YAML shape. The data-config/ directory is a source-tree example asset, not a packaged runtime asset, so built wheel and sdist installs should replace those demo paths with user-owned config and data paths.

Inference Config Parameters

Below are the standard configuration keys used in geolift_analysis_config.yaml:

schema_version: "1.0"
data_path: "data-config/synthetic_geolift_multi.csv"
location_col_name: "location"
date_col_name: "date"
date_format: "%d/%m/%Y"
duplicate_policy: "error"
missing_outcome_policy: "error"
outcome_col_name: "Y"
treatment_unit_ids: [501, 502, 503]
intervention_date_str: "01/03/2023"
estimator: "sparsesc"
cooldown_periods: 0
# measurement_start_date_str: "08/03/2023"  # Optional; must agree with cooldown_periods if set.
covariate_col_names: ["covariate_1", "covariate_2"]
sparse_sc_model_type: "retrospective"
sparse_sc_fast_estimation: true
sparse_sc_level: 0.95
sparse_sc_return_ci: true
sparse_sc_max_n_pl: 1000
sparse_sc_placebo_seed: 110011
min_pre_treatment_periods: 12
min_post_treatment_periods: 1
run_assumption_checks: false
require_assumption_checks: false
fail_on_assumption_error: true
parallel_trends_method: "regression"
spillover_method: "correlation"
assumption_alpha: 0.05
output_dir: "outputs/multicell_geolift_analysis"
create_plots: true

Cooldown And Measurement Window

cooldown_periods excludes an initial post-launch transition window from effect measurement. It defaults to 0, which preserves the existing behavior where the measurement window starts on intervention_date_str.

Cooldown counts observed data periods, not calendar days. For daily data, cooldown_periods: 7 excludes seven daily observations. For weekly data, cooldown_periods: 2 excludes two weekly observations.

When cooldown is positive:

  • pre-treatment fit uses dates before intervention_date_str;
  • cooldown dates are excluded from the estimator-facing panel;
  • measurement post-periods start at the resolved measurement start date;
  • min_post_treatment_periods is checked after cooldown exclusion.

You may supply measurement_start_date_str explicitly, but if it is supplied with cooldown_periods, both settings must resolve to the same observed period. Positive cooldown shortens the measurement window and can reduce power.

sparse_sc_T0 and sparse_sc_T1 are SparseSC history-length settings. They are not cooldown controls.

All file-based and direct-data panels are canonicalised before validation. Date labels become a sorted, timezone-naive DatetimeIndex; malformed or timezone-aware labels, duplicate periods after parsing, and treatment dates absent from the outcome panel are rejected rather than coerced.

Estimator Identity

estimator: "sparsesc" is the only supported estimator selector and is also the default when the key is omitted. Historical values sdid, sc, did, and regsynth all executed this same SparseSC path; they are now rejected because their names implied implementations that were not present. Replace any such value with estimator: "sparsesc".

SparseSC Runtime Keys

GeoSC validates keys that start with sparse_sc_ so misspellings fail before the adapter falls back to defaults. Supported SparseSC runtime keys are:

  • sparse_sc_model_type
  • sparse_sc_fast_estimation
  • sparse_sc_return_ci
  • sparse_sc_T0
  • sparse_sc_T1
  • sparse_sc_max_n_pl
  • sparse_sc_placebo_seed
  • sparse_sc_level
  • sparse_sc_lasso_max_iter
  • sparse_sc_lasso_tol
  • sparse_sc_cv_folds
  • sparse_sc_scoring
  • sparse_sc_gcv_mode

sparse_sc_placebo_seed defaults to 110011 and must be a non-negative integer. It controls only the sampled-placebo branch used when sparse_sc_max_n_pl caps the feasible placebo combinations. Exhaustive enumeration is unchanged. Results persist the seed, placebo mode, requested count, possible count, and effective count so sampled inference can be replayed.

Existing non-prefixed lasso options such as lasso_selection, lasso_fit_intercept, and lasso_normalize keep their legacy behavior and are not part of the sparse_sc_* allowlist.

Data Contract Policies

Inference defaults are fail-closed:

  • duplicate_policy: "error" rejects duplicate location-date rows. Use "mean" or "sum" only when that aggregation is statistically intended.
  • missing_outcome_policy: "error" rejects incomplete outcome panels. Explicit alternatives are "drop_unit", "drop_period", and "impute_with_report".

Every inference run writes data_validation.json next to the result artifacts. Review it before interpreting lift. The artifact includes raw outcome value checks plus separate input-panel and model-panel quality summaries, so missing values hidden by explicit duplicate aggregation remain auditable.

Inference also writes geolift_results.json with schema_version and status. Treat ok as “the configured contract completed”, not as a proof of causal validity. Treat partial as “estimate available but interpretation metadata incomplete”, and treat failed as unusable for decision-making.

Assumption Gates

  • run_assumption_checks: false skips the parallel-trends diagnostic and advisory interference screen, but still writes assumption_validation.json with overall_status: "skipped".
  • require_assumption_checks: true makes only the gate-eligible parallel-trends diagnostic a hard result gate. A failed gate marks geolift_results.json as status: "failed".
  • fail_on_assumption_error: true treats parallel-trends validator exceptions as failed gates when checks are run. An interference-screen exception is recorded as indeterminate and remains advisory.
  • parallel_trends_method supports "placebo", "regression", and "visual".
  • spillover_method supports "correlation", "change", and "visual" for the advisory interference screen.

The interference screen reports signal, no_signal, or indeterminate and adds a result caveat where appropriate; it never changes result status or significance. Neither a signal nor no signal identifies treatment exposure or proves the presence or absence of spillover. A future hard interference gate would need a pre-specified exposure, network, or geographical-interference design; GeoSC does not implement one.

Power Config Parameters

Power analysis accepts the demo keys in data-config/power_analysis_config.yaml, including:

random_seed: 12345
alpha: 0.05
n_simulations: 300
sparse_sc_max_n_pl: 100
power_dgp_rank: null
power_dgp_max_rank: 5
power_failure_rate_threshold: 0.2
effect_pattern: "constant"
use_gpu: false

random_seed makes repeated runs with the same config reproducible. Each power_analysis_results.csv row records n_failed, failure_rate, valid, random_seed, simulation_seed, dgp_rank, dgp_explained_var, requested_max_n_pl, possible_placebos, effective_max_n_pl, dgp_backend, generation_backend, seeded_reproducibility_mode, and effect_pattern. Rows with valid: false exceeded the configured failure tolerance or had no successful simulations. power_failure_rate_threshold must be a finite number in [0, 1]; non-finite values are rejected at construction. When random_seed is set, GPU DGP estimation and GPU post-period generation are disabled so seeded CPU simulation draws are reproducible. The shipped demo config sets use_gpu: false to match this seeded reproducibility default.

The factor-model DGP supports SVD extraction only. power_dgp_rank controls the number of SVD factors, and power_dgp_max_rank caps automatic rank selection. GeoSC estimates VAR(1) dynamics on the selected factor series and stores the transition, intercept, innovation covariance, and initial factor distribution in the DGP metadata used for simulation. Automatic rank selection applies an additional effective cap so the selected rank is estimable by the VAR(1) regression on short pre-period panels. Explicit power_dgp_rank values that cannot support VAR(1) estimation fail with an actionable error instead of silently switching DGP families. Unsupported extraction methods such as pca are rejected rather than treated as aliases.

Generated post-period panels use the VAR(1) temporal parameters whenever they are present. Covariance matrices are symmetrized and regularized with a small diagonal floor; invalid factor dynamics or failed factor draws raise errors instead of falling back to diagonal covariance. Because the current GPU path does not implement the VAR(1) generator contract, post-period generation for estimator-produced power DGPs is reported as cpu_numpy_generator even when GPU SVD estimation was requested and available. Power values are Monte Carlo estimates, not guarantees; compare them with power_ci_lower, power_ci_upper, n_simulations, and failure_rate.

If a power-analysis design has fewer control units than treatment units the calculator now fails fast at construction with a ValueError rather than forwarding an infeasible placebo count to SparseSC. plot_power_curves filters rows with valid: false out of the decision plots (logging the count of excluded rows) and skips plot creation entirely when no valid rows remain.

Donor Config Parameters

Donor evaluation accepts donor_dominance_threshold in addition to the existing metric thresholds. donor_eval_results.csv includes rank, quality_band, warning_flags, and selected_weight. selected_weight is a normalized recommendation weight within the ranked donor set; it is not a fitted SparseSC weight. The stage also writes versioned donor_pool_quality.json with threshold settings, warning-flag counts, selected-weight concentration, effective donor count, and by-treatment quality summaries.

The maintained artifact contract is enforced by release preflight and described in the output sections of API Reference, Interpreting Results, and the stage-specific how-to guides.

Shapemap Keys

Donor maps use:

shapemap_file: "/path/to/markets.geojson"
shapemap_id_column: "id"

shapemap_file may point to any GeoPandas-readable vector file. The configured shapemap_id_column must contain IDs that exactly match the treatment and donor unit IDs in the analysis data. For UK production workflows, postcode district IDs such as SW1A or postcode area IDs such as SW can be used when the data and shapemap are both normalized to the same geography. See How to Use Shapemaps for examples.

Release Certification Boundary

scripts/verify_release_candidate.py includes a named statistical certification gate for the 0.3.0 beta. The gate runs the maintained wrapper tests for SparseSC parity, effect recovery, treatment timing and orientation, artifact parity, result uncertainty and placebo semantics, donor semantics, power semantics, assumption-validator metadata, and release-preflight schema regressions.

Passing the gate means the tested GeoSC wrapper contract is intact for the declared SparseSC operational surface. It does not prove donor exchangeability, absence of spillovers, correct campaign timing, outcome suitability, credible power DGP assumptions, or acceptable placebo p-value granularity for a specific measurement decision.

Vendored Dependencies Policy

This project vendors parts of third-party libraries to ensure stability, provenance, and reproducibility of core methods.

SparseSC (Microsoft)

  • Source: https://github.com/microsoft/SparseSC
  • Vendored Path: src/sparsesc/
  • Pinned upstream commit: 3b4d2ebd87b41fc8e0ec3e97fcb2be1b689275c0
  • Status: src/sparsesc/ is a governed integration fork of Microsoft’s SparseSC.
  • Technical provenance: See src/sparsesc/PROVENANCE.md for upstream commit, local modifications, coupling notes, and update procedure.
  • Change Policy: Modifications to src/sparsesc/ must be documented in src/sparsesc/PROVENANCE.md with rationale and verification impact.
  • Tooling: Excluded from formatting, linting, and typing checks (see pyproject.toml).
  • Release certification: GeoSC validates the first-party wrapper boundary around vendored SparseSC through the statistical certification gate in scripts/verify_release_candidate.py. The gate checks adapter/result parity, effect recovery, timing/orientation, artifact schemas, uncertainty and placebo metadata, and related advisory diagnostics. It does not treat passing wrapper tests as proof that a specific campaign satisfies SparseSC’s causal design assumptions.
  • Known cleanup candidate: src/sparsesc/utils/AzureBatch/ remains part of the governed vendored SparseSC fork. Removing or pruning it requires a separate SparseSC provenance-backed phase with documented rationale, verification impact, and an update to src/sparsesc/PROVENANCE.md.

synthdid (if retained)

  • Vendored Path: src/synthdid/
  • Status: Not used by the primary GeoLiftAnalyzer workflow.
  • Recommendation: Either remove it entirely, or expose it behind a stable adapter interface and document its usage with tests.
  • Tooling: Excluded from formatting, linting, and typing checks.

Update Procedure

  1. Keep a file-local provenance record for each vendored dependency.
  2. Verify unit, integration, and performance tests pass after a refresh.
  3. Update pyproject.toml exclusion lists only if vendored paths change.
  4. Document any intentional local modifications in the dependency-specific provenance file.

Subsections of Explanation

Comparison to Other Geo-Experiment Libraries

GeoSC is an end-to-end Python toolkit for planning, running, and interpreting geo-level marketing lift tests with SparseSC-backed synthetic control. It helps teams choose markets with data-driven donor evaluation and power analysis, estimate incremental impact, and produce decision-ready diagnostics, plots, and artifacts. It overlaps with several other geo-experiment libraries, but it is not a drop-in replacement for all of them.

This page is a methodology-level guide, not a live feature matrix. Upstream libraries change. Before choosing a tool for a production measurement program, check the current upstream documentation, licence terms, and supported runtime surface.

Quick Positioning

Library family Typical counterfactual Inference style Main fit
GeoSC Sparse synthetic control from individual donor geos SparseSC CIs when available, plus donor-pool placebo diagnostics Python-first geo-lift workflows with market selection, power analysis, explicit artifacts, and donor diagnostics
Google geo-experiment tools, including GeoX/TBR/GBR/matched markets Regression or matched-market designs using treatment/control aggregates or matched groups Regression/Bayesian-style TBR uncertainty, depending on workflow Regression-based geo experiments, matched-market design, and budget-constrained assignment
Meta GeoLift Augmented synthetic control with prognostic outcome models Conformal/placebo-style synthetic-control inference, depending on settings End-to-end R workflow for market selection, power, GeoLift analysis, and plotting
General Bayesian causal-impact tools Structural time-series or Bayesian synthetic-control model Posterior intervals from the fitted model Time-series impact analysis when a Bayesian model is the desired estimand

GeoSC vs Google GeoX / TBR / GBR

Google’s geo-experiment literature and tools include time-based regression (TBR), geo-based regression (GBR), matched-market design, and related GeoX workflows. These methods are useful when the analyst wants an explicitly regression-based design and can defend the model form.

GeoSC differs in the counterfactual construction:

  • Google TBR/GBR workflows model treatment outcomes from aggregate or matched control signals through a regression-style model.
  • GeoSC constructs a counterfactual as a SparseSC-weighted combination of individual donor geographies.
  • TBR/GBR diagnostics are naturally regression diagnostics: residual behaviour, model fit, and stability of the treatment-control relationship.
  • GeoSC diagnostics emphasize donor comparability, pre-period synthetic-control fit, selected donor concentration, placebo granularity, and artifact status.

Operationally, this means GeoSC is usually more inspectable at the donor level: the analyst can review which donor geos were recommended or influential, whether the donor pool is concentrated, and whether placebo p-values are coarse. That does not make GeoSC automatically safer. If the donor pool is small, non-comparable, or exposed to spillovers, a sparse synthetic control can still be misleading.

Use Google-style TBR/GBR or matched-market tools when:

  • your organization already uses the Google geo-experiment workflow;
  • regression assumptions and aggregate controls are acceptable for the decision;
  • assignment constraints and matched-market design are the main problem;
  • you need outputs that align with existing Google measurement or Meridian calibration processes.

Use GeoSC when:

  • individual donor geography diagnostics matter;
  • you want a Python package and CLI with YAML-backed runs;
  • you need explicit JSON/CSV artifacts for validation and audit;
  • you want SparseSC interpolation rather than an aggregate regression counterfactual.

GeoSC vs Meta GeoLift

Meta GeoLift and GeoSC are closer methodologically than TBR/GBR because both are synthetic-control-oriented geo-experiment tools. The key difference is the synthetic-control variant.

Meta GeoLift is built around augmented synthetic control methods. The augmentation uses a prognostic outcome model, such as ridge regression or generalized synthetic control options, to improve the counterfactual when raw synthetic-control balance is imperfect. It also provides an end-to-end R workflow for market selection, power analysis, analysis, and plotting.

GeoSC uses SparseSC as its estimator backend. SparseSC regularizes the matching problem and uses cross-validation to select penalty settings. GeoSC wraps this with Python-native operational controls: data validation artifacts, assumption validation artifacts, donor evaluation outputs, power simulation metadata, and release-preflight checks.

The practical distinction:

  • Meta GeoLift is often a better fit for teams already using the Meta R package, its design functions, and its conformal/augmented synthetic-control workflow.
  • GeoSC is a better fit for Python teams that want SparseSC-based inference, explicit artifact contracts, and a CLI-oriented production workflow.
  • Neither package removes the need to validate donor comparability, treatment isolation, measurement-source consistency, and estimand alignment.

GeoSC vs Bayesian Causal-Impact Tools

GeoSC is not Bayesian and does not use PyMC. It does not produce posterior credible intervals, hierarchical meta-analysis estimates, or Bayesian structural time-series decompositions.

Bayesian causal-impact tools are useful when the analysis goal is to fit an explicit Bayesian time-series or Bayesian synthetic-control model and interpret posterior uncertainty under that model. GeoSC is useful when the analysis goal is SparseSC-based geo lift with donor-level diagnostics and production artifacts.

Do not pool or directly compare posterior effects from Bayesian tools with GeoSC’s ATT unless the estimand, scaling, time window, outcome definition, and measurement source are aligned.

What GeoSC Adds Operationally

GeoSC is designed to make production readiness demonstrable:

  • geosc power writes power_analysis_results.csv with simulation seeds, failed-run counts, failure rates, effective placebo settings, backend fields, and valid flags.
  • geosc donors writes donor_eval_results.csv and donor_pool_quality.json with quality bands, warning flags, and donor concentration diagnostics.
  • geosc infer writes geolift_results.json, geolift_diagnostics.json, data_validation.json, and assumption_validation.json.
  • result status is explicit: ok, partial, or failed.
  • selected_weight in donor evaluation is a design-stage recommendation and concentration signal, not a fitted SparseSC donor weight.
  • seeded CPU power runs are reproducible; GPU behaviour can vary by backend, hardware, and numerical library.

These are engineering and auditability features. They are not evidence that a specific experiment is causally valid.

Selection Checklist

Choose the tool based on the estimand and workflow, not the package name:

  • Use a regression-based tool when a regression counterfactual is the estimand you can defend.
  • Use Meta GeoLift when the augmented synthetic-control workflow and R ecosystem are the desired operational surface.
  • Use GeoSC when SparseSC, Python, donor-level diagnostics, and artifact validation are the desired operational surface.
  • Use Bayesian causal-impact tools when posterior model uncertainty is the target output.

For any tool, treat small samples carefully. Placebo p-values are coarse when few eligible donor geos are available, and no software package can repair a non-comparable donor pool or contaminated experiment.

References

Frequently Asked Questions

Quick answers to the most common GeoSC questions.

Business Questions

Q: What problem does GeoSC solve?

A: GeoSC estimates the incremental impact of regional marketing campaigns under a synthetic-control design. It answers the practical question: “How much did observed outcomes differ from a SparseSC counterfactual for the treated geographies?” The estimate is only credible when the donor pool, treatment timing, measurement source, and spillover assumptions are defensible.

Q: Why is this important for my business?

A:

  • Estimate incremental lift: Quantify observed outcomes against a documented counterfactual.
  • Improve budget decisions: Compare campaigns using a consistent measurement design.
  • Avoid unsupported claims: Separate evidence of lift from descriptive trends where the design is weak.
  • Support governance: Preserve JSON/CSV artifacts for review, audit, and sensitivity analysis.

Q: Is this the only measurement tool I need?

A: No, GeoSC is one component of a comprehensive measurement strategy:

  • Best for: Regional campaigns, store rollouts, geo-targeted advertising
  • Complements: Media Mix Modelling (MMM), multi-touch attribution, A/B testing
  • Integrates with: Your existing analytics stack (Google Analytics, Adobe, etc.)
  • Use alongside: Brand studies, customer surveys, and other measurement approaches

Q: What’s the cost and expected ROI?

A: GeoSC does not estimate commercial ROI by itself. Cost and ROI depend on your license terms, campaign spend, margin assumptions, operational constraints, and whether the experiment produces a credible effect estimate. Use GeoSC’s statistical outputs as inputs to a separate business-case calculation.

Q: How does the process work?

A: GeoSC supports a three-stage workflow:

  1. Evaluate donors: Identify control geographies that are plausible comparisons for the treated geographies.
  2. Check test strength: Use simulation-based power analysis to assess detectable effects and failure rates under explicit DGP assumptions.
  3. Measure lift: Run SparseSC inference and review result, data-validation, diagnostic, and assumption-validation artifacts.

Elapsed time depends on data readiness, review requirements, donor-pool quality, and campaign complexity.


Getting Started

Q1: What data do I need to run a GeoSC analysis?

A: You need:

  • Time series data: At least the configured minimum number of pre-treatment observations (12 in the shipped inference example). More history is usually preferable when it is comparable to the campaign period.
  • Geographic units: Markets, DMAs, states, or regions
  • Outcome metric: Sales, conversions, or other KPIs
  • Treatment assignment: Which markets received the campaign

Data format: CSV with columns for date, geographic unit, outcome metric, and treatment information. The exact required columns and date format come from your YAML configuration.

Q2: How long should I run my campaign to get reliable results?

A: Use the power-analysis command to assess duration and detectable effects:

geosc power --config data-config/power_analysis_config.yaml --use-gpu --jobs -1

This source-checkout example uses the shipped demo configuration. With a packaged installation, supply your own configuration path instead. Review power_analysis_results.csv for simulated power by duration and effect size, including valid, failure_rate, and Monte Carlo uncertainty fields.

There is no universal duration that guarantees power. Longer post periods can help, but power also depends on outcome noise, effect size, donor comparability, placebo support, and campaign timing.

Q3: How many control markets do I need?

A: There is no fixed safe number. You need enough comparable, uncontaminated control geographies to support a useful synthetic counterfactual and placebo reference set. Use the donor evaluator to inspect candidate quality:

geosc donors --config data-config/donor_eval_config.yaml --jobs -1

Outputs are saved in outputs/multicell_donor_eval/ (CSV with scores and donor maps).

Analysis Issues

Q4: My pre-period fit looks poor. What should I do?

A: Investigate the design before changing the estimator:

  1. Check data quality: Confirm consistent measurement methodology, treatment timing, and geography identifiers.
  2. Inspect unusual periods: Document holidays, promotions, outages, or other shocks. Do not remove observations simply to improve fit; pre-specify exclusions where possible and report sensitivity analyses.
  3. Extend comparable history: Add more pre-treatment observations only if measurement definitions and market conditions remain comparable.
  4. Revisit the design: Consider an alternative, defensible geographic aggregation or donor pool if the current one cannot support a credible counterfactual.

Q5: My results show no significant effect. What went wrong?

A: Common causes:

  • Low statistical power: Campaign too short or effect too small
  • Poor control selection: Controls don’t match treatment markets well
  • External factors: Market disruptions during campaign period
  • Data issues: Measurement problems or missing data

Check power analysis first to ensure the study design was capable of detecting the expected effect size.

Q6: The effect size seems too large to be believable. Is this normal?

A: Large effects can be real, but verify:

  1. Check data definitions: Ensure consistent measurement
  2. Review external events: Major market changes during campaign
  3. Validate treatment assignment: Confirm which markets got treatment
  4. Run sensitivity analysis: Test with different time periods or alternative donor pools.

Interpretation

Q7: How do I interpret the confidence intervals?

A: Confidence intervals show the range of effect sizes implied by the SparseSC placebo interval when the estimator returns finite bounds:

  • Narrow intervals: More precise estimates
  • Wide intervals: More uncertainty in the estimate
  • Intervals excluding zero: Statistically significant effects

Example interpretation:

Relative Lift: +15.2% (95% CI: +7.8% to +22.6%)

Read this as: “SparseSC returned a finite 95% placebo interval from +7.8% to +22.6% for this configured analysis.” It is not proof that the campaign-level causal assumptions hold.

Q8: What’s the difference between absolute and relative lift?

A:

  • Absolute lift: Raw units of incremental impact (e.g., +1,000 sales)
  • Relative lift: Percentage increase over baseline (e.g., +15%)

Both are important:

  • Use absolute lift for revenue calculations
  • Use relative lift for comparing across campaigns

Q9: How do I calculate ROI from the results?

A: GeoSC reports statistical effect estimates (e.g. ATT, confidence intervals). ROI calculations depend on your business context (price, margin, cost). Combine effect estimates with your cost and unit economics to derive ROI externally.

Q10: When should I trust the results vs. be sceptical?

A: Treat results as more credible when:

  • status is ok, data validation is clean, and required assumption checks have passed.
  • Pre-period fit and donor comparability are good, with an adequate placebo reference set.
  • The effect remains interpretable across pre-specified sensitivity analyses.
  • Treatment isolation, outcome definitions, and the measurement window are defensible for the estimand.

Be sceptical when:

  • Poor pre-period fit or large residuals
  • status: partial or failed, missing uncertainty metadata, or failed required assumption checks
  • Very large or very small effect sizes
  • A coarse or unstable placebo reference set, including very few eligible donors
  • Results change dramatically with small specification changes

A small placebo p-value is evidence within the configured reference set, not a standalone proof of a campaign-level causal effect.

Need More Help?

Interpreting Results

GeoSC produces a suite of statistical indicators to help determine the validity and magnitude of an experiment’s impact.

Main Statistical Outputs

  • status: Result contract status. ok means the core ATT, p-value, and uncertainty fields are available; partial means the estimate ran but one or more uncertainty fields are unavailable; failed means required estimator outputs are missing and the effect should not be interpreted.
  • att (Average Treatment Effect on the Treated): The average difference between the observed outcome and the synthetic counterfactual during the post-treatment period.
  • p_value: A donor-pool placebo p-value: the share of placebo effects at least as extreme as the observed ATT under the implemented SparseSC placebo comparison.
  • ci_lower / ci_upper: The SparseSC placebo confidence interval for the unscaled ATT when available.
  • se: Backward-compatible approximate SE derived from the reported placebo confidence interval width. This is not an estimator-provided sampling standard error.
  • se_approx_from_ci: The same approximation exposed under an explicit name. se_available remains false unless a future estimator provides a true sampling SE.
  • significant: Boolean flag indicating if the p-value falls below the configured alpha threshold (usually 0.05) when significance_available is true. Failed results always report significant: false.
  • assumption_status: Summary of gate-eligible assumption checks. skipped means the parallel-trends diagnostic was not completed, pass means it did not fail, and fail or error means causal interpretation needs review. It is not a certificate of causal validity.

Placebo p-values are finite-sample and donor-pool dependent. With few eligible donor units, p-values are coarse. Missing confidence intervals must not be interpreted as zero uncertainty. Check ci.unavailable_reason to distinguish disabled CI computation from missing or non-finite estimator outputs. Check p_value_placebo_mode before relying on reproducibility: sampled means SparseSC capped the placebo enumeration.

Operational Interpretation Gates

Use status, data_validation.json, and assumption_validation.json together. status: "ok" means the artifact contract completed; it does not certify donor comparability, absence of spillovers, or production safety. The advisory interference screen is recorded in assumption_validation.json as signal, no_signal, or indeterminate. It adds a caveat for a signal or indeterminate screen, but does not alter result status or significance. Required parallel-trends failures mark inference as failed.

Do not combine scaled diagnostics with unscaled ATT, or compare GeoSC ATT to revenue, platform-reported lift, or other measurement-source outputs, unless the estimands, units, and time windows have been harmonized upstream.

Business Metrics

GeoSC provides raw lift estimates. It does not output built-in ROI or business-impact metrics (e.g., incremental revenue, ROAS). Use the statistical outputs (att) multiplied by your unit volumes and margin data in downstream reporting tools to calculate ROI.

Methodology & Advanced Topics

This document provides a technical overview of the advanced features and mathematical concepts underlying GeoSC, focusing on how SparseSC is implemented and operationalised.

For a more rigorous econometric exposition intended for Data Scientists and Statisticians, see the Mathematical Formalism of Synthetic Controls.

SparseSC Mathematical Foundation

The standard Synthetic Control Method minimises pre-treatment prediction error by constructing a convex combination of donor units.

GeoSC uses the vendored SparseSC implementation, which extends traditional synthetic control by learning a regularized match space and donor weights from the pre-treatment data.

Two objects are useful for interpretation:

  1. Feature weights / match space (V): Which pre-treatment features or time periods matter most for matching.
  2. Unit weights (W): How control units combine to form the synthetic counterfactual.

SparseSC exposes penalties for unit-weight shrinkage (w_pen) and feature weighting (v_pen). GeoSC’s default inference configuration uses SparseSC’s fast path, which constructs the match space through RidgeCV-backed machinery in the vendored implementation. The non-fast path uses SparseSC’s coordinate descent and cross-validation over feature-weight penalties.

Regularization can reduce overfitting to pre-treatment noise and often produces more interpretable donor structure, but it does not make a weak donor pool valid. The result still depends on comparable controls, adequate pre-treatment support, and uncontaminated treatment timing.

Statistical Inference in GeoSC

Because regional marketing experiments typically involve a very small number of treated units (often just one), asymptotic statistical inference is usually not appropriate. GeoSC uses SparseSC placebo inference to compute empirical p-values and, when SparseSC returns finite bounds, placebo confidence intervals.

Placebo Inference (Permutation Tests)

The primary method for significance testing is in-space placebo permutations.

  1. The algorithm iteratively reassigns the “treatment” status to every untreated unit in the donor pool.
  2. It fits a completely new SparseSC model for each of these placebo units.
  3. It calculates the pseudo-treatment effect (the prediction error in the post-treatment period) for each placebo.
  4. The empirical p-value is calculated as the proportion of placebo effects that are at least as extreme as the actual estimated treatment effect.

This is a finite-sample comparison against the observed donor pool, not an unconditional production-safety guarantee. With few donors the p-value grid is coarse, and non-comparable geographies, spillovers, or poor pre-period fit can make the placebo reference set uninformative.

Advanced Configuration & Tuning

Regularisation Selection (Cross-Validation)

SparseSC selects penalty settings using cross-validation over pre-treatment information. The exact machinery depends on whether the configured run uses the fast path.

If the default automatic search does not yield a good fit, the configuration allows for direct intervention:

# Inference configuration options
sparse_sc_model_type: "retrospective"
sparse_sc_fast_estimation: true
sparse_sc_level: 0.95
  • sparse_sc_fast_estimation: When set to true, GeoSC calls SparseSC’s fast estimator. This uses a RidgeCV-backed match-space path and is generally faster than the full coordinate-descent estimator. Treat any speed/accuracy tradeoff as data-dependent.
  • sparse_sc_model_type: Setting this to retrospective ensures the model adheres strictly to the pre/post treatment boundary defined in the configuration.

Donor Pool Constraints

By default, GeoSC includes all non-treated units in the donor pool. However, if business logic dictates that certain regions should not be used (e.g., they experienced a supply chain shock), they should be excluded from the input CSV entirely prior to analysis.

The recipes/donor_evaluator.py tool helps systematically identify the highest quality donors prior to running inference.

Performance Considerations

GeoSC relies heavily on matrix operations during the cross-validation and optimisation phases. The following options are supported to improve execution speed:

  • Power Analysis Parallelism: Set jobs: -1 (or pass --jobs -1 via CLI) to use all available CPU cores when simulating hundreds of treatment effects.
  • GPU Acceleration: The maintained GPU path is optional CuPy-backed acceleration for the power calculator only. Pass --use-gpu to request it for performance-oriented power runs. This requires cupy to be installed and matched to your system’s CUDA version (e.g., pip install cupy-cuda12x). Seeded power runs disable GPU DGP/generation paths so CPU simulation draws are reproducible.
  • Inference Computation: The main SparseSC inference step runs on CPU. The legacy geolift.gpu_accelerator module is experimental compatibility code and is not wired into the maintained inference pipeline. To avoid thread oversubscription when running on multi-core servers, set environment variables such as OMP_NUM_THREADS or MKL_NUM_THREADS to a sensible value relative to your total core count.

Need More Help?

Subsections of Methodology

Mathematical Formalism of Synthetic Controls in GeoSC

This document provides a rigorous econometric and statistical foundation for the methods implemented in GeoSC, intended for Data Scientists and Statisticians.

1. The Potential Outcomes Framework

Let $Y_{it}$ denote the outcome of interest for region $i \in \{1, \dots, N\}$ at time period $t \in \{1, \dots, T\}$. We observe a pre-treatment period $t \in \{1, \dots, T_0\}$ and a post-treatment period $t \in \{T_0+1, \dots, T\}$.

Without loss of generality, let unit $i=1$ be the treated unit, and units $i \in \{2, \dots, N\}$ be the donor pool (control units).

Following the Rubin Causal Model, we define potential outcomes:

  • $Y_{it}^N$: The outcome that would be observed for unit $i$ at time $t$ absent the intervention.
  • $Y_{it}^I$: The outcome that would be observed for unit $i$ at time $t$ exposed to the intervention.

The observed outcome is:

$$ Y_{it} = Y_{it}^N + \alpha_{it} D_{it} $$

Where $D_{it}$ is an indicator variable equal to 1 if unit $i$ receives treatment at time $t$, and 0 otherwise. $\alpha_{it} = Y_{it}^I - Y_{it}^N$ is the treatment effect for unit $i$ at time $t$.

Our goal is to estimate the Average Treatment Effect on the Treated (ATT) during the post-treatment period:

$$ \tau = \frac{1}{T - T_0} \sum_{t=T_0+1}^{T} \alpha_{1t} = \frac{1}{T - T_0} \sum_{t=T_0+1}^{T} (Y_{1t}^I - Y_{1t}^N) $$

Since $Y_{1t}^I$ is observed post-intervention ($Y_{1t}$), the fundamental problem of causal inference is estimating the unobserved counterfactual $Y_{1t}^N$.

2. The Standard Synthetic Control Estimator

The Synthetic Control Method (Abadie, Diamond, and Hainmueller, 2010) estimates the counterfactual $Y_{1t}^N$ as a weighted combination of the donor pool:

$$ \hat{Y}_{1t}^N = \sum_{j=2}^{N} w_j Y_{jt} $$

Where $\mathbf{W} = (w_2, \dots, w_N)'$ is a vector of weights satisfying:

  1. $w_j \geq 0 \quad \forall j$ (Non-negativity)
  2. $\sum_{j=2}^{N} w_j = 1$ (Simplex constraint)

The weights are chosen to minimise the discrepancy between the treated unit and the synthetic control in the pre-treatment period, typically by minimizing:

$$ || \mathbf{X}_1 - \mathbf{X}_0 \mathbf{W} ||_V = \sqrt{(\mathbf{X}_1 - \mathbf{X}_0 \mathbf{W})' V (\mathbf{X}_1 - \mathbf{X}_0 \mathbf{W})} $$

Where $\mathbf{X}_1$ is a $(K \times 1)$ vector of pre-intervention characteristics for the treated unit, $\mathbf{X}_0$ is a $(K \times J)$ matrix of the same variables for the donor pool, and $V$ is a positive semi-definite weighting matrix.

3. Scope Relative to Synthetic Difference-in-Differences

Synthetic Difference-in-Differences (SDiD) is a related panel estimator that combines weighting ideas from synthetic control and difference-in-differences. It is not implemented by GeoSC. GeoSC’s supported estimator selector is sparsesc; the documentation does not claim an empirical superiority of SparseSC over SDiD for every geo-experiment.

The methods make different modelling and weighting choices. Suitability depends on the estimand, timing, donor pool, outcome process, and the diagnostics that the analyst can defend. Neither method removes the need to assess treatment isolation, donor comparability, stable measurement, and sensitivity to reasonable design choices.

GeoSC employs SparseSC (Sparse Synthetic Controls), which regularizes the matching problem through feature-weight and unit-weight penalties. By encouraging a parsimonious match space, it can reduce interpolation error when comparable donors exist. It does not prove that the selected donor set shares the treated unit’s data-generating process, and the implemented assumption and interference diagnostics are design checks rather than guarantees.

4. SparseSC Regularisation Mechanics

Let $\mathbf{Y}_0^{pre}$ be the $(T_0 \times J)$ matrix of pre-treatment outcomes for donors, and $\mathbf{Y}_1^{pre}$ be the $(T_0 \times 1)$ vector for the treated unit.

SparseSC modifies standard synthetic control by fitting in a regularized match space. A simplified objective for interpreting the mechanism is:

$$ \hat{\mathbf{W}}, \hat{V} = \arg\min_{\mathbf{W}, V} || \mathbf{Y}_1^{pre} - \mathbf{Y}_0^{pre} \mathbf{W} ||_V^2

  • \mathcal{P}_W(\mathbf{W}; \lambda_W)
  • \mathcal{P}_V(V; \lambda_V) $$

where $V$ controls the feature or time-period match space, $\mathcal{P}_W$ shrinks unit weights toward a regularized solution, and $\mathcal{P}_V$ penalizes feature weights.

In GeoSC’s default configuration, sparse_sc_fast_estimation: true calls SparseSC’s fast path, which uses RidgeCV-backed match-space machinery. When the fast path is disabled, SparseSC uses its full fitting path with cross-validation over feature-weight penalties. The practical goal is to reduce overfitting to pre-treatment noise, especially when $T_0$ is small relative to the donor pool, while preserving a donor-based counterfactual that remains auditable.

5. Placebo Inference and Empirical P-Values

Because geo-experiments often have $N_1 = 1$ or a small integer, asymptotic inference is usually not appropriate. GeoSC calculates donor-pool empirical p-values using in-space placebo permutations.

We iteratively reassign the treatment status to every donor unit $j \in \{2, \dots, N\}$, calculate a placebo synthetic control $\hat{Y}_{jt}^N$, and derive a placebo effect $\hat{\alpha}_{jt}$.

The ratio of post-treatment MSPE to pre-treatment MSPE is calculated for the actual treated unit and all placebos:

$$ r_i = \frac{\frac{1}{T-T_0} \sum_{t=T_0+1}^T (Y_{it} - \hat{Y}_{it}^N)^2}{\frac{1}{T_0} \sum_{t=1}^{T_0} (Y_{it} - \hat{Y}_{it}^N)^2} $$

The p-value is the finite donor-pool placebo share with a ratio at least as extreme as the treated unit’s ratio:

$$ p = \frac{1}{N} \sum_{j=1}^N \mathbf{I}(r_j \geq r_1) $$

This quantity is only as informative as the placebo reference set. With few eligible donors, the attainable p-values are coarse; with non-comparable donors, spillovers, or poor pre-period fit, the empirical comparison can be misleading.