# An In\-Depth Guide to Synthetic Control Using Propensity Score Matching in Python

- Canonical: https://33rdsquare.com/introduction-to-synthetic-control-using-propensity-score-matching/
- Published: 2024-09-03
- Author: Jordan Brown
- Categories: [Artificial Intelligence & Machine Learning & ChatGPT](https://33rdsquare.com/category/tech/ai/)

---

In an ideal world, every question about the effectiveness of a treatment, program or policy could be answered with a perfectly executed randomized controlled trial. In reality, randomly assigning subjects to treatment and control groups is often infeasible due to ethical, logistical or financial constraints.

This is where synthetic control methods come to the rescue. Synthetic control is a statistical technique for estimating the effects of an intervention on a treatment group by constructing an artificial control group that closely resembles the treatment group. One of the most popular approaches for creating synthetic control groups is propensity score matching.

In this article, we‘ll take a deep dive into propensity score matching and how to implement it in Python. Whether you‘re an aspiring data scientist or a veteran researcher, you‘ll come away with a solid understanding of this powerful tool and practical tips for applying it to your own projects. Let‘s get started!

## What is Propensity Score Matching?

Propensity score matching is a way to create balanced treatment and control groups when random assignment is not possible. The basic idea is to match each subject in the treatment group with one or more subjects in the control group that have similar probabilities of receiving the treatment, known as propensity scores.

To estimate propensity scores, we fit a model (usually logistic regression) that predicts the probability of treatment assignment based on a set of observed pre-treatment covariates. Covariates are variables that are associated with both the treatment assignment and the outcome of interest. By including relevant covariates in the propensity model, we aim to control for confounding and isolate the effect of the treatment itself.

Once we have estimated propensity scores for all subjects, we can use various matching algorithms to pair up treated and control subjects with similar scores. After matching, we assess the balance of covariates between the matched groups to ensure that they are indeed comparable. If the matching was successful, we can estimate the treatment effect by simply comparing outcomes between the matched treated and control groups.

Propensity score matching rests on two key assumptions:

1. Conditional independence: There are no unobserved confounders that influence both treatment assignment and outcomes after conditioning on the observed covariates.
2. Common support: There is sufficient overlap in the distribution of propensity scores between the treatment and control groups.

When these assumptions are met, propensity score matching can yield unbiased estimates of treatment effects that closely approximate those from a randomized experiment. However, as we‘ll discuss later on, it‘s important to be aware of the limitations and potential pitfalls of this approach.

## Step-by-Step: Propensity Score Matching in Python

Now that we have a high-level understanding of propensity score matching, let‘s walk through the process of implementing it in Python. We‘ll illustrate each step with code snippets, but keep in mind that there are many different ways to approach propensity score matching and the specifics may vary depending on your dataset and research question.

### Step 1: Prepare your data

The first step is to wrangle your data into a format suitable for propensity score matching. At minimum, you‘ll need:

- A treatment indicator variable (1 for treated, 0 for control)
- One or more outcome variables measured after treatment
- A set of pre-treatment covariates to include in the propensity model

It‘s also a good idea to split your data into training and testing sets at this stage.

```
import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_csv(‘data.csv‘)
X = df[[‘covariate1‘, ‘covariate2‘, ...]]
y = df[‘treatment‘]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
```

### Step 2: Fit the propensity model

Next, we fit a logistic regression model to estimate propensity scores, i.e. probabilities of treatment assignment given the observed covariates.

```
from sklearn.linear_model import LogisticRegression

propensity_model = LogisticRegression()
propensity_model.fit(X_train, y_train)

propensity_scores = propensity_model.predict_proba(X_test)[:,1]
```

### Step 3: Match treatment and control units

There are several different algorithms for matching treatment and control units based on propensity scores. One common approach is nearest neighbor matching, where each treated unit is matched to the control unit with the closest propensity score.

```
from sklearn.neighbors import NearestNeighbors

matcher = NearestNeighbors(n_neighbors=1, metric=‘euclidean‘)
matcher.fit(propensity_scores.reshape(-1,1))

distances, indices = matcher.kneighbors(propensity_scores[y_test==1].reshape(-1,1))
matches = X_test.iloc[indices.flatten()]
```

We can also specify a caliper to avoid matching treated and control units with propensity scores that are too far apart. And to make full use of the control pool, we can allow matching with replacement, i.e. the same control unit can be matched to multiple treated units.

### Step 4: Assess balance

After matching, it‘s crucial to check that the distribution of covariates is balanced between the matched treated and control groups. We can compare summary statistics like means and standard deviations, or use visual diagnostics like box plots and histograms.

```
import seaborn as sns
import matplotlib.pyplot as plt

matched_df = pd.concat([matches, y_test], axis=1)

for col in [‘covariate1‘, ‘covariate2‘, ...]:
    matched_treated = matched_df[matched_df[‘treatment‘]==1][col]
    matched_control = matched_df[matched_df[‘treatment‘]==0][col]

    plt.figure(figsize=(8,4))
    sns.histplot(matched_treated, color=‘b‘, label=‘Treated‘)
    sns.histplot(matched_control, color=‘r‘, label=‘Control‘)
    plt.legend()
    plt.title(f‘Distribution of {col}‘)
    plt.show()
```

If the matching was successful, the distributions should be very similar between the two groups. If not, you may need to modify your propensity model, matching algorithm or caliper until you achieve satisfactory balance.

### Step 5: Estimate treatment effect

Finally, we can estimate the average treatment effect on the treated (ATT) by comparing outcomes between the matched treated and control groups. For a continuous outcome, we can use a simple difference in means. For a binary outcome, we can calculate the difference in proportions or odds ratio.

```
matched_treated_outcome = matched_df[matched_df[‘treatment‘]==1][‘outcome‘]
matched_control_outcome = matched_df[matched_df[‘treatment‘]==0][‘outcome‘]

ATT = matched_treated_outcome.mean() - matched_control_outcome.mean()
print(f‘The average treatment effect on the treated is: {ATT:.3f}‘)
```

The ATT estimates the average effect of the treatment on those subjects who actually received the treatment. This is often the most relevant estimand for policy and decision-making, but it‘s important to be clear about what you are estimating and not to generalize beyond the population represented by the treated units.

## Advanced Topics and Extensions

We‘ve covered the basic framework for propensity score matching, but there are many ways to extend and refine this approach. Here are a few advanced topics to consider:

- Alternative matching algorithms: In addition to nearest neighbor matching, you can use other methods like optimal matching, genetic matching, or full matching to create balanced treated and control groups. These approaches aim to minimize the overall distance between matched units while maintaining covariate balance.
- Matching with multiple treatments: Propensity score matching can be extended to situations with more than two treatment conditions. The most common approach is to estimate separate propensity scores for each treatment relative to the control condition, and then match units within each treatment group.
- Doubly robust estimation: Combining propensity score matching with regression adjustment can provide additional protection against model misspecification. Doubly robust estimators use both the propensity score and the outcome model to estimate treatment effects, and are consistent as long as at least one of the models is correctly specified.
- Sensitivity analysis: Because propensity score matching relies on the assumption of no unobserved confounders, it‘s important to assess the sensitivity of your results to potential violations of this assumption. One approach is to simulate the presence of an unobserved confounder and see how much it would have to influence treatment assignment and outcomes to alter your conclusions.

## Limitations and Caveats

While propensity score matching is a powerful tool for causal inference, it‘s not a panacea. Here are some important limitations and caveats to keep in mind:

- Propensity score matching can only adjust for observed confounders that are included in the propensity model. If there are unobserved variables that influence both treatment assignment and outcomes, the resulting estimates of treatment effects may be biased.
- The quality of matches and the resulting balance of covariates depends on the specification of the propensity model. If important confounders are omitted or the functional form is misspecified, the matching may not adequately control for confounding.
- Propensity score matching can only be used to estimate treatment effects for the treated units that have suitable matches in the control group. If there is insufficient overlap in the distribution of propensity scores, some treated units may be excluded from the analysis, limiting the generalizability of the results.
- Propensity score matching may be sensitive to the choice of caliper and other matching parameters. It‘s important to assess the robustness of your results to different specifications and to report the sensitivity of your estimates to these choices.
- Propensity score matching is not a substitute for careful study design and data collection. The validity of the resulting estimates depends crucially on the quality and completeness of the observed covariates used to estimate the propensity scores.

## Conclusion

Propensity score matching is a valuable tool for estimating causal effects from observational data when randomized experiments are not feasible. By creating balanced treatment and control groups based on observed covariates, propensity score matching can help to isolate the effect of a treatment or intervention and provide more reliable evidence for decision-making.

In this article, we‘ve taken a deep dive into the theory and practice of propensity score matching, with a focus on implementation in Python. We walked through each step of the process, from preparing the data and estimating propensity scores to matching units and estimating treatment effects. We also discussed some advanced topics and extensions, as well as important limitations and caveats to keep in mind.

Whether you‘re a researcher, data scientist, or analyst, we hope this guide has given you a solid foundation in propensity score matching and the tools to apply it to your own work. As with any statistical method, it‘s important to use propensity score matching thoughtfully and transparently, and to be clear about the assumptions and limitations of your analysis.

If you‘re interested in learning more about propensity score matching and related methods for causal inference, there are many excellent resources available. Some good places to start are:

- The [MatchIt](https://cran.r-project.org/web/packages/MatchIt/MatchIt.pdf) package in R, which provides a comprehensive suite of tools for propensity score matching and other matching methods.
- The [Causal Inference Book](https://www.hsph.harvard.edu/miguel-hernan/causal-inference-book/) by Miguel Hernán and James Robins, which provides a rigorous introduction to the principles and methods of causal inference from observational data.
- The [Causal Inference Bootcamp](https://www.youtube.com/watch?v=rHCcZGx1rMI) video series by Stephen Slade, which covers a range of topics in causal inference with an emphasis on practical applications.

Happy matching!

---

Source: [An In\-Depth Guide to Synthetic Control Using Propensity Score Matching in Python](https://33rdsquare.com/introduction-to-synthetic-control-using-propensity-score-matching/)
