Chapter 5 of 6

Python Code

Each snippet is self-contained — copy it into a .py file or Jupyter notebook and run it directly.

pandas diff-diff matplotlib

Estimate the DiD effect using the diff-diff TwoWayFixedEffects estimator.

Ch 2

Two-Way Fixed Effects with diff-diff

import numpy as np
import pandas as pd
from diff_diff import TwoWayFixedEffects

# Hypothetical panel: 2 leagues x 4 seasons.
# EPL (treated) gets a timekeeping rule from season 23/24 onward.
data = pd.DataFrame({
    "league":  ["epl"]*4 + ["bundesliga"]*4,
    "season":  [1, 2, 3, 4] * 2,
    "goals":   [1.4092, 1.4263, 1.6395, 1.4671,
                1.5588, 1.5866, 1.6095, 1.5670],
    "treated": [1, 1, 1, 1,
                0, 0, 0, 0],
    "post":    [0, 0, 1, 1] * 2,
})

# Two-way fixed effects: unit = league, time = season
twfe_model = TwoWayFixedEffects()
model = twfe_model.fit(
    data,
    outcome="goals",
    unit="league",
    time="post",
    treatment="treated",
)

result = model.summary()
print(result)

Visualise the treated vs control trajectories around the treatment season.

Ch 1

Parallel Trends Plot

import pandas as pd
import matplotlib.pyplot as plt

data = pd.DataFrame({
    "league":  ["epl"]*4 + ["bundesliga"]*4,
    "season":  ["21/22", "22/23", "23/24", "24/25"] * 2,
    "goals":   [1.4092, 1.4263, 1.6395, 1.4671,
                1.5588, 1.5866, 1.6095, 1.5670],
})

pivot = data.pivot(index="season", columns="league", values="goals")

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(pivot.index, pivot["epl"], "-o",
        color="#3B82F6", linewidth=2, label="EPL (treated)")
ax.plot(pivot.index, pivot["bundesliga"], "-o",
        color="#EF4444", linewidth=2, label="Bundesliga (control)")

# Treatment introduced from season 23/24
ax.axvline(x="23/24", color="#6B7280", linestyle="--",
           linewidth=1, label="Rule introduced")

ax.set_xlabel("Season", fontsize=12)
ax.set_ylabel("Avg. goals per club per game", fontsize=12)
ax.set_title("EPL Timekeeping Rule — Parallel Trends", fontsize=14)
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()

Built with SvelteKit + D3.js