using UnfoldDecode
using UnfoldSim
using UnfoldMakie
using CairoMakie
using Unfold

Overlap-corrected decoding

We will try to introduce as many fancy features as possible Please read the "tutorial" first

Simulation

multi-event

dat, evt = UnfoldSim.predef_eeg()
evt.event = rand(["eventA", "eventB"], size(evt, 1)) # add random events
dat = repeat(dat', 5)
dat .= dat .+ 20 .* rand(size(dat)...)
5×120199 Matrix{Float64}:
  9.01374   3.764      4.54548   15.7452   …   5.83032   3.0957     9.00664
 17.7199   16.565     14.6415     1.46751     19.5718   11.3176    10.2678
  2.18348  10.3004    11.5502    12.9094       5.30589   0.124008  16.7909
 17.3435    5.80782   14.3009    20.0696      15.5848   14.1926     1.14269
 11.2792    0.684888   0.639159  12.9794      12.9816   11.1436     6.8912

Overlap-model Definition

We have two basis functions now, with two different timewindows. Let's see if it works!

des = [
    "eventA" => (@formula(0 ~ 1 + condition + continuous), firbasis((-0.1, 1.0), 100)),
    "eventB" => (@formula(0 ~ 1 + continuous), firbasis((-0.3, 0.5), 100)),
]
2-element Vector{Pair{String, Tuple{StatsModels.FormulaTerm{StatsModels.ConstantTerm{Int64}}, Unfold.FIRBasis}}}: "eventA" => (0 ~ 1 + condition + continuous, �[0m�[22m╭──────────────────────────────────────────────────────────────────────────────╮�[22m�[0m111×111 SparseArrays.SparseMatrixCSC with 111 stored entries: �[22m│�[22m �[1m�[34m::BasisFunction�[22m�[39m �[22m│�[22m⎡⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤ �[22m│�[22m �[1mname: �[22m�[38;2;144;202;249m�[39m �[22m│�[22m⎢⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mkerneltype: �[22m�[38;2;144;202;249mUnfold.FIRBasis�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mwidth: �[22m�[38;2;144;202;249m111�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mheight: �[22m�[38;2;144;202;249m111�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mcolnames: �[22m�[38;2;144;202;249m[-0.1, -0.09 ... 1.0]�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mtimes: �[22m�[38;2;144;202;249m[-0.1, -0.09 ... 1.0]�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mcollabel: �[22m�[38;2;144;202;249mtime�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⎥ �[22m│�[22m �[1mshift_onset: �[22m�[38;2;144;202;249m-10�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⎥ �[22m│�[22m �[22m│�[22m⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⎦ �[0m�[22m╰──────────────────────────────────────────────────────────────────────────────╯�[22m�[0m�[0m ) "eventB" => (0 ~ 1 + continuous, �[0m�[22m╭──────────────────────────────────────────────────────────────────────────────╮�[22m�[0m81×81 SparseArrays.SparseMatrixCSC with 81 stored entries: �[22m│�[22m �[1m�[34m::BasisFunction�[22m�[39m �[22m│�[22m⎡⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤ �[22m│�[22m �[1mname: �[22m�[38;2;144;202;249m�[39m �[22m│�[22m⎢⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mkerneltype: �[22m�[38;2;144;202;249mUnfold.FIRBasis�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mwidth: �[22m�[38;2;144;202;249m81�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mheight: �[22m�[38;2;144;202;249m81�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mcolnames: �[22m�[38;2;144;202;249m[-0.3, -0.29 ... 0.5]�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mtimes: �[22m�[38;2;144;202;249m[-0.3, -0.29 ... 0.5]�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⎥ �[22m│�[22m �[1mcollabel: �[22m�[38;2;144;202;249mtime�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⎥ �[22m│�[22m �[1mshift_onset: �[22m�[38;2;144;202;249m-30�[39m �[22m│�[22m⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⎥ �[22m│�[22m �[22m│�[22m⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⎦ �[0m�[22m╰──────────────────────────────────────────────────────────────────────────────╯�[22m�[0m�[0m )

To show that it is possible, we explicitly specify the solver

customsolver = (x, y) -> Unfold.solver_default(x, y)
uf = Unfold.fit(UnfoldModel, des, evt, dat[1, :]; solver = customsolver);
plot_erp(coeftable(uf); mapping = (; col = :eventname))
Example block output

Fitting the Overlap-corrected LDA model

using MLJ, MultivariateStats, MLJMultivariateStatsInterface
LDA = @load LDA pkg = MultivariateStats
MLJMultivariateStatsInterface.LDA

you could use other parameters, check out ?LDA

ldaModel = LDA(
    method = :whiten,
    cov_w = SimpleCovariance(),
    cov_b = SimpleCovariance(),
    regcoef = 1e-3,
)

uf_lda = UnfoldDecode.fit(
    UnfoldDecodingModel,
    des,
    evt,
    dat,
    ldaModel,
    "eventA" => :condition;
    nfolds = 2,# only 2 folds to speed up computation
    unfold_fit_options = (; solver = customsolver), #customer solver for fun
    eventcolumn = :event, # actually the default, but maybe your event dataframe has a different name?
    multithreading = false,
) # who needs speed anyway :shrug:

plot_erp(coeftable(uf_lda))
Example block output

Voila, the model classified the correct period at the correct event


This page was generated using Literate.jl.