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}:
17.9053 8.87049 10.7419 6.66026 … 11.1886 2.38817 13.3819
14.3933 15.454 11.7659 4.8754 3.18727 1.37389 19.7361
12.4731 18.9506 13.8243 20.5309 8.4603 9.88742 8.92185
19.1302 16.534 13.4568 19.2871 7.97039 18.6621 9.28247
2.0325 8.48175 15.1328 13.0128 17.7138 8.29587 5.18685
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.