DataFrame Panels

MSC.jl includes a small long-panel helper for common-adoption designs. Treatment must be binary, controls must never be treated, and treated units must remain treated after the first post-treatment period.

using MSC
using DataFrames

sim = simulate_msc(ncontrols = 12, ntreated = 3, T0 = 40, T1 = 5, tau = 2.0, seed = 4)

rows = NamedTuple[]
for i in eachindex(sim.units), t in eachindex(sim.times)
    push!(
        rows,
        (
            unit = sim.units[i],
            time = sim.times[t],
            outcome = sim.Y[i, t],
            treated = sim.W[i, t],
        ),
    )
end

df = DataFrame(rows)
first(df, 5)
5×4 DataFrame
Rowunittimeoutcometreated
StringInt64Float64Float64
1control_110.766730.0
2control_123.514150.0
3control_13-2.873690.0
4control_142.715140.0
5control_15-2.180760.0

Convert the long table to matrix form:

panel = panel_matrices(df, :unit, :time, :outcome, :treated)

(size(panel.Y), panel.N0, panel.T0)
((15, 45), 12, 40)

Estimate directly from the table:

est = msc_estimate(
    df,
    :unit,
    :time,
    :outcome,
    :treated;
    lambdas = [0.002],
    max_iter = 1000,
    tol = 1e-6,
)

round(est.estimate, digits = 3)
2.031

The table labels are preserved for diagnostics:

first(weights_table(est; atol = 1e-8), 10)
10×3 DataFrame
Rowcontroltreatedweight
AnyAnyFloat64
1control_1treated_10.0422069
2control_12treated_1-0.0635816
3control_3treated_10.34683
4control_4treated_10.0660343
5control_5treated_10.0501473
6control_6treated_1-0.114599
7control_7treated_1-0.0236845
8control_8treated_1-0.151047
9control_9treated_10.30177
10control_1treated_20.154887