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.0307107.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.1639
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.7807
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.34110.1265-2.700.00700.5682
urban: Y0.39340.08534.61<1e-05
anych: true0.60660.10455.80<1e-08
age-0.01290.0112-1.160.2461
abs2(age)-0.00560.0008-6.67<1e-10
anych: true & age0.03330.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.8263135090639312
r2(ss2; conditional=false)
0.28647139510771
icc(ie2)
0.28852801934725547
icc(ie2, :dept)
0.016117901466392092
vif(ie1)
9-element Vector{Float64}:
 1.5141903373605303
 1.735406021956399
 1.7822316985578348
 1.449378975108032
 1.4380891515108385
 1.5948966180649566
 1.4634020913401908
 1.8267103210543985
 1.0161785415502533
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.3110872223681633
 1.3257311624888224
 1.0161785415502536
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.07<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.0757
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)
72×6 DataFrame
Rowageanychuseerrlowerupper
Int64BoolFloat64Float64Float64Float64
1-15true-1.46990.286497-2.03143-0.908381
2-14true-1.286420.257773-1.79165-0.781195
3-13true-1.114190.231085-1.56711-0.661271
4-12true-0.9532110.206514-1.35797-0.548451
5-11true-0.8034850.18416-1.16443-0.442538
6-10true-0.6650130.164142-0.986726-0.3433
7-9true-0.5377940.146594-0.825114-0.250474
8-8true-0.4218280.131653-0.679864-0.163793
9-7true-0.3171150.11943-0.551194-0.0830364
10-6true-0.2236560.109968-0.439189-0.00812241
11-5true-0.1414490.103188-0.3436940.0607961
12-4true-0.07049540.0988549-0.2642480.123257
13-3true-0.01079490.0965803-0.2000890.178499
14-2true0.03765250.0958776-0.1502640.225569
15-1true0.07484670.0962393-0.1137790.263472
160true0.1007880.0972048-0.089730.291306
171true0.1154760.0983994-0.07738320.308335
182true0.1189110.0995473-0.07619820.31402
193true0.1110930.10047-0.08582520.308011
204true0.09202160.10108-0.1060910.290134
215true0.06169730.101371-0.1369860.260381
226true0.02011990.101421-0.1786620.218902
237true-0.03271060.101389-0.231430.166009
248true-0.09679420.101518-0.2957650.102177
259true-0.1721310.102131-0.3723040.0280419
2610true-0.2587210.103623-0.461818-0.0556232
2711true-0.3565640.10643-0.565163-0.147964
2812true-0.465660.110983-0.683182-0.248137
2913true-0.5860090.117651-0.816601-0.355417
3014true-0.7176110.1267-0.965938-0.469283
3115true-0.8604660.138272-1.13148-0.589457
3216true-1.014570.152403-1.31328-0.715869
3317true-1.179940.169051-1.51127-0.848601
3418true-1.356550.188133-1.72529-0.987816
3519true-1.544420.20955-1.95513-1.13371
3620true-1.743540.233203-2.20061-1.28647
37-15false-1.685660.205918-2.08926-1.28207
38-14false-1.568680.18562-1.93249-1.20487
39-13false-1.462950.167769-1.79177-1.13413
40-12false-1.368470.152687-1.66773-1.06921
41-11false-1.285240.140743-1.56109-1.00939
42-10false-1.213270.132297-1.47257-0.953975
43-9false-1.152550.127603-1.40265-0.902457
44-8false-1.103090.1267-1.35142-0.854759
45-7false-1.064870.129367-1.31843-0.81132
46-6false-1.037920.135164-1.30283-0.772998
47-5false-1.022210.14356-1.30358-0.740837
48-4false-1.017760.154042-1.31967-0.715839
49-3false-1.024560.166185-1.35027-0.698838
50-2false-1.042610.179673-1.39476-0.690455
51-1false-1.071910.194288-1.45271-0.691117
520false-1.112470.209891-1.52385-0.701093
531false-1.164280.226408-1.60804-0.720533
542false-1.227350.243808-1.7052-0.749496
553false-1.301670.26209-1.81535-0.787982
564false-1.387240.281277-1.93853-0.835947
575false-1.484060.301406-2.07481-0.893319
586false-1.592140.322525-2.22428-0.960005
597false-1.711470.344683-2.38704-1.03591
608false-1.842060.367935-2.5632-1.12092
619false-1.983890.392335-2.75286-1.21493
6210false-2.136980.417935-2.95612-1.31785
6311false-2.301330.444786-3.17309-1.42956
6412false-2.476920.472935-3.40386-1.54999
6513false-2.663770.502425-3.64851-1.67904
6614false-2.861870.533297-3.90712-1.81663
6715false-3.071230.565588-4.17976-1.9627
6816false-3.291840.59933-4.4665-2.11717
6917false-3.52370.634554-4.7674-2.28
7018false-3.766810.671286-5.08251-2.45112
7119false-4.021180.709549-5.41187-2.63049
7220false-4.28680.749364-5.75553-2.81808
eff_prob = effects(design, gm1; eff_col="use", level=0.95, invlink=AutoInvLink())
72×6 DataFrame
Rowageanychuseerrlowerupper
Int64BoolFloat64Float64Float64Float64
1-15true0.1869570.232934-0.2695860.6435
2-14true0.2164590.201976-0.1794050.612324
3-13true0.2470910.173986-0.09391530.588097
4-12true0.278240.149054-0.01390010.570379
5-11true0.309280.1272030.05996710.558594
6-10true0.3396140.1083970.127160.552069
7-9true0.3687010.09254490.1873160.550086
8-8true0.3960790.07950810.2402460.551912
9-7true0.4213790.06910480.2859360.556822
10-6true0.4443180.06110720.324550.564086
11-5true0.4646970.0552370.3564340.572959
12-4true0.4823830.0511690.3820940.582673
13-3true0.4973010.04855080.4021440.592459
14-2true0.5094120.04703640.4172220.601602
15-1true0.5187030.04631970.4279180.609488
160true0.5251760.04615520.4347130.615638
171true0.5288370.04636210.4379690.619705
182true0.5296930.04681780.4379310.621454
193true0.5277450.04744760.4347490.62074
204true0.5229890.04821610.4284870.617491
215true0.5154190.04912240.4191410.611698
226true0.505030.05020060.4066390.603421
237true0.4918230.05152380.3908380.592808
248true0.475820.05321360.3715240.580117
259true0.4570730.05544960.3483940.565752
2610true0.4356780.05847680.3210660.550291
2711true0.4117920.06260320.2890920.534492
2812true0.3856440.0681830.2520080.51928
2913true0.3575510.07558490.2094070.505695
3014true0.3279190.08515270.1610230.494815
3115true0.2972420.09717210.1067880.487696
3216true0.2660860.1118510.04686160.48531
3317true0.2350640.129314-0.01838630.488514
3418true0.2048010.149603-0.08841580.498019
3519true0.1758940.172691-0.1625750.514363
3620true0.1488640.198488-0.2401650.537893
37-15false0.1563470.173724-0.1841450.496839
38-14false0.1724050.153619-0.1286820.473492
39-13false0.1880170.136226-0.07898070.455015
40-12false0.2028670.121712-0.03568340.441418
41-11false0.2166590.110250.0005737920.432744
42-10false0.2291230.1019840.02923670.429009
43-9false0.2400230.0969750.04995550.430091
44-8false0.2491620.09513160.06270740.435616
45-7false0.2563790.09619990.06783070.444927
46-6false0.2615520.09981160.06592530.45718
47-5false0.2645970.1055740.05767560.471519
48-4false0.2654650.1131490.04369670.487233
49-3false0.2641410.1222890.02445910.503823
50-2false0.2606470.1328420.0002816590.521013
51-1false0.2550390.144737-0.02863940.538718
520false0.247410.157962-0.06218990.55701
531false0.237890.172548-0.1002990.576078
542false0.2266460.18855-0.1429050.596196
553false0.2138840.206033-0.1899320.617701
564false0.1998490.225064-0.2412690.640966
575false0.1848140.245702-0.2967530.666382
586false0.1690830.267991-0.3561710.694336
597false0.1529730.291956-0.419250.725196
608false0.1368080.317598-0.4856730.75929
619false0.1209040.3449-0.5550870.796896
6210false0.1055540.37382-0.6271210.838228
6311false0.09101320.404305-0.7014090.883436
6412false0.07749190.436286-0.7776130.932597
6513false0.06514520.469694-0.8554390.985729
6614false0.05407080.504461-0.9346551.0428
6715false0.04430970.540527-1.01511.10372
6816false0.03585220.577843-1.09671.1684
6917false0.02864540.616377-1.179431.23672
7018false0.02260290.656113-1.263351.30856
7119false0.01761590.69705-1.348581.38381
7220false0.01356230.739201-1.435251.46237

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
Float64StringBoolFloat64Float64
10.00204757Nfalse-1.341210.221182
20.00204757Yfalse-0.5543790.229917
30.00204757Ntrue-0.1278160.112238
40.00204757Ytrue0.6590180.149685
empairs(gm1; dof=Inf)
6×8 DataFrame
Rowageurbananychuse: YerrdoftPr(>|t|)
Float64StringAnyFloat64Float64Float64Float64Float64
10.00204757N > Yfalse-0.7868340.319035Inf-2.46630.0136519
20.00204757Nfalse > true-1.21340.24803Inf-4.892149.9747e-7
30.00204757N > Yfalse > true-2.000230.267071Inf-7.48956.91368e-14
40.00204757Y > Nfalse > true-0.4265620.25585Inf-1.667240.0954671
50.00204757Yfalse > true-1.21340.274349Inf-4.422829.74196e-6
60.00204757N > Ytrue-0.7868340.187091Inf-4.205632.60358e-5
Tip

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

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.

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.

<!– MixedModelsSerializtion.jl MixedModelsSim.jl –>