Skip to main content

geoexp

Geo-experiment design on top of augsynth-py: market selection, power-based design ranking, and experiment reporting.

The boundary between the two packages is "given a design" vs. "choose a design":

  • augsynth-py answers what can this design detect? — synthetic-control estimators, conformal inference, and power/MDE for a treated set the caller already fixed.
  • geoexp (this package) answers which design should I run? — ranking candidate treated sets x durations by what each can detect, with GeoLift-shaped reporting. It consumes augsynth-py through its public API only.

Quickstart

import numpy as np
import polars as pl
from augsynth_py import AugSynth

from geoexp import enumerate_candidates, rank_designs

# Build candidates, or pass your own explicit sets.
candidates = enumerate_candidates(
    panel,
    unit="location",
    time="time",
    outcome="Y",
    sizes=[1, 2],
    max_per_size=15,
)

res = rank_designs(
    panel,  # long format: unit, time, outcome
    estimator=AugSynth(lambda_=1.0),  # freeze the penalty for large grids
    unit="location",
    time="time",
    outcome="Y",
    candidates=candidates,
    durations=[15, 30],
    lookback_window=10,
    permutation_type="iid",
    rng=np.random.default_rng(7),
    n_jobs=-1,  # parallel across candidates
)

res.ranking  # one row per candidate x duration, best first
res.power("chicago, portland", 15)  # the underlying augsynth_py.PowerResults

res.ranking carries candidate, duration, n_units, mde, mde_grid, power_at_target, empirical_size, rmspe_pre, null_bias, h1_calibration_error, proportion_total_y, holdout, rank, and investment when cpic is given.

Two things worth knowing before you read the output:

  • mde is interpolated off the power curve, not a grid point, because the grid value leaves most candidates tied. It is a geoexp construct and is not R GeoLift's Average_MDE. mde_grid carries the upstream grid point.
  • Rank order by mde is stable within an implementation, not across them. Small differences near ties carry no information. See docs/methodology.md.
  • null_bias and h1_calibration_error are near-duplicates on well-behaved panels; they separate only when the estimator's bias depends on the effect size.

Scope

0.1.x is single-cell market selection: candidate enumeration by similarity, power-based ranking, and cost per incremental conversion. Budget-constrained selection is 0.2.x; multi-cell designs are 0.3.x+.

enumerate_candidates is a convenience, not a gate — rank_designs takes any list of sets, so bring your own candidates if you prefer.

Validation

Every feature with a reproducible R GeoLiftMarketSelection counterpart is asserted against it via rpy2 on the GeoLift_PreTest fixture. Parity is established on the power curve at 240 cells (40 candidates x 6 effect sizes): the two implementations agree with each other as well as either agrees with itself, with no systematic bias. Features with no reproducible counterpart are listed with their reason in docs/geolift-oracle-characterization.md.

Clean-room: the R GeoLift package is a numerical oracle only, driven via rpy2. Its source is never read for implementation and never translated.

Development and AI use

This package was written by its maintainer working with Claude Code, Anthropic's coding agent. That is disclosed here because it should shape how you read the rest of this page, not as a novelty.

The working rule throughout was that nothing gets asserted that was not measured. The parity figures above come from real runs against the R oracle and are reproducible from tests/validation_against_r/; the noise floor they are judged against was measured rather than assumed, after fixed thresholds calibrated on a single draw turned out to flake. Where a quantity could not be validated — GeoLift's Average_MDE and most of its ranking columns — that is recorded as a limitation in docs/geolift-oracle-characterization.md rather than papered over, and a cross-package benchmark that did not work is written up there as a negative result instead of being quietly dropped. The public API was designed and signed off before implementation, the implementation was written test-first, and the clean-room rule stated above held throughout: the R GeoLift source was never read.

Install

pip install geoexp

Requires augsynth-py>=0.5.0 (the release that froze the power-API contract this package is written against).

License

MIT. No association with Meta's GeoLift is implied; the R package is used strictly as a numerical validation oracle.

Metadata

Release files for geoexp 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geoexp 0.1.0
File Size Uploaded
geoexp-0.1.0.tar.gz 45.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for geoexp 0.1.0
File Interpreter ABI Platform
geoexp-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.0 kB

Release files / geoexp-0.1.0.tar.gz

Download URL geoexp-0.1.0.tar.gz
Size 45.4 kB
Tags Source
SHA-256 checksum
How to use checksums
791b419509afb8497f110c74c61ac5d557e73220f4f9cd3976137a6f168e9a1f
BLAKE2b-256 checksum
How to use checksums
8e4e0cb63cabea8a1e724831df6330bc154ab8cd79d0de3e6c8ad13c8848c24b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / geoexp-0.1.0-py3-none-any.whl

Download URL geoexp-0.1.0-py3-none-any.whl
Size 14.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3ef16b165c71316a04e0e59ff7191fa9471ef06a3f6787801f8fcbd494676bce
BLAKE2b-256 checksum
How to use checksums
21303038c4ba261cde8c5fbfc0b9295c80a5aecfa05452b39c61501d823cda41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page