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

Commit 5186049

Browse files
committed
Reorganize types and replace DIDSpec with StatsSpec
1 parent 8fc3e5c commit 5186049

File tree

6 files changed

+178
-163
lines changed

6 files changed

+178
-163
lines changed

src/DiffinDiffsBase.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,17 @@ export @fieldequal,
4444
hastreat,
4545
parse_treat,
4646

47-
AbstractDiffinDiffs,
47+
AbstractStatsProcedure,
48+
StatsSpec,
49+
isnamed,
50+
StatsSpecSet,
51+
52+
DiffinDiffsEstimator,
4853
DefaultDID,
4954
did,
50-
DIDSpec,
51-
isnamed,
52-
spec,
53-
@spec,
55+
didspec,
56+
@didspec,
5457
@did,
55-
DIDSpecSet,
5658
DIDResult,
5759
agg,
5860
AggregatedDIDResult
@@ -61,6 +63,7 @@ include("utils.jl")
6163
include("treatments.jl")
6264
include("parallels.jl")
6365
include("terms.jl")
66+
include("procedures.jl")
6467
include("did.jl")
6568

6669
end

src/did.jl

Lines changed: 48 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
"""
2-
AbstractDiffinDiffs
2+
DiffinDiffsEstimator <: AbstractStatsProcedure
33
44
Supertype for all types specifying the estimation procedure for difference-in-differences.
55
"""
6-
abstract type AbstractDiffinDiffs end
6+
abstract type DiffinDiffsEstimator <: AbstractStatsProcedure end
77

88
"""
9-
DefaultDID <: AbstractDiffinDiffs
9+
DefaultDID <: DiffinDiffsEstimator
1010
11-
Try to use a default procedure based on other information.
11+
Default difference-in-differences estimator selected based on the context.
1212
"""
13-
struct DefaultDID <: AbstractDiffinDiffs end
13+
struct DefaultDID <: DiffinDiffsEstimator end
1414

1515
did(tr::AbstractTreatment, pr::AbstractParallel; kwargs...) =
1616
did(DefaultDID, tr, pr; kwargs...)
1717

1818
# The only case in which a message replaces MethodError
19-
did(d::Type{<:AbstractDiffinDiffs}, tr::AbstractTreatment, pr::AbstractParallel; kwargs...) =
19+
did(d::Type{<:DiffinDiffsEstimator}, tr::AbstractTreatment, pr::AbstractParallel; kwargs...) =
2020
error("$(typeof(d)) is not implemented for $(typeof(tr)) and $(typeof(pr))")
2121

2222
"""
23-
did(d::Type{<:AbstractDiffinDiffs}, t::TreatmentTerm, args...; kwargs...)
23+
did(d::Type{<:DiffinDiffsEstimator}, t::TreatmentTerm, args...; kwargs...)
2424
2525
A wrapper method that accepts a [`TreatmentTerm`](@ref).
2626
"""
27-
did(d::Type{<:AbstractDiffinDiffs}, @nospecialize(t::TreatmentTerm), args...; kwargs...) =
27+
did(d::Type{<:DiffinDiffsEstimator}, @nospecialize(t::TreatmentTerm), args...; kwargs...) =
2828
did(d, t.tr, t.pr, args...; treatname=t.sym, kwargs...)
2929

3030
"""
31-
did(d::Type{<:AbstractDiffinDiffs}, formula::FormulaTerm, args...; kwargs...)
31+
did(d::Type{<:DiffinDiffsEstimator}, formula::FormulaTerm, args...; kwargs...)
3232
3333
A wrapper method that accepts a formula.
3434
"""
35-
function did(d::Type{<:AbstractDiffinDiffs}, @nospecialize(formula::FormulaTerm),
35+
function did(d::Type{<:DiffinDiffsEstimator}, @nospecialize(formula::FormulaTerm),
3636
args...; kwargs...)
3737
treat, intacts, xs = parse_treat(formula)
3838
ints = intacts==() ? NamedTuple() : (treatintterms=intacts,)
@@ -41,25 +41,8 @@ function did(d::Type{<:AbstractDiffinDiffs}, @nospecialize(formula::FormulaTerm)
4141
yterm=formula.lhs, treatname=treat.sym, ints..., xterms..., kwargs...)
4242
end
4343

44-
"""
45-
args_kwargs(exprs)
46-
47-
Partition a collection of expressions into two arrays
48-
such that all expressions in the second array has `head` being `:(=)`.
49-
This function is useful for separating out expressions
50-
for positional arguments and those for keyword arguments.
51-
"""
52-
function args_kwargs(exprs)
53-
args = []
54-
kwargs = []
55-
for expr in exprs
56-
(expr isa Expr && expr.head==:(=)) ? push!(kwargs, expr) : push!(args, expr)
57-
end
58-
return args, kwargs
59-
end
60-
44+
argpair(arg::Type{<:DiffinDiffsEstimator}) = :d => arg
6145
argpair(arg::AbstractString) = :name => String(arg)
62-
argpair(arg::Type{<:AbstractDiffinDiffs}) = :d => arg
6346
argpair(arg::AbstractTreatment) = :tr => arg
6447
argpair(arg::AbstractParallel) = :pr => arg
6548
argpair(::Any) = throw(ArgumentError("unacceptable positional arguments"))
@@ -68,24 +51,16 @@ argpair(::Any) = throw(ArgumentError("unacceptable positional arguments"))
6851
parse_didargs(args...; kwargs...)
6952
7053
Classify positional arguments for [`did`](@ref) by type
71-
and parse any [`TreatmentTerm`](@ref) or [`FormulaTerm`](@ref) in `args`.
72-
The order of positional arguments in `args` does not make a difference.
54+
and parse any [`TreatmentTerm`](@ref) or [`FormulaTerm`](@ref).
55+
The order of positional arguments is irrelevant.
56+
This function helps constructing a [`StatsSpec`](@ref)
57+
that can be accepted by [`did`](@ref).
7358
7459
# Returns
75-
- `String`: optional name for [`DIDSpec`](@ref) (return `""` if no instance of a subtype of `AbstractString` is found).
76-
- `Dict`: up to three key-value pairs for processed positional arguments.
77-
- `Dict`: keyword arguments with possibly additional pairs after parsing `args`.
78-
79-
# Notes
80-
The possible keys for the first `Dict` are `:d`, `:tr` and `:pr`.
81-
They are used to index a subtype of [`AbstractDiffinDiffs`](@ref),
82-
instances of [`AbstractTreatment`](@ref) and [`AbstractParallel`](@ref) respectively.
83-
84-
If `args` contains any [`TreatmentTerm`](@ref) or [`FormulaTerm`](@ref),
85-
they are decomposed with additional key-value pairs
86-
joining the other keyword arguments in the second `Dict`.
87-
88-
This function is useful for constructing [`DIDSpec`](@ref).
60+
- `Type{<:DiffinDiffsEstimator}`: either the type found in positional arguments or `DefaultDID`.
61+
- `String`: either a string found in positional arguments or `""` if no instance of any subtype of `AbstractString` is found.
62+
- `Dict`: up to two key-value pairs for instances of [`AbstractTreatment`](@ref) and [`AbstractParallel`](@ref) with keys being `:tr` and `:pr`.
63+
- `Dict`: keyword arguments with possibly additional pairs after parsing positional arguments.
8964
"""
9065
function parse_didargs(args...; kwargs...)
9166
pargs = Pair{Symbol,Any}[]
@@ -108,36 +83,40 @@ function parse_didargs(args...; kwargs...)
10883
kwargs = Dict{Symbol,Any}(pkwargs...)
10984
length(args) == length(pargs) && length(kwargs) == length(pkwargs) ||
11085
throw(ArgumentError("redundant arguments encountered"))
86+
sptype = pop!(args, :d, DefaultDID)
11187
name = pop!(args, :name, "")
112-
return name, args, kwargs
88+
return sptype, name, args, kwargs
11389
end
11490

11591
"""
116-
DIDSpec{T<:AbstractDiffinDiffs}
92+
didspec(args...; kwargs...)
11793
118-
Record the positional arguments and keyword arguments for [`did`](@ref)
119-
and optionally a name for the specification.
94+
Construct a [`StatsSpec`](@ref) with fields set by processed arguments.
95+
An optional `name` for [`StatsSpec`](@ref) can be included in `args` as a string.
96+
The order of positional arguments is irrelevant.
12097
121-
# Fields
122-
- `name::String`: name for the specification.
123-
- `args::Dict{Symbol}`: positional arguments indexed based on their types.
124-
- `kwargs::Dict{Symbol}`: keyword arguments.
98+
This method simply passes objects returned by [`parse_didargs`](@ref)
99+
to the constructor of [`StatsSpec`](@ref).
125100
"""
126-
struct DIDSpec{T<:AbstractDiffinDiffs}
127-
name::String
128-
args::Dict{Symbol}
129-
kwargs::Dict{Symbol}
130-
DIDSpec(name::String, args::Dict{Symbol}, kwargs::Dict{Symbol}) =
131-
new{pop!(args, :d, DefaultDID)}(name, args, kwargs)
132-
end
101+
didspec(args...; kwargs...) = StatsSpec(parse_didargs(args...; kwargs...)...)
102+
103+
"""
104+
@didspec ["name" args... kwargs...]
133105
134-
==(a::DIDSpec{T}, b::DIDSpec{T}) where {T<:AbstractDiffinDiffs} =
135-
a.args == b.args && a.kwargs == b.kwargs
106+
Call [`didspec`](@ref) for constructing a [`StatsSpec`](@ref).
107+
The order of arguments is irrelevant.
136108
137-
isnamed(sp::DIDSpec) = sp.name != ""
109+
# Arguments
110+
- `name::AbstractString`: an optional name for the [`StatsSpec`](@ref).
111+
- `args... kwargs...`: a list of arguments accepted by [`did`](@ref).
112+
"""
113+
macro didspec(exprs...)
114+
args, kwargs = args_kwargs(exprs)
115+
return esc(:(didspec($(args...); $(kwargs...))))
116+
end
138117

139-
function show(io::IO, sp::DIDSpec{T}) where {T}
140-
print(io, "DIDSpec{", sprintcompact(T), "}")
118+
function show(io::IO, sp::StatsSpec{T}) where {T<:DiffinDiffsEstimator}
119+
print(io, "StatsSpec{", sprintcompact(T), "}")
141120
isnamed(sp) && print(io, ": ", sp.name)
142121
if get(io, :compact, false) || !haskey(sp.args, :tr) && !haskey(sp.args, :pr)
143122
return
@@ -149,38 +128,11 @@ function show(io::IO, sp::DIDSpec{T}) where {T}
149128
end
150129

151130
"""
152-
spec(args...; kwargs...)
131+
did(sp::StatsSpec)
153132
154-
Construct a [`DIDSpec`](@ref) with `args` and `kwargs`
155-
processed by [`parse_didargs`](@ref).
156-
An optional `name` for [`DIDSpec`](@ref) can be included in `args` as a string.
157-
158-
This method simply passes objects returned by [`parse_didargs`](@ref)
159-
to the constructor of [`DIDSpec`](@ref).
160-
"""
161-
spec(args...; kwargs...) = DIDSpec(parse_didargs(args...; kwargs...)...)
162-
163-
"""
164-
@spec ["name" args... kwargs...]
165-
166-
Return a [`DIDSpec`](@ref) with fields set by the arguments.
167-
The order of the arguments does not make a difference.
168-
169-
# Arguments
170-
- `name::AbstractString`: an optional name for the specification.
171-
- `args... kwargs...`: a list of arguments for [`did`](@ref).
133+
A wrapper method that accepts a [`StatsSpec`](@ref).
172134
"""
173-
macro spec(exprs...)
174-
args, kwargs = args_kwargs(exprs)
175-
return esc(:(spec($(args...); $(kwargs...))))
176-
end
177-
178-
"""
179-
did(sp::DIDSpec)
180-
181-
A wrapper method that accepts a [`DIDSpec`](@ref).
182-
"""
183-
function did(sp::DIDSpec{T}) where {T}
135+
function did(sp::StatsSpec{T}) where {T<:DiffinDiffsEstimator}
184136
if haskey(sp.args, :tr) && haskey(sp.args, :pr)
185137
did(T, sp.args[:tr], sp.args[:pr]; sp.kwargs...)
186138
else
@@ -192,16 +144,11 @@ end
192144
@did args... [kwargs...]
193145
194146
Call [`did`](@ref) with the specified arguments.
195-
The order of the arguments does not make a difference.
147+
The order of the arguments is irrelevant.
196148
"""
197149
macro did(exprs...)
198150
args, kwargs = args_kwargs(exprs)
199-
return esc(:(did(spec($(args...); $(kwargs...)))))
200-
end
201-
202-
struct DIDSpecSet
203-
default::DIDSpec
204-
specs::DIDSpec
151+
return esc(:(did(didspec($(args...); $(kwargs...)))))
205152
end
206153

207154

src/procedures.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
AbstractStatsProcedure
3+
4+
Supertype for all types specifying the procedure for statistical estimation or inference.
5+
"""
6+
abstract type AbstractStatsProcedure end
7+
8+
"""
9+
StatsSpec{T<:AbstractStatsProcedure}
10+
11+
Record the specification for a statistical procedure and
12+
optionally a name for the specification.
13+
14+
The specification is recorded based on the arguments
15+
for a function that will conduct the procedure.
16+
It is assumed that a tuple of positional arguments accepted by the function
17+
can be constructed solely based on the type of each argument.
18+
19+
# Fields
20+
- `name::String`: name for the specification.
21+
- `args::Dict{Symbol}`: positional arguments indexed based on their types.
22+
- `kwargs::Dict{Symbol}`: keyword arguments.
23+
"""
24+
struct StatsSpec{T<:AbstractStatsProcedure}
25+
name::String
26+
args::Dict{Symbol}
27+
kwargs::Dict{Symbol}
28+
StatsSpec(T::Type{<:AbstractStatsProcedure},
29+
name::String, args::Dict{Symbol}, kwargs::Dict{Symbol}) =
30+
new{T}(name, args, kwargs)
31+
end
32+
33+
==(a::StatsSpec{T}, b::StatsSpec{T}) where {T<:AbstractStatsProcedure} =
34+
a.args == b.args && a.kwargs == b.kwargs
35+
36+
isnamed(sp::StatsSpec) = sp.name != ""
37+
38+
struct StatsSpecSet
39+
default::StatsSpec
40+
specs::Vector{StatsSpec}
41+
end
42+

src/utils.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ macro unpack(functionname)
9393
end)
9494
end
9595

96+
"""
97+
args_kwargs(exprs)
98+
99+
Partition a collection of expressions into two arrays
100+
such that all expressions in the second array has `head` being `:(=)`.
101+
This function is useful for separating out expressions
102+
for positional arguments and those for keyword arguments.
103+
"""
104+
function args_kwargs(exprs)
105+
args = []
106+
kwargs = []
107+
for expr in exprs
108+
(expr isa Expr && expr.head==:(=)) ? push!(kwargs, expr) : push!(args, expr)
109+
end
110+
return args, kwargs
111+
end
112+
96113
"""
97114
exampledata()
98115

0 commit comments

Comments
 (0)