Skip to content
This repository was archived by the owner on Mar 11, 2022. It is now read-only.

Commit 8879696

Browse files
authored
Support ScaledArray and update dependency (#7)
1 parent b67d3e5 commit 8879696

File tree

7 files changed

+126
-68
lines changed

7 files changed

+126
-68
lines changed

Project.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ authors = ["Junyuan Chen <norman.chen.git@gmail.com>"]
44
version = "0.2.0"
55

66
[deps]
7+
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
78
DiffinDiffsBase = "7fc23ce2-6e83-4e3c-822f-a79085ccb3e8"
89
FixedEffectModels = "9d5cd8c9-2029-5cab-9928-427838db53e3"
910
FixedEffects = "c8885935-8500-56a7-9867-7708b20db0eb"
@@ -16,9 +17,10 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1617
Vcov = "ec2bfdc2-55df-4fc9-b9ae-4958c2cf2486"
1718

1819
[compat]
19-
DataFrames = "0.22"
20-
DiffinDiffsBase = "0.3"
21-
FixedEffectModels = "1.4"
20+
DataAPI = "1.5"
21+
DataFrames = "1"
22+
DiffinDiffsBase = "0.3.2"
23+
FixedEffectModels = "1.5"
2224
FixedEffects = "2"
2325
Reexport = "0.2, 1"
2426
StatsBase = "0.33"
@@ -30,7 +32,8 @@ julia = "1.3"
3032

3133
[extras]
3234
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
35+
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
3336
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3437

3538
[targets]
36-
test = ["DataFrames", "Test"]
39+
test = ["DataFrames", "Dates", "Test"]

src/InteractionWeightedDIDs.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
module InteractionWeightedDIDs
22

33
using Base: Callable
4+
using DataAPI: refarray
45
using FixedEffectModels: FixedEffectTerm, Combination,
5-
fe, _parse_fixedeffect, basecol, isnested, nunique
6+
fe, _parse_fixedeffect, invsym!, isnested, nunique
67
using FixedEffects
7-
using LinearAlgebra: Cholesky, Factorization, Symmetric, cholesky!
8+
using LinearAlgebra: Cholesky, Factorization, Symmetric, cholesky!, diag
89
using Reexport
910
using StatsBase: AbstractWeights, CovarianceEstimator, UnitWeights, PValue, TestStat, NoQuote
1011
using StatsFuns: fdistccdf
@@ -13,7 +14,7 @@ using Tables
1314
using Tables: getcolumn, columnnames
1415
using Vcov
1516
@reexport using DiffinDiffsBase
16-
using DiffinDiffsBase: TimeType, termvars, isintercept, parse_intercept!,
17+
using DiffinDiffsBase: ValidTimeType, termvars, isintercept, parse_intercept!,
1718
_count!, _groupfind, _treatnames, _parse_bycells!, _parse_subset
1819

1920
import Base: show

src/did.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function result(::Type{Reg}, @nospecialize(nt::NamedTuple))
7878
yterm = nt.yxterms[nt.yterm]
7979
yname = coefnames(yterm)
8080
cnames = _treatnames(nt.treatcells)
81-
cnames = append!(cnames, coefnames.(nt.xterms))[nt.basecols]
81+
cnames = append!(cnames, coefnames.(nt.xterms))[nt.basiscols]
8282
coefinds = Dict(cnames .=> 1:length(cnames))
8383
didresult = RegressionBasedDIDResult{typeof(nt.tr), nt.cohortinteracted}(
8484
nt.coef, nt.vcov_mat, nt.vce, nt.tr, nt.pr, nt.cellweights, nt.cellcounts,

src/procedures.jl

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@
44
Exclude rows that are invalid for variance-covariance estimator.
55
See also [`CheckVcov`](@ref).
66
"""
7-
checkvcov!(data, esample::BitVector,
7+
checkvcov!(data, esample::BitVector, aux::BitVector,
88
vce::Union{Vcov.SimpleCovariance, Vcov.RobustCovariance}) = NamedTuple()
99

10-
function checkvcov!(data, esample::BitVector, vce::Vcov.ClusterCovariance)
11-
esample .&= Vcov.completecases(data, vce)
10+
function checkvcov!(data, esample::BitVector, aux::BitVector, vce::Vcov.ClusterCovariance)
11+
for name in Vcov.names(vce)
12+
col = getcolumn(data, name)
13+
if Missing <: eltype(col)
14+
aux .= .!ismissing.(col)
15+
esample .&= aux
16+
end
17+
end
1218
return (esample=esample,)
1319
end
1420

@@ -20,7 +26,7 @@ exclude rows that are invalid for variance-covariance estimator.
2026
"""
2127
const CheckVcov = StatsStep{:CheckVcov, typeof(checkvcov!), true}
2228

23-
required(::CheckVcov) = (:data, :esample)
29+
required(::CheckVcov) = (:data, :esample, :aux)
2430
default(::CheckVcov) = (vce=Vcov.robust(),)
2531
copyargs(::CheckVcov) = (2,)
2632

@@ -163,7 +169,7 @@ default(::MakeYXCols) = (contrasts=nothing, fetol=1e-8, femaxiter=10000)
163169

164170
function combinedargs(::MakeYXCols, allntargs)
165171
yx = TermSet()
166-
@inbounds for nt in allntargs
172+
for nt in allntargs
167173
push!(yx, nt.yterm)
168174
foreach(x->push!(yx, x), nt.xterms)
169175
end
@@ -182,15 +188,15 @@ function maketreatcols(data, treatname::Symbol, treatintterms::TermSet,
182188
weights::AbstractWeights, esample::BitVector,
183189
cohortinteracted::Bool, fetol::Real, femaxiter::Int,
184190
::Type{DynamicTreatment{SharpDesign}}, time::Symbol,
185-
exc::IdDict{Int,Int}, notreat::IdDict{TimeType,Int})
191+
exc::Dict{Int,Int}, notreat::IdDict{ValidTimeType,Int})
186192

187193
nobs = sum(esample)
188194
# Putting treatname before time avoids sorting twice if cohortinteracted
189195
cellnames = Symbol[treatname, time, sort!(termvars(treatintterms))...]
190196
cols = subcolumns(data, cellnames, esample)
191197
cells, rows = cellrows(cols, findcell(cols))
192198

193-
rel = cells[2] .- cells[1]
199+
rel = refarray(cells[2]) .- refarray(cells[1])
194200
kept = .!haskey.(Ref(exc), rel) .& .!haskey.(Ref(notreat), cells[1])
195201
treatrows = rows[kept]
196202
# Construct cells needed for treatment indicators
@@ -283,17 +289,17 @@ combinedargs(step::MakeTreatCols, allntargs) =
283289

284290
# Obtain the relative time periods excluded by all tr in allntargs
285291
function combinedargs(::MakeTreatCols, allntargs, ::Type{DynamicTreatment{SharpDesign}})
286-
exc = IdDict{Int,Int}()
287-
notreat = IdDict{TimeType,Int}()
292+
exc = Dict{Int,Int}()
293+
notreat = IdDict{ValidTimeType,Int}()
288294
@inbounds for nt in allntargs
289295
foreach(x->_count!(exc, x), nt.tr.exc)
290296
foreach(x->_count!(notreat, x), nt.pr.e)
291297
end
292298
nnt = length(allntargs)
293-
@inbounds for (k, v) in exc
299+
for (k, v) in exc
294300
v == nnt || delete!(exc, k)
295301
end
296-
@inbounds for (k, v) in notreat
302+
for (k, v) in notreat
297303
v == nnt || delete!(notreat, k)
298304
end
299305
return (exc, notreat)
@@ -339,14 +345,14 @@ function solveleastsquares!(tr::DynamicTreatment{SharpDesign}, pr::TrendParallel
339345
X = hcat(tcols..., (yxcols[x] for x in xs)...)
340346

341347
ntcols = length(tcols)
342-
basecols = trues(size(X,2))
348+
basiscols = trues(size(X,2))
343349
if size(X, 2) > ntcols
344-
basecols = basecol(X)
350+
basiscols = diag(invsym!(X'X)) .> 0
345351
# Do not drop any treatment indicator
346-
sum(basecols[1:ntcols]) == ntcols ||
352+
sum(basiscols[1:ntcols]) == ntcols ||
347353
error("covariates are collinear with treatment indicators")
348-
sum(basecols) < size(X, 2) &&
349-
(X = X[:, basecols])
354+
sum(basiscols) < size(X, 2) &&
355+
(X = X[:, basiscols])
350356
end
351357

352358
crossx = cholesky!(Symmetric(X'X))
@@ -356,7 +362,7 @@ function solveleastsquares!(tr::DynamicTreatment{SharpDesign}, pr::TrendParallel
356362
return (coef=coef::Vector{Float64}, X=X::Matrix{Float64},
357363
crossx=crossx::Cholesky{Float64,Matrix{Float64}},
358364
residuals=residuals::Vector{Float64}, treatcells=treatcells::VecColumnTable,
359-
xterms=xs::Vector{AbstractTerm}, basecols=basecols::BitVector)
365+
xterms=xs::Vector{AbstractTerm}, basiscols=basiscols::BitVector)
360366
end
361367

362368
"""

test/did.jl

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
@test r.ycellweights == r.ycellcounts
3636
@test r.ycellcounts == repeat([252, 176, 163, 65], inner=4)
37-
@test all(i->r.coef[i]sum(r.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(r))
37+
@test all(i->r.coef[i] sum(r.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(r))
3838

3939
@test sprint(show, r) == "Regression-based DID result"
4040
pv = VERSION < v"1.6.0" ? " <1e-7" : "<1e-07"
@@ -55,10 +55,21 @@
5555
Converged: true Singletons dropped: 0
5656
──────────────────────────────────────────────────────────────────────"""
5757

58+
df = DataFrame(hrs)
59+
df.wave = Date.(df.wave)
60+
df.wave_hosp = Date.(df.wave_hosp)
61+
df.wave = settime(df, :wave, step=Year(1))
62+
df.wave_hosp = settime(df, :wave_hosp, start=Date(7), step=Year(1))
63+
r1 = @did(Reg, data=df, dynamic(:wave, -1), notyettreated(Date(11)),
64+
vce=Vcov.cluster(:hhidpn), yterm=term(:oop_spend), treatname=:wave_hosp,
65+
treatintterms=(), xterms=(fe(:wave)+fe(:hhidpn)), solvelsweights=false)
66+
@test coef(r1) coef(r)
67+
@test r1.coefnames[1] == "wave_hosp: 0008-01-01 & rel: 0"
68+
5869
r = @did(Reg, data=hrs, dynamic(:wave, -1), notyettreated([11]),
5970
vce=Vcov.cluster(:hhidpn), yterm=term(:oop_spend), treatname=:wave_hosp,
6071
treatintterms=(), cohortinteracted=false, lswtnames=(:wave_hosp, :wave))
61-
@test all(i->r.coef[i]sum(r.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(r))
72+
@test all(i->r.coef[i] sum(r.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(r))
6273

6374
@test sprint(show, MIME("text/plain"), r) == """
6475
──────────────────────────────────────────────────────────────────────
@@ -75,8 +86,8 @@
7586
──────────────────────────────────────────────────────────────────────"""
7687

7788
sr = view(r, 1:3)
78-
@test coef(sr)[1] 1560.2174484214308
79-
@test vcov(sr)[1] 1.1248572269146878e6
89+
@test coef(sr)[1] == r.coef[1]
90+
@test vcov(sr)[1] == r.vcov[1]
8091
@test parent(sr) === r
8192

8293
tr = rescale(r, fill(2, 5), 1:5)
@@ -137,7 +148,7 @@ end
137148

138149
@test a.cellweights == a.cellcounts
139150
@test a.cellcounts == [163, 339, 591, 428, 252]
140-
@test all(i->a.coef[i]sum(a.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(a))
151+
@test all(i->a.coef[i] sum(a.lsweights[:,i].*r.ycellmeans), 1:ntreatcoef(a))
141152

142153
a1 = agg(r, (:rel,), subset=:rel=>isodd)
143154
@test length(coef(a1)) == 2

0 commit comments

Comments
 (0)