Skip to content

Commit 389cbf2

Browse files
committed
Fix Y permutation error, implement placebo SEs
1 parent 683f3fd commit 389cbf2

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

src/SyntheticDiD.jl

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ a unit weight and λ is a time period weight.
1212
1313
The implementation follows the author's reference implementation in the R package `synthdid`.
1414
15+
NOTE: The implementation assumes that the outcomes and treatment matrices are sorted such that
16+
treated units come last in both `Y` and `W`. It therefore checks whether `W` is sorted and swaps
17+
rows in both `Y` and `W` to sort both matrices accordingly if required.
18+
1519
"""
1620
struct SyntheticDiD{T1}# <: SCM where T1
1721
tp::T1
@@ -35,27 +39,63 @@ function isfitted(x::SyntheticDiD)
3539
all(!iszero, x.τ̂) || all(!iszero, x.se_τ̂)
3640
end
3741

38-
function fit!(x::SyntheticDiD{BalancedPanel{SingleUnitTreatment{Continuous}}})
42+
function fit!(x::SyntheticDiD{BalancedPanel{SingleUnitTreatment{Continuous}}};
43+
se = nothing)
3944
(;Y, W) = x.tp
4045

4146
T₀ = length_T₀(x.tp)
4247
N₁ = 1 # length(treated_ids(x)) - has to be one given type of BalancedPanel in this method
4348
N₀ = size(Y, 1) - N₁
4449

4550
# Implementation of synthdid below assumes that treated unit comes last
46-
swaprows!(Y, treated_ids(x.tp), size(Y, 1))
51+
if !issorted(any(x) for x eachrow(W))
52+
treated_row = treated_ids(x.tp)
53+
swaprows!(Y, treated_row, size(Y, 1))
54+
swaprows!(W, treated_row, size(W, 1))
55+
end
4756

4857
# Get estimate and store in SyntheticDiD object
4958
estimate = synthdid_estimate(Y, N₀, T₀)
5059
x.τ̂ .= estimate.τ̂
5160
x.λ̂ .= estimate.λ̂
5261
x.ω̂ .= estimate.ω̂
5362

63+
if !isnothing(se)
64+
if se != :placebo
65+
error("Only placebo standard error estimation is currently implemented")
66+
else
67+
= get_V̂_τ(x).
68+
x.se_τ̂ .= (V̂)
69+
end
70+
end
71+
5472
# return SynhtDiD object
5573
return x
5674
end
5775

5876

77+
function get_V̂_τ(x::SyntheticDiD{BalancedPanel{SingleUnitTreatment{Continuous}}})
78+
(;Y, W) = x.tp
79+
80+
Y_untreated = Y[Not(treated_ids(x.tp)), :]
81+
82+
T₀ = length_T₀(x.tp)
83+
N₁ = 1
84+
N₀ = size(Y_untreated, 1) - N₁
85+
86+
τ̂s = zeros(N₀)
87+
88+
for i 1:N₀-1
89+
swaprows!(Y_untreated, i, size(Y_untreated, 1))
90+
τ̂s[i] = synthdid_estimate(Y_untreated, N₀, T₀).τ̂
91+
end
92+
93+
swaprows!(Y_untreated, 1, size(Y_untreated, 1))
94+
τ̂s[end] = synthdid_estimate(Y_untreated, N₀, T₀).τ̂
95+
96+
return (V̂ = var(τ̂s), τ̂s = τ̂s)
97+
end
98+
5999
function synthdid_estimate(Y, N₀, T₀;
60100
ω_intercept = true, λ_intercept = true,
61101
max_iter = 10_000, max_iter_pre_sparsify = 100, sparsify_function = sparsify_weights!)
@@ -196,7 +236,7 @@ end
196236

197237

198238
function swaprows!(X::AbstractMatrix, i::Integer, j::Integer)
199-
@inbounds for k = 1:size(X,2)
239+
@inbounds for k = axes(X,2)
200240
X[i, k], X[j, k] = X[j, k], X[i, k]
201241
end
202242
end

test/runtests.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ end
3939
fit!(sdid_model)
4040

4141
@test only(sdid_model.τ̂) -15.604 atol=0.1
42+
43+
@test_throws "placebo" fit!(sdid_model, se = :jackknife)
44+
45+
fit!(sdid_model, se = :placebo)
46+
47+
@test only(sdid_model.se_τ̂) 9.3 atol=0.1
4248
end
4349

4450

0 commit comments

Comments
 (0)