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.50901 13.7884 19.6804 15.9948 … 14.3846 11.1383 7.87808
7.93281 9.31424 16.7044 4.37156 12.4072 12.7546 3.5393
6.78216 13.4509 8.10916 3.9423 5.22966 14.2837 13.5129
7.14976 3.34022 8.09905 7.42073 17.6824 14.1518 4.83414
3.77254 2.53089 14.6006 15.1018 11.8468 2.95192 4.43227
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))

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))
Voila, the model classified the correct period at the correct event
This page was generated using Literate.jl.