Skip to content

Latest commit

 

History

History
599 lines (460 loc) · 17.2 KB

File metadata and controls

599 lines (460 loc) · 17.2 KB

GeoLift Python API Reference

This document describes every public function and class in the geolift package.

Table of Contents


Standard data shape

All GeoLift functions expect a pandas.DataFrame with these columns:

Column Type Description
location str Geo-unit name (lowercased internally)
time int Integer time index starting at 1
Y float Outcome variable (e.g. conversions, revenue)

geo_data_read converts raw date-indexed data into this canonical shape.


Data utilities

geo_data_read

from geolift import geo_data_read

geo_data_read(
    data: pd.DataFrame,
    date_id: str = "date",
    location_id: str = "location",
    Y_id: str = "units",
    format: str = "mm/dd/yyyy",
    X: Optional[List[str]] = None,
    summary: bool = False,
    keep_unix_time: bool = False,
) -> Optional[pd.DataFrame]

Reads and pre-processes a raw geo panel into the GeoLift standard format. Normalises locations to lowercase, parses dates into a 1-indexed consecutive integer time column, drops rows with null Y, and removes locations whose time series is incomplete (i.e. missing any time period present in other locations). Duplicate (location, time) pairs are aggregated by summing Y.

Parameters

Parameter Default Description
data Raw panel DataFrame
date_id "date" Name of the date column
location_id "location" Name of the location column
Y_id "units" Name of the outcome column
format "mm/dd/yyyy" Date format string. Supported: mm/dd/yyyy, yyyy-mm-dd, mm-yyyy, ww/yyyy, and 20+ others — see _VALID_FORMATS in data.py
X None Optional list of covariate column names to carry through (each summed within (location, time))
summary False Print processing summary if True
keep_unix_time False Retain the intermediate date_unix column

Returns pd.DataFrame in GeoLift standard format, or None if validation fails.

Example

import pandas as pd
from geolift import geo_data_read

raw = pd.read_csv("my_data.csv")  # has columns: date, location, sales
data = geo_data_read(raw, date_id="date", Y_id="sales", format="yyyy-mm-dd")

trim_controls

from geolift import trim_controls

trim_controls(
    data: pd.DataFrame,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    max_controls: int = 20,
    test_locations: Optional[List[str]] = None,
    forced_control_locations: Optional[List[str]] = None,
    random_state: Optional[int] = None,
) -> Optional[pd.DataFrame]

Reduces the control pool via stratified sampling by mean-deviation quintile. Computes each location's mean deviation from the cross-sectional average Y, assigns to 5 equal quintile buckets, then samples round(max_controls / 5) locations per bucket. Test and forced-control locations are always retained.

Parameters

Parameter Default Description
data Panel in GeoLift standard format
max_controls 20 Target number of control locations. Recommended: 4–5× the number of test locations
test_locations None Treatment locations — always kept
forced_control_locations None Additional locations to always keep in the control pool
random_state None Integer seed for reproducible sampling

Returns Filtered pd.DataFrame, or None when max_controls exceeds distinct locations.


Core inference

geo_lift

from geolift import geo_lift

geo_lift(
    data: pd.DataFrame,
    treatment_locations: List[str],
    treatment_start_time: int,
    treatment_end_time: int,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    X: Optional[List[str]] = None,
    model: str = "none",
    fixed_effects: bool = True,
    alpha: float = 0.1,
    stat_func: Union[str, Callable] = "two_sided",
    side_of_test: str = "two_sided",
    type: str = "iid",
    n_samples: int = 200,
    compute_ci: bool = True,
    grid_size: int = 250,
    random_state: Optional[int] = None,
) -> GeoLiftResult

Main inference function. Fits an Augmented Synthetic Control Model (ASCM) and returns a GeoLiftResult containing point estimates, inference statistics, and diagnostics.

Inference method: conformal inference via residual permutation (IID or block). This differs from R's time-shift placebo tests — see docs/r_python_differences.md.

Parameters

Parameter Default Description
data Panel in GeoLift standard format
treatment_locations List of treated location names
treatment_start_time First treatment period (inclusive)
treatment_end_time Last treatment period (inclusive)
model "none" ASCM model type. Options: "none" (OLS-only synth), "ridge" (Ridge-augmented synth), "best" (selects the lower scaled-L2 option). "gsyn" raises NotImplementedError
fixed_effects True Demean by location and time fixed effects before fitting
alpha 0.1 Significance level for CI and p-value
stat_func "two_sided" Test statistic: "two_sided" or "positive" / "negative" (one-sided)
side_of_test "two_sided" Legacy alias; use stat_func directly
type "iid" Permutation type: "iid" or "block"
n_samples 200 Number of permutation samples
compute_ci True Whether to compute the conformal CI via grid search
grid_size 250 Number of null-hypothesis grid points for CI search
random_state None Integer seed for reproducible inference. None uses seed 42 internally

Returns GeoLiftResult

Example

from geolift import geo_lift

result = geo_lift(
    data=data,
    treatment_locations=["chicago", "portland"],
    treatment_start_time=36,
    treatment_end_time=45,
    model="none",
    alpha=0.1,
    n_samples=200,
    random_state=42,
)
print(result)
print(f"ATT: {result.average_att:.3f}")
print(f"P-value: {result.p_val:.4f}")

GeoLiftResult

from geolift import GeoLiftResult

Returned by geo_lift. Attributes:

Attribute Type Description
treatment_start int First treatment period
treatment_end int Last treatment period
treatment_locations List[str] Treated location names
average_att float Average treatment effect on the treated (per location-period)
se float Standard error of the ATT (from conformal inference)
p_val float Conformal p-value in [0, 1]
lower_bound float Lower bound of the (1 - alpha) confidence interval
upper_bound float Upper bound of the (1 - alpha) confidence interval
att pd.DataFrame Period-by-period ATT with SE and CI columns; indexed by time
l2_imbalance float Pre-period L2 imbalance (absolute units)
scaled_l2_imbalance float L2 imbalance scaled by naïve mean outcome (dimensionless)
bias_est float Bias estimate from the augmented component
weights pd.DataFrame Control location weights; columns ["location", "weight"]
data pd.DataFrame Panel data used for fitting (with D treatment indicator)
model AugSynthModel Fitted model object
alpha float Significance level used

cumulative_lift

from geolift import cumulative_lift

cumulative_lift(
    geo_lift_obj: GeoLiftResult,
    treatment_start_time: Optional[int] = None,
) -> pd.DataFrame

Computes cumulative ATT across the treatment period.

Returns pd.DataFrame with columns ["time", "cumulative_att"].


ascm_execution

from geolift import ascm_execution

ascm_execution(
    data: pd.DataFrame,
    treatment_locations: List[str],
    treatment_start_time: int,
    treatment_end_time: int,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    X: Optional[List[str]] = None,
    model: str = "none",
    fixed_effects: bool = True,
) -> dict

Low-level ASCM fitting step used internally by geo_lift. Returns a dict with keys "ascm_model" (AugSynthModel), "data" (panel with D column), and "treatment_locations" (lowercased list).


Pre-test (power & market selection)

geo_lift_power

from geolift import geo_lift_power

geo_lift_power(
    data: pd.DataFrame,
    treatment_locations: List[str],
    treatment_start_time: int,
    treatment_end_time: int,
    effect_size: List[float] = None,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    X: Optional[List[str]] = None,
    model: str = "none",
    fixed_effects: bool = True,
    alpha: float = 0.1,
    stat_func: str = "two_sided",
    n_samples: int = 200,
    random_state: Optional[int] = None,
) -> pd.DataFrame

Estimates detection power for a given treatment configuration across multiple effect sizes. For each effect size, simulates the experiment by injecting the scaled effect into the pre-treatment data, fits ASCM, and records whether the null is rejected.

Parameters

Parameter Default Description
effect_size [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3] List of multiplicative effect sizes (0 = null, 0.1 = 10% lift)
n_samples 200 Permutation samples per simulation
random_state None Integer seed for reproducibility

Returns pd.DataFrame with columns including effect_size, power, average_att, scaled_l2_imbalance.


geo_lift_market_selection

from geolift import geo_lift_market_selection

geo_lift_market_selection(
    data: pd.DataFrame,
    treatment_periods: List[int],
    N: List[int],
    effect_size: List[float] = None,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    X: Optional[List[str]] = None,
    model: str = "none",
    fixed_effects: bool = True,
    alpha: float = 0.1,
    stat_func: str = "two_sided",
    include_markets: Optional[List[str]] = None,
    exclude_markets: Optional[List[str]] = None,
    dtw: float = 0,
    n_samples: int = 200,
    random_state: Optional[int] = None,
) -> GeoLiftMarketSelection

Runs power analysis across all candidate treatment market combinations and ranks them. For each combination of treatment period length (from treatment_periods) and number of treatment markets (from N), fits ASCM and computes power at each effect size.

Parameters

Parameter Default Description
treatment_periods List of candidate treatment period lengths
N List of candidate numbers of treatment markets
include_markets None Markets that must always be included in treatment
exclude_markets None Markets to never consider as treatment
dtw 0 Weight on DTW distance vs correlation for market ranking. 0 = correlation only
random_state None Integer seed for reproducibility

Returns GeoLiftMarketSelection


GeoLiftMarketSelection

Returned by geo_lift_market_selection. Attributes:

Attribute Type Description
best_markets pd.DataFrame Top-ranked market combinations sorted by power and L2 imbalance
all_results pd.DataFrame Full results across all combinations
data pd.DataFrame Input panel
alpha float Significance level

Multi-cell (experimental)

Note: All multi-cell functions are experimental. The API may change in future releases.

geo_lift_multi_cell

from geolift import geo_lift_multi_cell

geo_lift_multi_cell(
    data: pd.DataFrame,
    treatment_locations: List[List[str]],
    treatment_start_time: int,
    treatment_end_time: int,
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
    X: Optional[List[str]] = None,
    model: str = "none",
    fixed_effects: bool = True,
    alpha: float = 0.1,
    n_samples: int = 200,
    random_state: Optional[int] = None,
) -> GeoLiftMultiCell

Fits an ASCM independently for each cell (arm) in a multi-cell experiment. treatment_locations is a list of lists where each inner list defines the treatment markets for one cell.


GeoLiftMultiCell

Returned by geo_lift_multi_cell. Attributes:

Attribute Type Description
cell_results List[GeoLiftResult] One GeoLiftResult per cell
k int Number of cells

multi_cell_market_selection

from geolift import multi_cell_market_selection

multi_cell_market_selection(
    data: pd.DataFrame,
    k: int,
    treatment_periods: List[int],
    N: List[int],
    effect_size: List[float] = None,
    ...,
    random_state: Optional[int] = None,
) -> MultiCellMarketSelection

Runs market selection independently for each of k cells.


multi_cell_power

from geolift import multi_cell_power

multi_cell_power(
    data: pd.DataFrame,
    treatment_locations: List[List[str]],
    treatment_start_time: int,
    treatment_end_time: int,
    effect_size: List[float] = None,
    ...,
    random_state: Optional[int] = None,
) -> MultiCellPower

Runs power analysis for each cell independently.


multi_cell_winner

from geolift import multi_cell_winner

multi_cell_winner(
    geo_lift_multi_cell_obj: GeoLiftMultiCell,
    alpha: float = 0.1,
) -> pd.DataFrame

Determines the winning cell (arm) based on ATT magnitude and statistical significance.


MultiCellMarketSelection

Attributes: models (list of GeoLiftMarketSelection), data, test_details, k, best_markets.

MultiCellPower

Attributes: cell_power (list of power DataFrames), k.


Auxiliary utilities

fn_treatment

from geolift import fn_treatment

fn_treatment(
    data: pd.DataFrame,
    locations: List[str],
    treatment_start_time: int,
    treatment_end_time: int,
    location_id: str = "location",
    time_id: str = "time",
) -> pd.DataFrame

Adds a binary D column (1 = treated location in treatment period, 0 otherwise) to the panel. Used internally before fitting ASCM.


append_agg

from geolift import append_agg

append_agg(
    data: pd.DataFrame,
    treatment_locations: List[str],
    Y_id: str = "Y",
    time_id: str = "time",
    location_id: str = "location",
) -> pd.DataFrame

Appends an aggregated row for all treatment locations combined (sum of Y). Useful for multi-location post-test diagnostics.


correlation_coefficient

from geolift import correlation_coefficient

correlation_coefficient(x: np.ndarray, y: np.ndarray) -> float

Returns the Pearson correlation coefficient between two arrays.


market_correlations

from geolift import market_correlations

market_correlations(
    data: pd.DataFrame,
    location_id: str = "location",
    time_id: str = "time",
    Y_id: str = "Y",
) -> pd.DataFrame

Computes pairwise Pearson correlations between all locations' Y time series. Returns a long-form DataFrame with columns ["location_1", "location_2", "correlation"] sorted by descending correlation.


Notes

model parameter

Value Description
"none" OLS-only synthetic control (Abadie-style)
"ridge" Ridge-augmented synthetic control
"best" Selects "none" or "ridge" based on scaled L2 imbalance
"gsyn" Generalized synthetic control — raises NotImplementedError

Reproducibility

All stochastic functions accept random_state: Optional[int] = None. Pass the same integer to get identical p-values, confidence bounds, and power estimates across runs:

result = geo_lift(data, ["city_a"], 30, 40, n_samples=200, random_state=42)

Known differences from R

  • Inference: R uses time-shift placebo tests; Python uses conformal inference. P-values are not comparable across languages even with the same seed. See docs/r_python_differences.md.
  • Deterministic quantities (ATT, L2 imbalance, weights) agree within numerical tolerance (≤ 0.01 absolute) between R and Python.