Mass Univariate Linear Mixed Models

using UnfoldMixedModels
using UnfoldSim

using UnfoldMakie, CairoMakie # plotting
using DataFrames
using CategoricalArrays

This notebook is similar to the Unfold.jl Mass Univariate Linear Models (no overlap correction) tutorial, but fits mass-univariate mixed models - that is, one model over all subjects, instead of one model per subject. This allows to include item effects, for example.

Mass Univariate Mixed Models

Again we have 4 steps:

  1. Split data into epochs
  2. Specify a formula
  3. Fit a linear model to each time point & channel
  4. Visualize the results.

1. Epoching

data, evts = UnfoldSim.predef_eeg(10; return_epoched = true) # simulate 10 subjects
data = reshape(data, 1, size(data, 1), :) # concatenate the data into a long EEG dataset
times = range(0, length = size(data, 2), step = 1 / 100)
transform!(evts, :subject => categorical => :subject); # :subject must be categorical, otherwise MixedModels.jl complains

The events dataFrame has an additional column (besides being much taller): subject

first(evts, 6)
6×5 DataFrame
Rowsubjectitemcontinuousconditionlatency
Cat…StringFloat64StringInt64
1S01I0382.77778face62
2S01I0671.66667car132
3S01I032-3.88889face196
4S01I013-2.77778face249
5S01I0582.77778face303
6S01I094-1.66667face366

2. Formula specification

We define the formula. Importantly, we need to specify a random effect. We use zerocorr to speed up the calculation.

f = @formula 0 ~ 1 + condition * continuous + zerocorr(1 + condition * continuous | subject);

3. Model fitting

We can now run the LinearMixedModel at each time point.

m = fit(UnfoldModel, f, evts, data, times)

Progress:   4%|█▉                                       |  ETA: 0:00:11
   channel: 1
      time: 2





Progress: 100%|█████████████████████████████████████████| Time: 0:00:00
   channel: 1
      time: 45

4. Visualization of results

Let's start with the fixed effects. We see the condition effects and some residual overlap activity in the fixed effects.

results = coeftable(m)

res_fixef = results[isnothing.(results.group), :]
plot_erp(res_fixef)
Example block output

And now comes the random effect:

res_ranef = results[results.group .== :subject, :]
plot_erp(res_ranef)
Example block output

Statistics

Check out the LMM p-value tutorial