Skip to content

Commit 44e4ecc

Browse files
authored
GLMM dispersion and dof_residual (#270)
* GLMM dispersion and dof_residual * reenable broken Gaussian GLMM test * LMM dof_residual distinct from ssqdenom.
1 parent a2617ad commit 44e4ecc

File tree

3 files changed

+51
-11
lines changed

3 files changed

+51
-11
lines changed

src/generalizedlinearmixedmodel.jl

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,29 @@ function deviance!(m::GeneralizedLinearMixedModel, nAGQ = 1)
112112
deviance(m, nAGQ)
113113
end
114114

115-
GLM.dispersion(m::GeneralizedLinearMixedModel, sqr::Bool = false) =
116-
dispersion(m.resp, dof_residual(m), sqr)
115+
function GLM.dispersion(m::GeneralizedLinearMixedModel, sqr::Bool = false)
116+
# adapted from GLM.dispersion(::AbstractGLM, ::Bool)
117+
# TODO: PR for a GLM.dispersion(resp::GLM.GlmResp, dof_residual::Int, sqr::Bool)
118+
r = m.resp
119+
if dispersion_parameter(r.d)
120+
wrkwt, wrkresid = r.wrkwt, r.wrkresid
121+
s = sum(i -> wrkwt[i] * abs2(wrkresid[i]), eachindex(wrkwt, wrkresid)) / dof_residual(m)
122+
sqr ? s : sqrt(s)
123+
else
124+
one(eltype(r.mu))
125+
end
126+
end
117127

118128
GLM.dispersion_parameter(m::GeneralizedLinearMixedModel) = dispersion_parameter(m.resp.d)
119129

120-
function StatsBase.dof(m::GeneralizedLinearMixedModel)
130+
function StatsBase.dof(m::GeneralizedLinearMixedModel)::Int
121131
length(m.β) + length(m.θ) + GLM.dispersion_parameter(m.resp.d)
122132
end
123133

134+
function StatsBase.dof_residual(m::GeneralizedLinearMixedModel)::Int
135+
nobs(m) - dof(m)
136+
end
137+
124138
fit(
125139
::Type{GeneralizedLinearMixedModel},
126140
f::FormulaTerm,

src/linearmixedmodel.jl

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,12 @@ GLM.dispersion_parameter(m::LinearMixedModel) = true
287287
StatsBase.dof(m::LinearMixedModel) = size(m)[2] + (m) + 1
288288

289289
function StatsBase.dof_residual(m::LinearMixedModel)::Int
290-
(n, p, q, k) = size(m)
291-
n - m.optsum.REML * p
290+
# nobs - rank(FE) - 1 (dispersion)
291+
# this differs from lme4 by not including nθ
292+
# a better estimate would be a number somewhere between the number of
293+
# variance components and the number of conditional modes
294+
# nobs, rank FE, num conditional modes, num grouping vars
295+
nobs(m) - size(m)[2] - 1
292296
end
293297

294298
"""
@@ -528,7 +532,7 @@ Return negative twice the log-likelihood of model `m`
528532
"""
529533
function objective(m::LinearMixedModel)
530534
wts = m.sqrtwts
531-
logdet(m) + dof_residual(m) * (1 + log2π + log(varest(m))) -
535+
logdet(m) + ssqdenom(m) * (1 + log2π + log(varest(m))) -
532536
(isempty(wts) ? 0 : 2 * sum(log, wts))
533537
end
534538

@@ -778,8 +782,16 @@ function σρs(m::LinearMixedModel)
778782
NamedTuple{fnames(m)}(((σρs(t, σ) for t in m.reterms)...,))
779783
end
780784

785+
"""
786+
size(m::LinearMixedModel)
787+
788+
Returns the size of a mixed model as a tuple of length four:
789+
the number of observations, the number of (non-singular) fixed-effects parameters,
790+
the number of conditional modes (random effects), the number of grouping variables
791+
"""
781792
function Base.size(m::LinearMixedModel)
782-
n, p = size(first(m.feterms))
793+
n = size(first(m.feterms),1)
794+
p = first(m.feterms).rank
783795
n, p, sum(size.(m.reterms, 2)), length(m.reterms)
784796
end
785797

@@ -792,6 +804,20 @@ This value is the contents of the `1 × 1` bottom right block of `m.L`
792804
"""
793805
sqrtpwrss(m::LinearMixedModel) = first(m.L.blocks[end, end])
794806

807+
"""
808+
ssqdenom(m::LinearMixedModel)
809+
810+
Return the denominator for penalized sums-of-squares.
811+
812+
For MLE, this value is either the number of observation for ML. For REML, this
813+
value is the number of observations minus the rank of the fixed-effects matrix.
814+
The difference is analagous to the use of n or n-1 in the denominator when
815+
calculating the variance.
816+
"""
817+
function ssqdenom(m::LinearMixedModel)::Int
818+
nobs(m) - m.optsum.REML * first(m.feterms).rank
819+
end
820+
795821
"""
796822
std(m::MixedModel)
797823
@@ -898,7 +924,7 @@ end
898924
899925
Returns the estimate of σ², the variance of the conditional distribution of Y given B.
900926
"""
901-
varest(m::LinearMixedModel) = pwrss(m) / dof_residual(m)
927+
varest(m::LinearMixedModel) = pwrss(m) / ssqdenom(m)
902928

903929
"""
904930
vcov(m::LinearMixedModel)

test/fit.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ end
2121
end
2222

2323
@testset "Normal Distribution GLMM" begin
24-
@test_broken(isa(fit(MixedModel, @formula(yield ~ 1 + (1|batch)), MixedModels.dataset(:dyestuff),
25-
Normal(), LogLink),
26-
GeneralizedLinearMixedModel))
24+
@test isa(fit(MixedModel, @formula(yield ~ 1 + (1|batch)), MixedModels.dataset(:dyestuff),
25+
Normal(), SqrtLink()),
26+
GeneralizedLinearMixedModel)
2727
end

0 commit comments

Comments
 (0)