Additional Functionality in Other Packages

Several packages extend the functionality of MixedModels.jl, both in ways specific to mixed models and in ways applicable to more general regression models. In the following, we will use the models from the previous sections to showcase this functionality.

using MixedModels
progress = isinteractive()
false
insteval = MixedModels.dataset("insteval")
ie1 = fit(MixedModel,
          @formula(y ~ 1 + studage + lectage + service + (1|s) + (1|d) + (1|dept)),
          insteval; progress)
Est.SEzpσ_sσ_dσ_dept
(Intercept)3.29080.0324101.45<1e-990.32640.51060.0787
studage: 40.05190.02322.240.0249
studage: 60.07210.02403.010.0026
studage: 80.13630.02645.17<1e-06
lectage: 2-0.08080.0154-5.25<1e-06
lectage: 3-0.11020.0167-6.59<1e-10
lectage: 4-0.18920.0196-9.65<1e-21
lectage: 5-0.16440.0214-7.68<1e-13
lectage: 6-0.24600.0205-12.01<1e-32
service: Y-0.07270.0135-5.40<1e-07
Residual1.1762
ie2 = fit(MixedModel,
          @formula(y ~ 1 + studage + lectage + service +
                      (1 | s) +
                      (1 + service | d) +
                      (1 + service | dept)),
          insteval; progress)
Est.SEzpσ_sσ_dσ_dept
(Intercept)3.29850.0308107.27<1e-990.32420.51600.0642
studage: 40.05020.02322.160.0306
studage: 60.05730.02422.370.0180
studage: 80.11280.02684.21<1e-04
lectage: 2-0.07870.0156-5.03<1e-06
lectage: 3-0.10360.0169-6.14<1e-09
lectage: 4-0.18370.0199-9.21<1e-19
lectage: 5-0.15030.0217-6.94<1e-11
lectage: 6-0.22320.0209-10.66<1e-25
service: Y-0.02810.0498-0.560.5731 0.39060.1640
Residual1.1698
sleepstudy = MixedModels.dataset("sleepstudy")
ss1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1|subj)), sleepstudy; progress)
Est.SEzpσ_subj
(Intercept)251.40519.506226.45<1e-9936.0121
days10.46730.801713.06<1e-38
Residual30.8954
ss2 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress)
Est.SEzpσ_subj
(Intercept)251.40516.632337.91<1e-9923.7805
days10.46731.50226.97<1e-115.7169
Residual25.5918
using DataFrames
contra = DataFrame(MixedModels.dataset("contra"))
contra[!, :anych] .= contra[!, :livch] .!= "0"
contrasts = Dict(:livch => EffectsCoding(; base="0"),
                 :urban => HelmertCoding(),
                 :anych => HelmertCoding())
gm1 = fit(MixedModel,
          @formula(use ~ 1 + urban + anych * age + abs2(age) + (1 | dist & urban)),
          contra,
          Bernoulli();
          contrasts,
          progress)
Est.SEzpσ_dist & urban
(Intercept)-0.34100.1265-2.700.00700.5683
urban: Y0.39340.08534.61<1e-05
anych: true0.60650.10455.80<1e-08
age-0.01290.0112-1.160.2464
abs2(age)-0.00560.0008-6.67<1e-10
anych: true & age0.03320.01282.590.0095

MixedModelsExtras.jl

https://palday.github.io/MixedModelsExtras.jl/v2

MixedModelsExtras.jl is a collection of odds-and-ends that may be useful when working with mixed effects models, but which we do not want to include in MixedModels.jl at this time. Some functions may one day migrate to MixedModels.jl, when we are happy with their performance and interface (e.g. vif), but some are intentionally omitted from MixedModels.jl (e.g. r2, adjr2).

using MixedModelsExtras
r2(ss2; conditional=true)
0.8263130612096021
r2(ss2; conditional=false)
0.28647138741126765
icc(ie2)
0.2885292866744761
icc(ie2, :dept)
0.016119194950382547
vif(ie1)
9-element Vector{Float64}:
 1.5141903374729053
 1.7354060220617624
 1.7822316986634812
 1.449378975046207
 1.4380891514780725
 1.5948966178188415
 1.4634020911560608
 1.8267103207894357
 1.0161785415795814
DataFrame(; coef=fixefnames(ie1)[2:end], VIF=vif(ie1))
9×2 DataFrame
RowcoefVIF
StringFloat64
1studage: 41.51419
2studage: 61.73541
3studage: 81.78223
4lectage: 21.44938
5lectage: 31.43809
6lectage: 41.5949
7lectage: 51.4634
8lectage: 61.82671
9service: Y1.01618
gvif(ie1)
3-element Vector{Float64}:
 1.3110872223511254
 1.325731162491792
 1.0161785415795814
DataFrame(; term=termnames(ie1)[2][2:end], GVIF=gvif(ie1))
3×2 DataFrame
RowtermGVIF
StringFloat64
1studage1.31109
2lectage1.32573
3service1.01618

RegressionFormulae.jl

https://github.com/kleinschmidt/RegressionFormulae.jl

RegressionFormulae.jl provides a few extensions to the somewhat more restricted variant of the Wilkinson-Roger notation found in Julia. In particular, it adds / for nested designs within the fixed effects and ^ for computing interactions only up to a certain order.

using RegressionFormulae

fit(MixedModel,
          @formula(y ~ 1 + service / (studage + lectage) +
                      (1 | s) +
                      (1 | d) +
                      (1 | dept)),
          insteval; progress)
Est.SEzpσ_sσ_dσ_dept
(Intercept)3.27880.034994.06<1e-990.32660.50990.0799
service: Y-0.04880.0275-1.780.0758
service: N & studage: 40.09040.02753.280.0010
service: Y & studage: 40.00930.02850.330.7442
service: N & studage: 60.07540.02752.740.0062
service: Y & studage: 60.06480.03082.100.0354
service: N & studage: 80.13980.03054.58<1e-05
service: Y & studage: 80.13490.03344.04<1e-04
service: N & lectage: 2-0.05110.0197-2.600.0093
service: Y & lectage: 2-0.11390.0233-4.89<1e-05
service: N & lectage: 3-0.10650.0211-5.06<1e-06
service: Y & lectage: 3-0.10230.0267-3.830.0001
service: N & lectage: 4-0.17970.0252-7.14<1e-12
service: Y & lectage: 4-0.19390.0294-6.61<1e-10
service: N & lectage: 5-0.20790.0283-7.34<1e-12
service: Y & lectage: 5-0.11800.0312-3.770.0002
service: N & lectage: 6-0.27120.0264-10.27<1e-24
service: Y & lectage: 6-0.22680.0293-7.74<1e-14
Residual1.1759
fit(MixedModel,
          @formula(y ~ 1 + (studage + lectage + service)^2 +
                      (1 | s) +
                      (1 | d) +
                      (1 | dept)),
          insteval; progress)
Est.SEzpσ_sσ_dσ_dept
(Intercept)3.22850.036887.85<1e-990.32640.50920.0800
studage: 40.12800.03403.770.0002
studage: 60.15250.03434.45<1e-05
studage: 80.23260.03995.83<1e-08
lectage: 20.05540.03021.840.0662
lectage: 3-0.02730.0640-0.430.6702
lectage: 4-0.13020.0724-1.800.0720
lectage: 5-0.08850.0807-1.100.2728
lectage: 6-0.17070.0836-2.040.0411
service: Y-0.03640.0278-1.310.1912
studage: 4 & lectage: 2-0.11170.0400-2.800.0052
studage: 6 & lectage: 2-0.16380.0397-4.13<1e-04
studage: 8 & lectage: 2-0.16830.0469-3.590.0003
studage: 4 & lectage: 3-0.11050.0694-1.590.1112
studage: 6 & lectage: 3-0.12950.0688-1.880.0599
studage: 8 & lectage: 3-0.08110.0714-1.140.2557
studage: 4 & lectage: 40.04200.07650.550.5833
studage: 6 & lectage: 4-0.12730.0770-1.650.0983
studage: 8 & lectage: 4-0.10950.0797-1.370.1694
studage: 4 & lectage: 5-0.17940.0964-1.860.0627
studage: 6 & lectage: 5-0.14000.0831-1.680.0921
studage: 8 & lectage: 5-0.17290.0864-2.000.0453
studage: 4 & lectage: 60.04910.09730.500.6137
studage: 6 & lectage: 6-0.08340.0853-0.980.3282
studage: 8 & lectage: 6-0.18210.0867-2.100.0358
studage: 4 & service: Y-0.08410.0314-2.670.0075
studage: 6 & service: Y-0.00680.0333-0.210.8376
studage: 8 & service: Y0.01570.03640.430.6652
lectage: 2 & service: Y-0.08410.0301-2.790.0053
lectage: 3 & service: Y-0.00310.0342-0.090.9277
lectage: 4 & service: Y-0.03500.0379-0.930.3547
lectage: 5 & service: Y0.06510.04161.560.1176
lectage: 6 & service: Y0.01370.03760.370.7150
Residual1.1755

BoxCox.jl

https://palday.github.io/BoxCox.jl/v0.3/

BoxCox.jl implements a the Box-Cox transformation in an efficient way. Via package extensions, it supports specializations for MixedModels.jl and several plotting functions, but does not incur a dependency penalty for this functionality when MixedModels.jl or Makie.jl are not loaded.

using BoxCox

bc = fit(BoxCoxTransformation, ss2)
Box-Cox transformation

estimated λ: -1.0749
resultant transformation:

 y^-1.1 - 1
------------
    -1.1
using CairoMakie
boxcoxplot(bc; conf_level=0.95)
Example block output

The estimated λ is very close to -1, i.e. the reciprocal of reaction time, which has a natural interpretation as speed. In other words, the Box-Cox transformation suggests that we should consider modelling the sleepstudy data as speed (reaction per unit time) instead of reaction time:

fit(MixedModel, @formula(1000 / reaction ~ 1 + days + (1 + days|subj)), sleepstudy)
Est.SEzpσ_subj
(Intercept)3.96580.105637.55<1e-990.4190
days-0.11100.0151-7.37<1e-120.0566
Residual0.2698

(We multiply by 1000 to get the responses per second instead of the responses per millisecond.)

Tip

BoxCox.jl also works with classical linear models.

Effects.jl

https://beacon-biosignals.github.io/Effects.jl/v1.2/

Effects.jl provides a convenient method to compute effects, i.e. predictions and associated prediction intervals computed at points on a reference grid. For models with a nonlinear link function, Effects.jl will also compute appropriate errors on the response scale based on the difference method.

For MixedModels.jl, the predictions are computed based on the fixed effects only.

The functionality of Effects.jl was inspired by the effects and emmeans packages in R and the methods within are based on @fox:effect:2003.

using Effects
design = Dict(:age => -15:1:20,
              :anych => [true, false])

eff_logit = effects(design, gm1; eff_col="use", level=0.95)

first(eff_logit, 10)
10×6 DataFrame
Rowageanychuseerrlowerupper
Int64BoolFloat64Float64Float64Float64
1-15true-1.469750.286495-2.03127-0.908228
2-14true-1.286290.257772-1.79151-0.781063
3-13true-1.114080.231084-1.56699-0.66116
4-12true-0.9531190.206514-1.35788-0.548359
5-11true-0.8034140.184161-1.16436-0.442465
6-10true-0.6649610.164144-0.986677-0.343245
7-9true-0.5377590.146597-0.825084-0.250435
8-8true-0.421810.131656-0.679852-0.163768
9-7true-0.3171120.119434-0.551199-0.0830263
10-6true-0.2236670.109972-0.439209-0.00812554
eff_prob = effects(design, gm1; eff_col="use", level=0.95, invlink=AutoInvLink())

first(eff_prob, 10)
10×6 DataFrame
Rowageanychuseerrlowerupper
Int64BoolFloat64Float64Float64Float64
1-15true0.1869810.04355270.1016190.272343
2-14true0.2164820.04372260.1307870.302177
3-13true0.2471120.04299260.1628480.331376
4-12true0.2782580.04147430.196970.359546
5-11true0.3092960.03934270.2321850.386406
6-10true0.3396260.03681420.2674720.411781
7-9true0.3687090.03412230.3018310.435587
8-8true0.3960840.03149240.334360.457808
9-7true0.421380.02912020.3643050.478454
10-6true0.4443150.0271520.3910980.497532

Effects are particularly nice for visualizing the model fit and its predictions.

using AlgebraOfGraphics # like ggplot2, but an algebra instead of a grammar
using CairoMakie

plt1 = data(eff_logit) * mapping(:age; color=:anych) *
      (mapping(:use) * visual(Lines) +
       mapping(:lower, :upper) * visual(Band; alpha=0.3))
draw(plt1)
Example block output
plt2 = data(eff_prob) * mapping(:age; color=:anych) *
      (mapping(:use) * visual(Lines) +
       mapping(:lower, :upper) * visual(Band; alpha=0.3))
draw(plt2)
Example block output
using Statistics: mean
contra_by_age = transform(contra,
                          :age => ByRow(x -> round(Int, x)),
                          :use => ByRow(==("Y"));
                          renamecols=false)
contra_by_age = combine(groupby(contra_by_age, [:age, :anych]),
                        :use => mean => :use)
plt3 = plt2 +
       data(contra_by_age) *
       mapping(:age, :use;
               color=:anych => "children") * visual(Scatter)

draw(plt3;
     axis=(; title="Estimated contraceptive use by age and children",
            limits=(nothing, (0, 1)) # ylim=0,1, xlim=auto
            ))
Example block output

Effects and estimated marginal (least squares) means are closely related and partially concepts. Effects.jl provides convenience function emmeans and empairs for computing EM means and pairwise differences of EM means.

emmeans(gm1)
4×5 DataFrame
Rowageurbananychuse: Yerr
Float32StringBoolFloat64Float64
10.00204738Nfalse-1.340950.221169
20.00204738Yfalse-0.5542070.229908
30.00204738Ntrue-0.1278640.112245
40.00204738Ytrue0.6588790.149692
empairs(gm1; dof=Inf)
6×8 DataFrame
Rowageurbananychuse: YerrdoftPr(>|t|)
Float32StringAnyFloat64Float64Float64Float64Float64
10.00204738N > Yfalse-0.7867430.31902Inf-2.466130.0136582
20.00204738Nfalse > true-1.213090.248022Inf-4.891051.00302e-6
30.00204738N > Yfalse > true-1.999830.267065Inf-7.488196.98311e-14
40.00204738Y > Nfalse > true-0.4263430.255845Inf-1.666410.0956311
50.00204738Yfalse > true-1.213090.274345Inf-4.421769.79016e-6
60.00204738N > Ytrue-0.7867430.1871Inf-4.204932.6116e-5
Tip

Effects.jl will work with any package that supports the StatsAPI.jl-based RegressionModel interface.

Margins.jl

StandardizedPredictors.jl

https://beacon-biosignals.github.io/StandardizedPredictors.jl/v1/

StandardizedPredictors.jl provides a convenient way to express centering, scaling, and z-standardization as a "contrast" via the pseudo-contrasts Center, Scale, ZScore. Because these use the usual contrast machinery, they work well with any packages that use that machinery correctly (e.g. Effects.jl). The default behavior is to empirically compute the center and scale, but these can also be explicitly provided, either as a number or as a function (e.g. median to use the median for centering.)

using StandardizedPredictors

contrasts = Dict(:days => Center())
fit(MixedModel,
    @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy;
    contrasts)
Est.SEzpσ_subj
(Intercept)298.50798.795033.94<1e-9936.4259
days(centered: 4.5)10.46731.50226.97<1e-115.7168
Residual25.5918
Tip

StandardizedPredictors.jl will work with any package that supports the StatsModels.jl-based @formula and contrast machinery.

MixedModelsSmallSample.jl

https://arnostrouwen.github.io/MixedModelsSmallSample.jl/stable/

MixedModelsSmallSample.jl provides the Satterthwaite and Kenward-Roger approximations for denominator degrees of freedom.

Tip

There is a reason why MixedModelsSmallSample has "small sample" in its name:

  1. the underlying computations, especially in the Kenward-Roger case, do not scale well to large samples and may be very slow and memory intensive.
  2. the correction has the biggest impact on small samples – the $t$ distribution rapidly converges to the normal distribution as the degrees of freedom gets large.
using MixedModelsSmallSample

sw = small_sample_adjust(ss1, Satterthwaite())
coeftable(ss1)
──────────────────────────────────────────────────
                Coef.  Std. Error      z  Pr(>|z|)
──────────────────────────────────────────────────
(Intercept)  251.405     9.50618   26.45    <1e-99
days          10.4673    0.801735  13.06    <1e-38
──────────────────────────────────────────────────
coeftable(sw)
────────────────────────────────────────────────────────────
                Coef.  Std. Error     DenDF      t  Pr(>|t|)
────────────────────────────────────────────────────────────
(Intercept)  251.405     9.50618    24.4905  26.45    <1e-18
days          10.4673    0.801735  162.0     13.06    <1e-26
────────────────────────────────────────────────────────────
# Kenward-Roger degrees of freedom requires REML fit

ss1_reml = refit!(ss1; REML=true)
kr = small_sample_adjust(ss1_reml, KenwardRoger())

coeftable(ss1_reml)
──────────────────────────────────────────────────
                Coef.  Std. Error      z  Pr(>|z|)
──────────────────────────────────────────────────
(Intercept)  251.405     9.74672   25.79    <1e-99
days          10.4673    0.804221  13.02    <1e-38
──────────────────────────────────────────────────
coeftable(kr)
────────────────────────────────────────────────────────────
                Coef.  Std. Error     DenDF      t  Pr(>|t|)
────────────────────────────────────────────────────────────
(Intercept)  251.405     9.74672    22.8102  25.79    <1e-17
days          10.4673    0.804221  161.0     13.02    <1e-26
────────────────────────────────────────────────────────────

RCall.jl and JellyMe4.jl

https://juliainterop.github.io/RCall.jl/stable/

https://github.com/palday/JellyMe4.jl/

RCall.jl provides a convenient interface for interoperability with R from Julia. JellyMe4.jl extends the functionality of RCall so that MixedModels.jl-fitted models and lme4-fitted models can be translated to each other. In practical terms, this means that you can enjoy the speed of Julia for model fitting, but use all the extra packages you love from R's larger ecosystem.

MixedModelsSerialization.jl

https://juliamixedmodels.github.io/MixedModelsSerialization.jl/stable/api/

MixedModelsSerialization.jl provides a reduced-memory "summary" representation of a fitted model, LinearMixedModelSummary, that discards the model matrices and other data-sized fields while retaining the fixed- and random-effects estimates, θ, the log-likelihood, and other quantities needed to support many StatsAPI and MixedModels methods (e.g. coef, vcov, VarCorr, coeftable). This makes it practical to save and later reload models fit to very large datasets without also serializing the original data. The package is a proving ground for these ideas so that its API can evolve and have breaking releases independently of MixedModels.jl.

MixedModelsSim.jl

https://repsychling.github.io/MixedModelsSim.jl/stable/

MixedModelsSim.jl provides utilities for generating experimental designs, especially designs with crossed grouping factors (e.g. "Subject" and "Item") and both within- and between-unit experimental factors. Combined with simulate/parametricbootstrap from MixedModels.jl, it is commonly used for power analysis: create a design, specify hypothesized effect sizes and variance components, simulate many datasets, and examine the distribution of the resulting test statistics.