Package to implement the multi-level SC estimator
Project description
mlSC: Multi-level Synthetic Control Estimator
This package implements the multi-level SC estimator (mlSC) for the treatment effect for a single, treated, aggregated unit in panel data with multiple levels of aggregation, as proposed in Bottmer (2025). We observe matrices of outcomes at the disaggregate and the aggregate level. Treatment is assigned at the aggregate level and the goal is to obtain a treatment effect estimate at the aggregate level. The package captures cases for a single aggregated treated unit, possibly over multiple time periods (where treatment is assumed to never turn off). The multi-level SC estimator will use the disaggregated data as a starting point to estimate a synthetic control for the entire aggregated treated unit. It augments the objective function by adding a hierarchical penalty parameter that shrinks the flexible solution towards the classical synthetic control estimator that treats each state as a single unit. The penalty parameter is estimated using either (1) cross-validation over time or (2) a heuristic.
This package is currently in beta and the functionality and interface is subject to change.
Installation
Install this library using pip:
pip install multi-levelSC
Example
In this example, data at the county- and state-level is simulated from a hierarchical linear latent factor model. Then, the multi-level SC estimator is applied, using the heuristic to estimate the penalty parameter. Alternatively, if the user can use cross-validation over time by specifying "cross-validation" as the lambda_est method (and additionally a lambda_grid, if desired).
#####################
### Generate data ###
#####################
np.random.seed(42)
# --- Parameters ---
N_states = 10
counties_per_state = 10 # each state has the same number of counties in this example
n_c = np.full(N_states, counties_per_state) # array of number of counties per state
T = 20
# Standard deviations for each component
sigma_time = 1.0 # time factor scale
sigma_state = 0.8 # state loading scale
sigma_county = 0.5 # county-level deviation scale
sigma_eps = 0.3 # idiosyncratic noise scale
# --- Generate latent factors ---
# Single time factor (T × 1)
time_factor = np.random.normal(0, sigma_time, size=(T, 1))
# State-level loadings (N_states × 1)
state_loadings = np.random.normal(0, sigma_state, size=(N_states, 1))
# County-level deviations (N_states * counties_per_state × 1)
n_counties = N_states * counties_per_state
county_components = np.random.normal(0, sigma_county, size=(n_counties, 1))
# --- Map counties to states ---
state_idx = np.repeat(np.arange(N_states), counties_per_state)
county_in_state_idx = np.tile(np.arange(counties_per_state), N_states)
# --- Combine state and county effects ---
county_loadings = state_loadings[state_idx] + county_components # (n_counties × 1)
# --- Generate outcome matrix at disaggregate level ---
# data_c_{s,c,t} = county_loading_sc * time_factor_t + ε_{s,c,t}
eps = np.random.normal(0, sigma_eps, size=(n_counties, T))
data_c = county_loadings @ time_factor.T + eps # (n_counties × T)
# --- Population weights within each state ---
# Each county gets equal weight = 1 / number of counties in its state
w_c = [np.full(n_c[s], 1 / n_c[s]) for s in range(N_states)] # IMPORTANT: this needs to be a list of arrays!
# --- Aggregate data by state using population weights ---
data_s = np.zeros((N_states, T))
for s in range(N_states):
# Weighted average across all counties in state s
counties_in_s = np.where(state_idx == s)[0]
data_s[s, :] = np.average(data_c[counties_in_s, :], axis=0, weights=w_c[s])
# --- Treated unit and time period ---
idx = 0
t = 19
################
### Analysis ###
################
# Import estimator
from multi_level_sc_estimator.mlSC import mlSC_estimator
# Define data sets, treated unit, treated period, population weights (w_c) and how to estimate lambda.
mlSC_results = mlSC_estimator(data_s,data_c, idx, n_c, t, w_c, lambda_est = "heuristic")
# Display results
tau_hat = mlSC_results[0]
lambda_hat = mlSC_results[1]
w_hat = mlSC_results[2]
References
Lea Bottmer. Synthetic Control with Disaggregated Data, 2025. [link]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file multi_levelsc-0.1.2.tar.gz.
File metadata
- Download URL: multi_levelsc-0.1.2.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
582fe6efc17389ef07a622fec43cd11ef70c1d1ceae44d8c764fb7e0cfc52f1c
|
|
| MD5 |
3aee47bf98f03222ec064bc34e992eeb
|
|
| BLAKE2b-256 |
3b535de6d0c16e7f123e230a90f5f810d640b7f9474b70861487dce8e8f5e1e4
|
Provenance
The following attestation bundles were made for multi_levelsc-0.1.2.tar.gz:
Publisher:
publish.yml on leabottmer/multi-level-sc-estimator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
multi_levelsc-0.1.2.tar.gz -
Subject digest:
582fe6efc17389ef07a622fec43cd11ef70c1d1ceae44d8c764fb7e0cfc52f1c - Sigstore transparency entry: 698586840
- Sigstore integration time:
-
Permalink:
leabottmer/multi-level-sc-estimator@0fb26391a21840085d7cb4d7b1aa257e7360f9ea -
Branch / Tag:
refs/tags/v1.2 - Owner: https://github.com/leabottmer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0fb26391a21840085d7cb4d7b1aa257e7360f9ea -
Trigger Event:
release
-
Statement type:
File details
Details for the file multi_levelsc-0.1.2-py3-none-any.whl.
File metadata
- Download URL: multi_levelsc-0.1.2-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
369f677013ccbfb51bd7c3d2649d73aa94fc4ad363161c344bb266515ce477b6
|
|
| MD5 |
3386496ed89d7310f0e1ea1c5837c1d6
|
|
| BLAKE2b-256 |
c33aad56c0be617a4c79ddb71729c84833ffdadceb8826b6306ef306f5701966
|
Provenance
The following attestation bundles were made for multi_levelsc-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on leabottmer/multi-level-sc-estimator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
multi_levelsc-0.1.2-py3-none-any.whl -
Subject digest:
369f677013ccbfb51bd7c3d2649d73aa94fc4ad363161c344bb266515ce477b6 - Sigstore transparency entry: 698586845
- Sigstore integration time:
-
Permalink:
leabottmer/multi-level-sc-estimator@0fb26391a21840085d7cb4d7b1aa257e7360f9ea -
Branch / Tag:
refs/tags/v1.2 - Owner: https://github.com/leabottmer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0fb26391a21840085d7cb4d7b1aa257e7360f9ea -
Trigger Event:
release
-
Statement type: