@@ -180,22 +180,24 @@ collect estimation results for difference-in-differences.
180
180
# Interface definition
181
181
| Required methods | Default definition | Brief description |
182
182
|---|---|---|
183
- | `coef(r)` | `r.coef` | Vector of point estimates for all treatment coefficients and covariates |
183
+ | `coef(r)` | `r.coef` | Vector of point estimates for all coefficients including covariates |
184
184
| `vcov(r)` | `r.vcov` | Variance-covariance matrix for estimates in `coef` |
185
+ | `vce(r)` | `r.vce` | Covariance estimator |
185
186
| `nobs(r)` | `r.nobs` | Number of observations (table rows) involved in estimation |
186
187
| `outcomename(r)` | `r.yname` | Name of the outcome variable |
187
- | `coefnames(r)` | `r.coefnames` | Names (`Vector{String}`) of all treatment coefficients and covariates |
188
- | `treatnames(r)` | `coefnames(r)[1:ntreatcoef(r)]` | Names (`Vector{String}`) of treatment coefficients |
188
+ | `coefnames(r)` | `r.coefnames` | Names (`Vector{String}`) of all coefficients including covariates |
189
189
| `treatcells(r)` | `r.treatcells` | Tables.jl-compatible tabular description of treatment coefficients in the order of `coefnames` (without covariates) |
190
+ | `weights(r)` | `r.weights` | Column name of the weight variable (if specified) |
190
191
| `ntreatcoef(r)` | `size(treatcells(r), 1)` | Number of treatment coefficients |
191
192
| `treatcoef(r)` | `view(coef(r), 1:ntreatcoef(r))` | A view of treatment coefficients |
192
193
| `treatvcov(r)` | `(N = ntreatcoef(r); view(vcov(r), 1:N, 1:N))` | A view of variance-covariance matrix for treatment coefficients |
193
- | `weights (r)` | `r.weights ` | Column name of the weight variable (if specified) |
194
+ | `treatnames (r)` | `coefnames(r)[1:ntreatcoef(r)] ` | Names (`Vector{String}`) of treatment coefficients |
194
195
| **Optional methods** | | |
195
196
| `parent(r)` | `r.parent` | Result object from which `r` is generated |
197
+ | `dof_residual(r)` | `r.dof_residual` | Residual degrees of freedom |
196
198
| `responsename(r)` | `outcomename(r)` | Name of the outcome variable |
197
199
| `coefinds(r)` | `r.coefinds` | Lookup table (`Dict{String,Int}`) from `coefnames` to integer indices (for retrieving estimates by name) |
198
- | `dof_residual (r)` | `r.dof_residual ` | Residual degrees of freedom |
200
+ | `ncovariate (r)` | `length(coef(r)) - ntreatcoef(r) ` | Number of covariate coefficients |
199
201
"""
200
202
abstract type AbstractDIDResult <: StatisticalModel end
201
203
@@ -323,6 +325,27 @@ through bit-wise `and`.
323
325
return treatvcov (r)[inds, inds]
324
326
end
325
327
328
+ """
329
+ vce(r::AbstractDIDResult)
330
+
331
+ Return the covariance estimator used to estimate variance-covariance matrix.
332
+ """
333
+ vce (r:: AbstractDIDResult ) = r. vce
334
+
335
+ """
336
+ confint(r::AbstractDIDResult; level::Real=0.95)
337
+
338
+ Return a confidence interval for each coefficient estimate.
339
+ The returned object is of type `Tuple{Vector{Float64}, Vector{Float64}}`
340
+ where the first vector collects the lower bounds for all intervals
341
+ and the second one collects the upper bounds.
342
+ """
343
+ function confint (r:: AbstractDIDResult ; level:: Real = 0.95 )
344
+ scale = tdistinvcdf (dof_residual (r), 1 - (1 - level) / 2 )
345
+ se = stderror (r)
346
+ return coef (r) .- scale .* se, coef (r) .+ scale .* se
347
+ end
348
+
326
349
"""
327
350
nobs(r::AbstractDIDResult)
328
351
@@ -345,13 +368,6 @@ Return a vector of coefficient names.
345
368
"""
346
369
coefnames (r:: AbstractDIDResult ) = r. coefnames
347
370
348
- """
349
- treatnames(r::AbstractDIDResult)
350
-
351
- Return a vector of names for treatment coefficients.
352
- """
353
- treatnames (r:: AbstractDIDResult ) = coefnames (r)[1 : ntreatcoef (r)]
354
-
355
371
"""
356
372
treatcells(r::AbstractDIDResult)
357
373
@@ -360,6 +376,14 @@ in the order of coefnames (without covariates).
360
376
"""
361
377
treatcells (r:: AbstractDIDResult ) = r. treatcells
362
378
379
+ """
380
+ weights(r::AbstractDIDResult)
381
+
382
+ Return the column name of the weight variable.
383
+ Return `nothing` if `weights` is not specified for estimation.
384
+ """
385
+ weights (r:: AbstractDIDResult ) = r. weightname
386
+
363
387
"""
364
388
ntreatcoef(r::AbstractDIDResult)
365
389
@@ -382,12 +406,11 @@ Return a view of variance-covariance matrix for treatment coefficients.
382
406
treatvcov (r:: AbstractDIDResult ) = (N = ntreatcoef (r); view (vcov (r), 1 : N, 1 : N))
383
407
384
408
"""
385
- weights (r::AbstractDIDResult)
409
+ treatnames (r::AbstractDIDResult)
386
410
387
- Return the column name of the weight variable.
388
- Return `nothing` if `weights` is not specified for estimation.
411
+ Return a vector of names for treatment coefficients.
389
412
"""
390
- weights (r:: AbstractDIDResult ) = r . weightname
413
+ treatnames (r:: AbstractDIDResult ) = coefnames (r)[ 1 : ntreatcoef (r)]
391
414
392
415
"""
393
416
parent(r::AbstractDIDResult)
@@ -396,6 +419,13 @@ Return the `AbstractDIDResult` from which `r` is generated.
396
419
"""
397
420
parent (r:: AbstractDIDResult ) = r. parent
398
421
422
+ """
423
+ dof_residual(r::AbstractDIDResult)
424
+
425
+ Return the residual degrees of freedom.
426
+ """
427
+ dof_residual (r:: AbstractDIDResult ) = r. dof_residual
428
+
399
429
"""
400
430
responsename(r::AbstractDIDResult)
401
431
@@ -413,11 +443,35 @@ for retrieving estimates by name.
413
443
coefinds (r:: AbstractDIDResult ) = r. coefinds
414
444
415
445
"""
416
- dof_residual (r::AbstractDIDResult)
446
+ ncovariate (r::AbstractDIDResult)
417
447
418
- Return the residual degrees of freedom .
448
+ Return the number of covariate coefficients .
419
449
"""
420
- dof_residual (r:: AbstractDIDResult ) = r. dof_residual
450
+ ncovariate (r:: AbstractDIDResult ) = length (coef (r)) - ntreatcoef (r)
451
+
452
+ """
453
+ agg(r::DIDResult)
454
+
455
+ Aggregate difference-in-differences estimates
456
+ and return a subtype of [`AggregatedDIDResult`](@ref).
457
+ The implementation depends on the type of `r`.
458
+ """
459
+ agg (r:: DIDResult ) = error (" agg is not implemented for $(typeof (r)) " )
460
+
461
+ function coeftable (r:: AbstractDIDResult ; level:: Real = 0.95 )
462
+ cf = coef (r)
463
+ se = stderror (r)
464
+ zs = cf ./ se
465
+ pv = 2 .* tdistccdf .(dof_residual (r), abs .(zs))
466
+ cil, ciu = confint (r)
467
+ cnames = coefnames (r)
468
+ levstr = isinteger (level* 100 ) ? string (Integer (level* 100 )) : string (level* 100 )
469
+ return CoefTable (Vector[cf, se, zs, pv, cil, ciu],
470
+ [" Estimate" ," Std. Error" ," t" , " Pr(>|t|)" , " Lower $levstr %" , " Upper $levstr %" ],
471
+ [" $(cnames[i]) " for i = 1 : length (cf)], 4 , 3 )
472
+ end
473
+
474
+ show (io:: IO , r:: AbstractDIDResult ) = show (io, coeftable (r))
421
475
422
476
"""
423
477
_treatnames(treatcells)
@@ -440,36 +494,52 @@ function _treatnames(treatcells)
440
494
return names
441
495
end
442
496
443
- function _parse_bys! (bycols:: Vector , cells:: VecColumnTable , by:: Pair{Symbol} )
497
+ # Helper functions that parse the bys option for agg
498
+ function _parse_bycells! (bycols:: Vector , cells:: VecColumnTable , by:: Pair{Symbol} )
444
499
lookup = getfield (cells, :lookup )
445
- _parse_bys ! (bycols, cells, lookup[by[1 ]]=> by[2 ])
500
+ _parse_bycells ! (bycols, cells, lookup[by[1 ]]=> by[2 ])
446
501
end
447
502
448
- function _parse_bys ! (bycols:: Vector , cells:: VecColumnTable , by:: Pair{Int} )
503
+ function _parse_bycells ! (bycols:: Vector , cells:: VecColumnTable , by:: Pair{Int} )
449
504
if by[2 ] isa Function
450
505
bycols[by[1 ]] = apply (cells, by[1 ]=> by[2 ])
451
506
else
452
507
bycols[by[1 ]] = apply (cells, by[2 ][1 ]=> by[2 ][2 ])
453
508
end
454
509
end
455
510
456
- function _parse_bys ! (bycols:: Vector , cells:: VecColumnTable , bys)
511
+ function _parse_bycells ! (bycols:: Vector , cells:: VecColumnTable , bys)
457
512
eltype (bys) <: Pair || throw (ArgumentError (" unaccepted type of bys" ))
458
513
for by in bys
459
- _parse_bys ! (bycols, cells, by)
514
+ _parse_bycells ! (bycols, cells, by)
460
515
end
461
516
end
462
517
463
- function _bycells (r :: DIDResult , names , bys)
464
- tcells = treatcells (r)
465
- bynames = names === nothing ? getfield (tcells, :names ) : collect (Symbol, names)
466
- bycols = AbstractVector[ getcolumn (tcells, n) for n in bynames]
467
- bys === nothing || _parse_bys! (bycols, tcells, bys )
468
- return VecColumnTable (bycols, bynames )
518
+ _parse_bycells! (bycols :: Vector , cells :: VecColumnTable , bys:: Nothing ) = nothing
519
+
520
+ # Helper function for _parse_subset
521
+ function _fill_x! (r :: AbstractDIDResult , inds :: BitVector )
522
+ nx = ncovariate (r )
523
+ nx > 0 && push! (inds, ( false for i in 1 : nx) . .. )
469
524
end
470
525
526
+ # Helper functions for handling subset option that may involves Pairs
527
+ _parse_subset (r:: AbstractDIDResult , by:: Pair , fill_x:: Bool ) =
528
+ (inds = apply (treatcells (r), by); fill_x && _fill_x! (r, inds); return inds)
529
+
530
+ function _parse_subset (r:: AbstractDIDResult , inds, fill_x:: Bool )
531
+ eltype (inds) <: Pair || return inds
532
+ inds = apply_and (treatcells (r), inds... )
533
+ fill_x && _fill_x! (r, inds)
534
+ return inds
535
+ end
536
+
537
+ _parse_subset (r:: AbstractDIDResult , :: Colon , fill_x:: Bool ) =
538
+ fill_x ? (1 : length (coef (r))) : 1 : ntreatcoef (r)
539
+
471
540
# Count number of elements selected by indices `inds`
472
541
_nselected (inds) = eltype (inds) == Bool ? sum (inds) : length (inds)
542
+ _nselected (:: Colon ) = throw (ArgumentError (" cannot accept Colon (:)" ))
473
543
474
544
"""
475
545
treatindex(ntcoef::Int, I)
@@ -536,17 +606,18 @@ from `r` at the given index or indices `inds` without constructing a copied subs
536
606
537
607
coef (r:: SubDIDResult ) = view (coef (parent (r)), r. inds)
538
608
vcov (r:: SubDIDResult ) = view (vcov (parent (r)), r. inds, r. inds)
609
+ vce (r:: SubDIDResult ) = vce (parent (r))
539
610
nobs (r:: SubDIDResult ) = nobs (parent (r))
540
611
outcomename (r:: SubDIDResult ) = outcomename (parent (r))
541
612
coefnames (r:: SubDIDResult ) = view (coefnames (parent (r)), r. inds)
542
- treatnames (r:: SubDIDResult ) = view (treatnames (parent (r)), r. treatinds)
543
613
treatcells (r:: SubDIDResult ) = view (treatcells (parent (r)), r. treatinds)
614
+ weights (r:: SubDIDResult ) = weights (parent (r))
544
615
ntreatcoef (r:: SubDIDResult ) = _nselected (r. treatinds)
545
616
treatcoef (r:: SubDIDResult ) = view (treatcoef (parent (r)), r. treatinds)
546
617
treatvcov (r:: SubDIDResult ) = view (treatvcov (parent (r)), r. treatinds, r. treatinds)
547
- weights (r:: SubDIDResult ) = weights (parent (r))
548
- responsename (r:: SubDIDResult ) = responsename (parent (r))
618
+ treatnames (r:: SubDIDResult ) = view (treatnames (parent (r)), r. treatinds)
549
619
dof_residual (r:: SubDIDResult ) = dof_residual (parent (r))
620
+ responsename (r:: SubDIDResult ) = responsename (parent (r))
550
621
551
622
"""
552
623
TransformedDIDResult{P,M} <: AbstractDIDResult
@@ -607,19 +678,21 @@ end
607
678
608
679
const TransOrTransSub = Union{TransformedDIDResult, TransSubDIDResult}
609
680
681
+ vce (r:: TransOrTransSub ) = vce (parent (r))
610
682
nobs (r:: TransOrTransSub ) = nobs (parent (r))
611
683
outcomename (r:: TransOrTransSub ) = outcomename (parent (r))
612
684
coefnames (r:: TransformedDIDResult ) = coefnames (parent (r))
613
685
coefnames (r:: TransSubDIDResult ) = view (coefnames (parent (r)), r. inds)
614
- treatnames (r:: TransOrTransSub ) = treatnames (parent (r))
615
686
treatcells (r:: TransformedDIDResult ) = treatcells (parent (r))
616
687
treatcells (r:: TransSubDIDResult ) = view (treatcells (parent (r)), r. treatinds)
688
+ weights (r:: TransOrTransSub ) = weights (parent (r))
617
689
ntreatcoef (r:: TransformedDIDResult ) = ntreatcoef (parent (r))
618
690
ntreatcoef (r:: TransSubDIDResult ) = _nselected (r. treatinds)
619
- weights (r:: TransOrTransSub ) = weights (parent (r))
691
+ treatnames (r:: TransformedDIDResult ) = treatnames (parent (r))
692
+ treatnames (r:: TransSubDIDResult ) = view (treatnames (parent (r)), r. treatinds)
693
+ dof_residual (r:: TransOrTransSub ) = dof_residual (parent (r))
620
694
responsename (r:: TransOrTransSub ) = responsename (parent (r))
621
695
coefinds (r:: TransformedDIDResult ) = coefinds (parent (r))
622
- dof_residual (r:: TransOrTransSub ) = dof_residual (parent (r))
623
696
624
697
"""
625
698
lincom(r::AbstractDIDResult, linmap::AbstractMatrix{<:Real}, subset=nothing)
@@ -643,13 +716,14 @@ function lincom(r::AbstractDIDResult, linmap::AbstractMatrix{<:Real}, subset::No
643
716
end
644
717
645
718
function lincom (r:: AbstractDIDResult , linmap:: AbstractMatrix{<:Real} , subset)
719
+ inds = _parse_subset (r, subset, true )
646
720
nr, nc = size (linmap)
647
721
length (coef (r)) == nc ||
648
722
throw (DimensionMismatch (" linmap must have $(length (coef (r))) columns" ))
649
- _nselected (subset ) == nr || throw (ArgumentError (" subset must select $nr elements" ))
723
+ _nselected (inds ) == nr || throw (ArgumentError (" subset must select $nr elements" ))
650
724
cf = linmap * coef (r)
651
725
v = linmap * vcov (r) * linmap'
652
- return TransSubDIDResult (r, linmap, cf, v, subset )
726
+ return TransSubDIDResult (r, linmap, cf, v, inds )
653
727
end
654
728
655
729
"""
@@ -684,21 +758,25 @@ function rescale(r::AbstractDIDResult, scale::AbstractVector{<:Real}, subset::No
684
758
end
685
759
686
760
function rescale (r:: AbstractDIDResult , scale:: AbstractVector{<:Real} , subset)
687
- N1 = length (scale)
688
- _nselected (subset) == N1 || throw (ArgumentError (" subset must select $N1 elements" ))
689
- cf = scale .* view (coef (r), subset)
690
- v = Matrix {Float64} (undef, N1, N1)
691
- pv = view (vcov (r), subset, subset)
692
- @inbounds for j in 1 : N1
693
- for i in 1 : N1
761
+ inds = _parse_subset (r, subset, true )
762
+ N = length (scale)
763
+ _nselected (inds) == N || throw (ArgumentError (" subset must select $N elements" ))
764
+ cf = scale .* view (coef (r), inds)
765
+ v = Matrix {Float64} (undef, N, N)
766
+ pv = view (vcov (r), inds, inds)
767
+ @inbounds for j in 1 : N
768
+ for i in 1 : N
694
769
v[i, j] = scale[i]* scale[j]* pv[i, j]
695
770
end
696
771
end
697
- return TransSubDIDResult (r, Diagonal (scale), cf, v, subset )
772
+ return TransSubDIDResult (r, Diagonal (scale), cf, v, inds )
698
773
end
699
774
700
775
rescale (r:: AbstractDIDResult , by:: Pair , subset:: Nothing = nothing ) =
701
776
rescale (r, apply (treatcells (r), by), 1 : ntreatcoef (r))
702
777
703
- rescale (r:: AbstractDIDResult , by:: Pair , subset) =
704
- rescale (r, apply (view (treatcells (r), subset), by), subset)
778
+ function rescale (r:: AbstractDIDResult , by:: Pair , subset)
779
+ inds = _parse_subset (r, subset, true )
780
+ tinds = treatindex (ntreatcoef (r), inds)
781
+ return rescale (r, apply (view (treatcells (r), tinds), by), inds)
782
+ end
0 commit comments