Skip to content

Commit e2e3d5d

Browse files
paldaydmbates
andauthored
specify re by name (#53)
* specify re by name * Apply suggestions from code review Co-authored-by: Douglas Bates <[email protected]>
1 parent 2e61032 commit e2e3d5d

5 files changed

Lines changed: 109 additions & 12 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MixedModelsSim"
22
uuid = "d5ae56c5-23ca-4a1f-b505-9fc4796fc1fe"
33
authors = ["Phillip Alday", "Douglas Bates", "Lisa DeBruine", "Reinhold Kliegl"]
4-
version = "0.2.4"
4+
version = "0.2.5"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

src/MixedModelsSim.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ using MixedModels: replicate
1212

1313
export
1414
create_re,
15+
create_theta,
16+
createθ,
1517
cyclicshift,
1618
factorproduct,
1719
flatlowertri,

src/utilities.jl

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ end
155155

156156

157157
"""
158-
update!(m::MixedModel; θ)
158+
_update!(m::MixedModel, θ)
159159
160160
Update the mixed model to use θ as its new parameter vector.
161161
@@ -167,8 +167,8 @@ Update the mixed model to use θ as its new parameter vector.
167167
!!! note
168168
For GLMMs, this only sets θ and not β, even for `fast=false` fits.
169169
"""
170-
update!(m::LinearMixedModel; θ) = updateL!(MixedModels.setθ!(m, θ))
171-
update!(m::GeneralizedLinearMixedModel; θ) = pirls!(MixedModels.setθ!(m, θ), false)
170+
_update!(m::LinearMixedModel, θ) = updateL!(MixedModels.setθ!(m, θ))
171+
_update!(m::GeneralizedLinearMixedModel, θ) = pirls!(MixedModels.setθ!(m, θ), false)
172172
# arguably type piracy, but we're all the same developers....
173173

174174

@@ -182,16 +182,87 @@ The `re` can be created using [`create_re`](@ref).
182182
183183
They should be specified in the order specified in `VarCorr(m)`.
184184
185+
!!! warning
186+
We recommend against calling this method directly. Instead, use the method
187+
with keyword arguments to specify the different `re` by name.
188+
189+
!!! note
190+
This is a convenience function for installing a particular parameter vector
191+
and the resulting model fit. It does not actually perform any type of
192+
optimization.
193+
185194
Details
186195
========
187196
The `re` used as the λ fields of the model's `ReTerm`s and should be specified
188197
as the lower Cholesky factor of covariance matrices.
189198
"""
190199
function update!(m::MixedModel, re...)
200+
Base.depwarn("Specifying the random effects by position instead of name is deprecated",
201+
:update!vararg)
202+
191203
θ = vcat((flatlowertri(rr) for rr in re)...)
192-
update!(m; θ=θ)
204+
return _update!(m, θ)
193205
end
194206

207+
"""
208+
update!(m::MixedModel; namedre...)
209+
update!(m::MixedModel; θ)
210+
211+
Update the mixed model to use the random-effects covariance matrices.
212+
213+
The `namedre` can be created using [`create_re`](@ref). The `namedre` are specified
214+
by the name of the blocking variable, e.g. `subj=create_re(...)`.
215+
216+
!!! warning
217+
Setting θ directly as a keyword-argument is deprecated.
218+
219+
!!! note
220+
This is a convenience function for installing a particular parameter vector
221+
and the resulting model fit. It does not actually perform any type of
222+
optimization.
223+
224+
Details
225+
========
226+
The `re` is used as the λ fields of the model's `ReTerm`s and should be specified
227+
as the lower Cholesky factor of covariance matrices.
228+
"""
229+
function update!(m::MixedModel; θ=nothing, namedre...)
230+
if θ === nothing
231+
length(namedre) == 0 && throw(ArgumentError("no random effects specified"))
232+
θ = createθ(m; namedre...)
233+
elseif θ !== nothing && length(namedre) != 0
234+
throw(ArgumentError("θ must not be specified when named random effects are"))
235+
end
236+
return _update!(m, θ)
237+
end
238+
239+
"""
240+
createθ(m::MixedModel; named_re)
241+
create_theta(m::MixedModel; named_re)
242+
243+
Create the parameter vector θ corresponding to the random effects.
244+
245+
The `named_re` can be created using [`create_re`](@ref). The `named_re` are specified
246+
by the name of the blocking variable, e.g. `subj=create_re(...)`.
247+
248+
The model must be specified because the parameters are sorted internally for computational
249+
efficiency.
250+
"""
251+
function createθ(m::MixedModel; named_re...)
252+
newre = Dict(named_re...)
253+
fns = fnames(m)
254+
255+
if Set(fns) != keys(newre)
256+
throw(ArgumentError("Specified blocking variables do not match model blocking variables."))
257+
end
258+
259+
return mapfoldl(vcat, fns) do fname
260+
flatlowertri(newre[fname])
261+
end
262+
end
263+
264+
const create_theta = createθ
265+
195266
"""
196267
create_re(sigmas...; corrmat=Matrix{Float64}(I, length(sigmas), length(sigmas))
197268

test/power.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ dat = simdat_crossed(StableRNG(42), subj_n, item_n,
1515
both_win = both_win)
1616
form = @formula(dv ~ 1 + age + pet + cond + time + (1|subj) + (1|item));
1717
cont = Dict(nm => HelmertCoding() for nm in (:age, :pet, :cond, :time));
18-
fm1 = fit(MixedModel, form, dat; contrasts=cont, REML=false);
19-
sim = parametricbootstrap(StableRNG(42), 10, fm1; use_threads=false);
18+
fm1 = fit(MixedModel, form, dat; contrasts=cont, REML=false, progress=false);
19+
sim = parametricbootstrap(StableRNG(42), 10, fm1; use_threads=false, hide_progress=true);
2020

2121
@testset "power_table" begin
2222
pt = power_table(sim)

test/utilities.jl

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ end
6060
@testset "update!" begin
6161
@testset "LMM" begin
6262
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)),
63-
MixedModels.dataset(:sleepstudy))
63+
MixedModels.dataset(:sleepstudy); progress=false)
6464
update!(fm1; θ=[1.0, 0.0, 1.0])
6565

6666
@test all(values(first(fm1.σs)) .== fm1.σ)
@@ -70,7 +70,7 @@ end
7070
@testset "GLMM" begin
7171
cbpp = MixedModels.dataset(:cbpp)
7272
gm1 = fit(MixedModel, @formula((incid/hsz) ~ 1 + period + (1|herd)), cbpp, Binomial();
73-
wts=cbpp.hsz, fast=true)
73+
wts=cbpp.hsz, fast=true, progress=false)
7474

7575
β = repeat([-1.], 4)
7676
θ = [0.5]
@@ -80,7 +80,7 @@ end
8080
@test all(values(first(gm1.σs)) .== θ)
8181
@test all(gm1.β .== β₀)
8282

83-
refit!(gm1, fast=false)
83+
refit!(gm1, fast=false, progress=false)
8484
β₀ = copy(fixef(gm1))
8585
update!(gm1; θ=θ)
8686
@test all(values(first(gm1.σs)) .== θ)
@@ -89,9 +89,9 @@ end
8989

9090
end
9191

92-
@testset "create_re" begin
92+
@testset "create_re" begin
9393
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)),
94-
MixedModels.dataset(:sleepstudy))
94+
MixedModels.dataset(:sleepstudy); progress=false)
9595

9696
σs = values(first(fm1.σs)) ./ fm1.σ
9797
ρ = only(first(VarCorr(fm1).σρ).ρ)
@@ -105,10 +105,34 @@ end
105105
fm1unfitted = LinearMixedModel(@formula(reaction ~ 1 + days + (1 + days|subj)),
106106
MixedModels.dataset(:sleepstudy))
107107

108+
@test_logs((:warn,
109+
"Specifying the random effects by position instead of name is deprecated"),
110+
update!(fm1unfitted, subjre))
111+
108112
update!(fm1unfitted, subjre)
109113

110114
vcu, vc = VarCorr(fm1unfitted), VarCorr(fm1)
111115

112116
@test all(values(vcu.σρ.subj.σ) .≈ values(vc.σρ.subj.σ))
113117
@test all(values(vcu.σρ.subj.ρ) .≈ values(vc.σρ.subj.ρ))
118+
119+
@test_throws ArgumentError update!(fm1unfitted; subjitem=subjre, θ=fm1.θ)
120+
@test_throws ArgumentError update!(fm1unfitted)
121+
@test_throws ArgumentError update!(fm1unfitted; item=subjre)
122+
123+
fmkb = fit(MixedModel,
124+
@formula(rt_trunc ~ 1 + spkr + prec + load + (1|subj) + (1 + prec|item)),
125+
MixedModels.dataset(:kb07); progress=false)
126+
127+
fmkb2 = LinearMixedModel(@formula(rt_trunc ~ 1 + spkr + prec + load + (1|subj) + (1 + prec|item)),
128+
MixedModels.dataset(:kb07))
129+
update!(fmkb2; subj=fmkb.reterms[2].λ, item=fmkb.reterms[1].λ)
130+
131+
vcu, vc = VarCorr(fmkb2), VarCorr(fmkb2)
132+
133+
@test all(values(vcu.σρ.subj.σ) .≈ values(vc.σρ.subj.σ))
134+
@test all(values(vcu.σρ.subj.ρ) .≈ values(vc.σρ.subj.ρ))
135+
@test all(values(vcu.σρ.item.σ) .≈ values(vc.σρ.item.σ))
136+
@test all(values(vcu.σρ.item.ρ) .≈ values(vc.σρ.item.ρ))
137+
114138
end

0 commit comments

Comments
 (0)