Part IV · Integration Week 6 Published

Panel DML and Rolling Window Methods

DML for panel data: the within (fixed-effects) transformation, Panel DML combining fixed effects with ML nuisance models, cluster-robust standard errors, Rolling Window DML for time-varying effects and structural-break detection, with insurance and macro applications.

On this page
  1. Introduction
  2. The panel data advantage
  3. Chapter roadmap
  4. Panel data fundamentals
  5. Fixed effects model
  6. Two-way fixed effects
  7. Panel DML
  8. The problem with fixed effects + ML
  9. Implementation
  10. Monte Carlo comparison
  11. Cluster-robust inference
  12. Why clustering matters
  13. Implementation details
  14. Rolling Window DML
  15. Time-varying treatment effects
  16. Implementation
  17. Detecting structural breaks
  18. Applications
  19. Application 1: insurance pricing
  20. Application 2: macroeconomic policy
  21. Exercises
  22. Exercise 6.1: within transformation
  23. Exercise 6.2: cluster-robust SE
  24. Recursive Dynamic G-Estimation
  25. Structural Nested Mean Models and the Blip
  26. Neyman-Orthogonal Recursive Peeling
  27. Implementation
  28. Summary
  29. Roadmap to Chapter 7

Panel DML and Rolling Window Methods

Introduction

Chapter 5 introduced TemporalPLRDML for single time series with autocorrelation. Many economic applications involve panel data — repeated observations on multiple units over time. This chapter develops DML methods for panel data, addressing both fixed effects and time-varying treatment effects.

The panel data advantage

Panel data combines cross-sectional and time series variation:

Yit=τTit+g(Xit)+αi+γt+εitY_{it} = \tau \cdot T_{it} + g(X_{it}) + \alpha_i + \gamma_t + \varepsilon_{it}

where:

  • i=1,,Ni = 1, \ldots, N indexes individuals (e.g., states, firms, customers).
  • t=1,,Tt = 1, \ldots, T indexes time periods.
  • αi\alpha_i is an individual-specific fixed effect.
  • γt\gamma_t is a time-specific fixed effect.

Chapter roadmap

  • Section 6.2: panel data fundamentals — fixed effects and within transformation.
  • Section 6.3: Panel DML — combining fixed effects with machine learning.
  • Section 6.4: cluster-robust inference — accounting for within-unit correlation.
  • Section 6.5: Rolling Window DML — time-varying treatment effects.
  • Section 6.6: applications.
  • Section 6.7: exercises.

Panel data fundamentals

Fixed effects model

The fixed effects model assumes individual-specific intercepts:

Yit=τTit+Xitβ+αi+εitY_{it} = \tau \cdot T_{it} + X_{it}'\beta + \alpha_i + \varepsilon_{it}

The key identification challenge: if αi\alpha_i correlates with TitT_{it} (e.g., more productive firms invest more), OLS on pooled data is biased.

Definition 6.1 (Within Transformation).

The within transformation demeans each variable by its individual-specific mean:

Y¨it=YitYˉiwhereYˉi=1Tt=1TYitT¨it=TitTˉiX¨it=XitXˉi\begin{aligned} \ddot{Y}_{it} &= Y_{it} - \bar{Y}_i \quad \text{where} \quad \bar{Y}_i = \frac{1}{T}\sum_{t=1}^{T} Y_{it} \\ \ddot{T}_{it} &= T_{it} - \bar{T}_i \\ \ddot{X}_{it} &= X_{it} - \bar{X}_i \end{aligned}

This eliminates αi\alpha_i since α¨i=αiαi=0\ddot{\alpha}_i = \alpha_i - \alpha_i = 0.

After within transformation:

Y¨it=τT¨it+X¨itβ+ε¨it\ddot{Y}_{it} = \tau \cdot \ddot{T}_{it} + \ddot{X}_{it}'\beta + \ddot{\varepsilon}_{it}

OLS on the transformed data yields the fixed effects estimator.

Two-way fixed effects

When both individual and time effects matter:

Yit=τTit+Xitβ+αi+γt+εitY_{it} = \tau \cdot T_{it} + X_{it}'\beta + \alpha_i + \gamma_t + \varepsilon_{it}

The two-way within transformation:

Y¨it=YitYˉiYˉt+Yˉ\ddot{Y}_{it} = Y_{it} - \bar{Y}_i - \bar{Y}_t + \bar{Y}

where Yˉt\bar{Y}_t is the time-specific mean and Yˉ\bar{Y} is the grand mean.

Remark.

Two-way fixed effects are particularly important for policy evaluation, where both cross-sectional heterogeneity (states differ) and common time shocks (recessions affect everyone) may confound treatment effects.

Panel DML

The problem with fixed effects + ML

Standard fixed effects require:

  1. Linear functional form: g(Xit)=Xitβg(X_{it}) = X_{it}'\beta.
  2. Known confounders: all relevant XitX_{it} included.

DML relaxes the linearity assumption, but combining ML with fixed effects requires care.

Algorithm: Panel DML with Fixed Effects
Require: Panel data (Y_it, T_it, X_it) for i=1..N, t=1..T
Ensure: Treatment effect τ̂ with cluster-robust SE

1. Apply fixed effects transformation:
   for each variable V in {Y, T, X}:
       individual means:  V̄_i = (1/T) Σ_t V_it
       demean:            V̈_it = V_it - V̄_i
2. Run TemporalPLRDML on transformed data:
       τ̂ ← TemporalPLRDML(Ÿ, T̈, Ẍ)
3. Compute cluster-robust standard errors:
   for each individual i:
       cluster-level influence:  Ψ_i = Σ_t ψ_it
   cluster variance:  V̂_cluster = N/(N-1) · (1/n²) Σ_i Ψ_i²   (n = total retained observations)
   standard error:    SE = sqrt(V̂_cluster)
return τ̂, SE

Implementation

from dml_ts import PanelDML
import numpy as np

# Generate panel data
np.random.seed(42)
n_individuals = 100
n_periods = 20
n_total = n_individuals * n_periods

# Create IDs
individual_id = np.repeat(np.arange(n_individuals), n_periods)
time_id = np.tile(np.arange(n_periods), n_individuals)

# Individual fixed effects (unobserved)
alpha_i = np.random.randn(n_individuals)
alpha_expanded = alpha_i[individual_id]

# Covariates and treatment (correlated with fixed effects!)
X = np.random.randn(n_total, 3)
T = 0.5 * X[:, 0] + 0.3 * alpha_expanded + np.random.randn(n_total)

# Outcome with true effect tau = 2.0
true_tau = 2.0
Y = true_tau * T + X[:, 0]**2 + alpha_expanded + np.random.randn(n_total)

# Without fixed effects: BIASED
from dml_ts import TemporalPLRDML
naive_model = TemporalPLRDML(n_lags=0, model_y="ridge", model_t="ridge")
naive_result = naive_model.fit(Y, T, X)
print(f"Naive (no FE): theta = {naive_result.theta:.3f}")  # Biased!

# With fixed effects: UNBIASED
panel_model = PanelDML(
    fixed_effects="individual",
    cluster_se=True,
    model_y="ridge",
    model_t="ridge"
)
panel_result = panel_model.fit(Y, T, X, individual_id, time_id)
print(f"Panel DML:     theta = {panel_result.theta:.3f}")  # Close to 2.0

Monte Carlo comparison

Panel DML vs. naive DML: Monte Carlo results

MethodBiasAvg. SECoverage
Naive DML (no FE)+0.48+0.480.0400%
Panel DML (individual FE)+0.01+0.010.04690%
Panel DML (two-way FE)+0.01+0.010.04690%

Generated by scripts/mc_panel_dml_table.py (100 simulations, 40 units × 15 periods, true τ=2\tau = 2, ridge nuisances, cluster-robust SEs). The 90% coverage at nominal 95% reflects normal critical values with a moderate cluster count (Remark below); tN1t_{N-1} critical values close most of the gap.

Cluster-robust inference

Why clustering matters

Within each individual, observations are typically correlated:

Cov(εit,εis)0for ts\mathrm{Cov}(\varepsilon_{it}, \varepsilon_{is}) \neq 0 \quad \text{for } t \neq s

This violates the i.i.d. assumption and inflates standard errors.

Definition 6.2 (Cluster-Robust Variance).

Since τ^τ01niΨi\hat{\tau} - \tau_0 \approx \frac{1}{n}\sum_i \Psi_i with nn the total number of retained observations, the cluster-robust (CR1) variance of τ^\hat{\tau} with NN clusters is:

V^CR=NN11n2i=1NΨi2\hat{V}_{\text{CR}} = \frac{N}{N-1} \cdot \frac{1}{n^2} \sum_{i=1}^{N} \Psi_i^2

where Ψi=t=1Tiψit\Psi_i = \sum_{t=1}^{T_i} \psi_{it} is the sum of influence scores within cluster ii. No centering term appears because iΨi=itψit=0\sum_i \Psi_i = \sum_{it} \psi_{it} = 0 exactly, by the first-order condition of the FWL estimator; NN counts only clusters that retain observations after temporal-CV trimming.

Theorem 6.3 (Cluster-Robust Inference).

Under regularity conditions with NN \to \infty clusters:

τ^τ0SE^CRdN(0,1)\frac{\hat{\tau} - \tau_0}{\widehat{SE}_{\text{CR}}} \xrightarrow{d} N(0, 1)

where SE^CR=V^CR\widehat{SE}_{\text{CR}} = \sqrt{\hat{V}_{\text{CR}}} (V^CR\hat{V}_{\text{CR}} already estimates the variance of τ^\hat{\tau}; dividing by NN again is the sums-as-means scale error this companion once shipped).

Remark.

The cluster-robust variance is valid whether or not within-cluster correlation exists; with independent observations it converges to the heteroskedasticity-only variance. Two practical caveats: with few clusters, normal critical values are mildly anticonservative (tN1t_{N-1} critical values are the standard refinement; Cameron & Miller 2015), and clusters whose observations are entirely dropped by temporal-CV trimming carry no information — the companion excludes them from NN and warns.

Implementation details

def cluster_robust_se(influence_scores, cluster_ids):
    """
    Compute cluster-robust standard error.

    Parameters
    ----------
    influence_scores : array, shape (n,)
        Influence function values for each observation
    cluster_ids : array, shape (n,)
        Cluster identifiers

    Returns
    -------
    se : float
        Cluster-robust standard error
    """
    unique_clusters = np.unique(cluster_ids)
    N = len(unique_clusters)

    # Sum influence scores within each cluster
    cluster_sums = np.zeros(N)
    for i, cluster in enumerate(unique_clusters):
        mask = cluster_ids == cluster
        cluster_sums[i] = np.sum(influence_scores[mask])

    # CR1: V = N/(N-1) * sum(Psi_i^2) / n^2 — the sums are already
    # centered (sum of all influence scores is 0 by FWL construction).
    n = len(influence_scores)
    cluster_var = (N / (N - 1)) * np.sum(cluster_sums**2) / n**2

    return np.sqrt(cluster_var)

Rolling Window DML

Time-varying treatment effects

In many applications, treatment effects change over time:

  • Policy effects may fade or strengthen.
  • Market conditions evolve (e.g., competition intensity).
  • Learning effects (customers adapt to prices).

Rolling window DML estimates local treatment effects:

τ(t)=ATE in window [tw/2,t+w/2]\tau(t) = \text{ATE in window } [t - w/2, t + w/2]
Definition 6.4 (Rolling Window DML).

For window size ww and step size ss:

  1. For each center t=w/2,w/2+s,w/2+2s,t = w/2, w/2 + s, w/2 + 2s, \ldots
  2. Select observations in [tw/2,t+w/2][t - w/2, t + w/2].
  3. Estimate DML on window data.
  4. Store τ^(t)\hat{\tau}(t) and SE^(t)\widehat{SE}(t).

Implementation

from dml_ts import RollingWindowDML

# Fit rolling window DML
model = RollingWindowDML(
    window_size=100,   # 100 observations per window
    step_size=20,      # Move 20 observations between estimates
    model_y="ridge",
    model_t="ridge"
)

model.fit(Y, T, X, time_index=time)

# Get time series of local effects
time_centers, theta_series, se_series = model.get_effects()

# Plot time-varying effects
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 5))
plt.plot(time_centers, theta_series, 'b-', label='Local ATE')
plt.fill_between(
    time_centers,
    theta_series - 1.96 * se_series,
    theta_series + 1.96 * se_series,
    alpha=0.3
)
plt.axhline(true_tau, color='r', linestyle='--', label='True ATE')
plt.xlabel('Time')
plt.ylabel('Treatment Effect')
plt.legend()
plt.title('Rolling Window DML: Time-Varying Treatment Effect')
plt.show()

Detecting structural breaks

Rolling window DML naturally detects structural breaks — points where treatment effects change abruptly.

Definition 6.5 (Structural Break Detection).

A structural break at time tt^* is detected if:

τ^(t+s)τ^(ts)SE^(t+s)2+SE^(ts)2>z1α/2\frac{|\hat{\tau}(t^* + s) - \hat{\tau}(t^* - s)|}{\sqrt{\widehat{SE}(t^*+s)^2 + \widehat{SE}(t^*-s)^2}} > z_{1-\alpha/2}

where z1α/2z_{1-\alpha/2} is the critical value.

Remark.

For formal structural break testing, use the Chow test or Bai–Perron procedure. Rolling window DML provides visual diagnostics and approximate break detection.

Applications

Application 1: insurance pricing

Setting: N=50N = 50 states, T=10T = 10 years, estimating the effect of competitor price changes on market share.

Panel structure:

  • Individual FE: state-specific demand (regulation, demographics).
  • Time FE: macroeconomic conditions affecting all states.
  • Cluster SE: observations within a state are correlated.

Rolling window: detect if price sensitivity changed after major regulatory reform.

Application 2: macroeconomic policy

Setting: effect of monetary policy on output across N=30N = 30 countries, T=40T = 40 quarters.

Panel structure:

  • Individual FE: country-specific institutions.
  • Time FE: global business cycle.
  • Two-way FE needed to avoid confounding.

Rolling window: test if policy effectiveness changed after the 2008 financial crisis.

Exercises

Exercise 6.1: within transformation

Consider panel data with N=3N=3 individuals and T=2T=2 periods:

iittYitY_{it}TitT_{it}αi\alpha_i
11512
12722
21311
22411
31823
321243

(a) Compute the within-transformed variables Y¨it\ddot{Y}_{it} and T¨it\ddot{T}_{it}. (b) Estimate τ\tau using OLS on the transformed data. (c) What is the true τ\tau in this DGP?

Solution.

(a) Individual means: Yˉ1=6\bar{Y}_1 = 6, Yˉ2=3.5\bar{Y}_2 = 3.5, Yˉ3=10\bar{Y}_3 = 10. Within-transformed YY: Y¨=(1,1,0.5,0.5,2,2)\ddot{Y} = (-1, 1, -0.5, 0.5, -2, 2)'. Similarly for TT: Tˉ1=1.5\bar{T}_1 = 1.5, Tˉ2=1\bar{T}_2 = 1, Tˉ3=3\bar{T}_3 = 3, giving T¨=(0.5,0.5,0,0,1,1)\ddot{T} = (-0.5, 0.5, 0, 0, -1, 1)'.

(b) OLS on the transformed data: τ^=Y¨T¨T¨2=0.5+0.5+0+0+2+20.25+0.25+0+0+1+1=52.5=2.0\hat{\tau} = \frac{\sum \ddot{Y} \ddot{T}}{\sum \ddot{T}^2} = \frac{0.5 + 0.5 + 0 + 0 + 2 + 2}{0.25 + 0.25 + 0 + 0 + 1 + 1} = \frac{5}{2.5} = 2.0.

(c) The true τ=2.0\tau = 2.0 (coefficient on TT in the DGP).

Exercise 6.2: cluster-robust SE

You estimate τ^=1.5\hat{\tau} = 1.5 from n=40n = 40 observations in N=4N=4 clusters, with cluster-level influence sums

Ψ1=0.2,Ψ2=0.1,Ψ3=0.3,Ψ4=0.4\Psi_1 = 0.2, \quad \Psi_2 = -0.1, \quad \Psi_3 = 0.3, \quad \Psi_4 = -0.4

(they sum to zero, as FWL guarantees).

(a) Compute the cluster variance V^CR\hat{V}_{\text{CR}}. (b) Compute the cluster-robust standard error. (c) Construct a 95% confidence interval.

Solution.

(a) iΨi2=0.04+0.01+0.09+0.16=0.30\sum_i \Psi_i^2 = 0.04 + 0.01 + 0.09 + 0.16 = 0.30. V^CR=430.30402=0.401600=0.00025\hat{V}_{\text{CR}} = \frac{4}{3} \cdot \frac{0.30}{40^2} = \frac{0.40}{1600} = 0.00025.

(b) SE^=0.00025=0.0158\widehat{SE} = \sqrt{0.00025} = 0.0158.

(c) 1.5±1.96×0.0158=[1.469,1.531]1.5 \pm 1.96 \times 0.0158 = [1.469, 1.531].

Recursive Dynamic G-Estimation

The estimators so far return a single scalar effect. Many dynamic problems instead ask for a sequence of period-specific effects: when a treatment is applied over mm periods and can alter both the outcome and the future state of the treated unit, what is the effect of the period-τ\tau treatment? This is the dynamic treatment-effect setting of Lewis and Syrgkanis (2021), and the companion class DynamicGEstimationDML implements their recursive g-estimation.

Structural Nested Mean Models and the Blip

Consider nn i.i.d. trajectories, each observed over mm periods, with treatment TτT_\tau, evolving controls (state) XτX_\tau, and a final outcome YY. Crucially, the state may depend on past treatment,

Xτ=AXτ1+bTτ1+ετ,X_\tau = A X_{\tau-1} + b\,T_{\tau-1} + \varepsilon_\tau,

so that past treatment confounds both future treatment and the outcome — the time-varying confounding that ordinary adjustment for XX mishandles (conditioning on a post-treatment state blocks part of the effect and opens collider paths). A linear structural nested mean model writes the effect of the period-τ\tau treatment on the final outcome as a constant blip θτ\theta_\tau:

Y=τ=1mθτTτ+g(X)+U,E[UHτ,Tτ]=E[UHτ],Y = \sum_{\tau=1}^{m} \theta_\tau\, T_\tau + g(X) + U, \qquad \mathbb{E}[U \mid H_\tau, T_\tau] = \mathbb{E}[U \mid H_\tau],

where Hτ=(X1:τ,T1:τ1)H_\tau = (X_{1:\tau}, T_{1:\tau-1}) is the history available before TτT_\tau. Sequential conditional exchangeability given HτH_\tau identifies θτ\theta_\tau.

Neyman-Orthogonal Recursive Peeling

Lewis and Syrgkanis give a cross-fitted, Neyman-orthogonal g-estimator — a dynamic generalization of Robinson’s partially linear model — that peels the blips off the outcome from the last period backward. Let MτM_\tau denote the residual-maker that partials out HτH_\tau, with the conditional means E[Hτ]\mathbb{E}[\,\cdot \mid H_\tau] estimated by cross-fitting arbitrary machine-learning nuisances. For τ=m,m1,,1\tau = m, m-1, \dots, 1:

  1. Residualize on HτH_\tau: Y~(τ)=Mτ(Ys>τθsTs)\tilde{Y}^{(\tau)} = M_\tau(Y - \sum_{s>\tau} \theta_s T_s) and T~τ=MτTτ\tilde{T}_\tau = M_\tau T_\tau.
  2. Solve the orthogonal moment: θτ=T~τ,Y~(τ)/T~τ,T~τ\theta_\tau = \langle \tilde{T}_\tau, \tilde{Y}^{(\tau)} \rangle / \langle \tilde{T}_\tau, \tilde{T}_\tau \rangle.
  3. Peel period τ\tau off the outcome and recurse to τ1\tau-1.

Inference uses the joint sandwich variance over the stacked moments. Because later-stage estimates enter earlier stages, Neyman orthogonality does not hold across the structural parameters: the Jacobian is triangular and the per-period variances are coupled. The implementation forms the full joint covariance rather than treating each period in isolation, with the sandwich “meat” clustered by unit (panel) or HAC/Newey–West (single series).

Implementation

from dml_ts import DynamicGEstimationDML
from dml_ts.validation import DynamicTreatmentDGP

# Known per-period blips with treatment-dependent state (time-varying confounding)
dgp = DynamicTreatmentDGP(
    n_periods=3, theta_t=[1.0, 0.5, 1.5], n_units=1500, p=3,
    state_feedback=True, treatment_state_coef=0.8, random_state=7,
)
data = dgp.generate()  # data.theta_t == [1.96, 2.90, 1.50] (total blips)

# Linear confounding here, so ridge nuisances are correctly specified
est = DynamicGEstimationDML(model_y="ridge", model_t="ridge", random_state=0)
result = est.fit(data.Y, data.T, data.X, groups=data.groups)
print(result.summary())

The estimator recovers the true total blips (1.96,2.90,1.50)(1.96, 2.90, 1.50) — the direct coefficients plus the indirect path through the treatment-dependent state — to within Monte Carlo error, whereas a naive pooled regression that ignores the sequential structure is badly biased. A reference path, fit_econml_reference, wraps EconML’s DynamicDML (the reference Lewis–Syrgkanis implementation, optional via the full extra) and agrees with the native estimator to within sampling error — a custom-versus-reference numerical cross-check.

A single long, stationary series is also supported (the series mode), where the blips become distributed-lag effects Yt=kθk+1Ttk+g(Xt)+UtY_t = \sum_k \theta_{k+1}\, T_{t-k} + g(X_t) + U_t estimated with HAC inference.

Summary

This chapter extended DML to panel data and time-varying effects:

  1. Panel DML: the within transformation eliminates fixed effects while preserving ML flexibility for nuisance functions.
  2. Cluster-robust SE: accounting for within-cluster correlation prevents underestimated standard errors.
  3. Rolling Window DML: local estimation reveals how treatment effects change over time.
  4. Structural breaks: rolling windows provide visual and approximate detection of regime changes.

Key takeaways:

  • Always use fixed effects when unobserved heterogeneity correlates with treatment.
  • Cluster standard errors by the unit of treatment assignment.
  • Use rolling windows to test the stability of treatment effects.
  • Two-way fixed effects handle both cross-sectional and time confounders.

Roadmap to Chapter 7

Chapter 7 integrates our methods with real macroeconomic data from FRED (Federal Reserve Economic Data). We develop:

  1. FRED data loader: automated retrieval of macro indicators.
  2. Time alignment: handling mixed-frequency data.
  3. Macro control variables: GDP, CPI, interest rates as confounders.
  4. Application: policy effect estimation with real data.

This bridges the gap from synthetic validation to audited applications with real economic data.