Skip to main content

Package to implement the multi-level SC estimator

Project description

mlSC: Multi-level Synthetic Control Estimator

PyPI Tests Changelog License

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multi_levelsc-0.1.2.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multi_levelsc-0.1.2-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

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

Hashes for multi_levelsc-0.1.2.tar.gz
Algorithm Hash digest
SHA256 582fe6efc17389ef07a622fec43cd11ef70c1d1ceae44d8c764fb7e0cfc52f1c
MD5 3aee47bf98f03222ec064bc34e992eeb
BLAKE2b-256 3b535de6d0c16e7f123e230a90f5f810d640b7f9474b70861487dce8e8f5e1e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for multi_levelsc-0.1.2.tar.gz:

Publisher: publish.yml on leabottmer/multi-level-sc-estimator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for multi_levelsc-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 369f677013ccbfb51bd7c3d2649d73aa94fc4ad363161c344bb266515ce477b6
MD5 3386496ed89d7310f0e1ea1c5837c1d6
BLAKE2b-256 c33aad56c0be617a4c79ddb71729c84833ffdadceb8826b6306ef306f5701966

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page