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×120198 Matrix{Float64}:
  6.21684   7.63839  16.9123    2.23046  …  14.606     5.00478   6.45514
  9.13038   6.58745  12.43      5.85597      3.64583  18.3988   13.6772
  7.50148   0.66175  16.4338   18.3745       8.45273   6.63517   9.94464
  1.8097   13.7348   18.5884    2.46048     19.0057    7.17104   8.5815
 16.6189   13.9638    1.75852   0.87182     15.8026   12.5732   16.4046

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.