FIR-Scaled duration predictors

using Unfold
using Interpolations

using UnfoldSim
using UnfoldMakie, CairoMakie
using DataFrames

data, evts = UnfoldSim.predef_eeg(sfreq = 10, n_repeats = 1)

evts.duration = 5:24
5:24

putting scale_duration = Interpolation.Linear() will introduce a Cameron-Hassall 2022 PNAS- Style basisfunction, that scales with the :duration column

basisfunction = firbasis(τ = (-1, 2), sfreq = 5, scale_duration = Interpolations.Linear())
╭──────────────────────────────────────────────────────────────────────────────╮8×16 SparseArrays.SparseMatrixCSC with 16 stored entries: │ ::BasisFunction │⎡⠉⠒⠤⣀⠀⠀⠀⠀⎤ │ name:  │⎣⠀⠀⠀⠀⠉⠒⠤⣀⎦ │ kerneltype: FIRBasis │ │ width: 16 │ │ height: NaN │ │ colnames: [-1.0, -0.8 ... 2.0] │ │ times: [-1.0, -0.8 ... 2.0] │ │ collabel: time │ │ shift_onset: -5 │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯

Two examples with duration = 10

Unfold.kernel(basisfunction, [0, 10])
10×16 SparseArrays.SparseMatrixCSC{Float64, Int64} with 20 stored entries:
⎡⠙⠢⢄⡀⠀⠀⠀⠀⎤
⎢⠀⠀⠀⠉⠲⢄⡀⠀⎥
⎣⠀⠀⠀⠀⠀⠀⠈⠓⎦

and duration = 20

Unfold.kernel(basisfunction, [0, 20])
20×16 SparseArrays.SparseMatrixCSC{Float64, Int64} with 38 stored entries:
⎡⠳⣄⠀⠀⠀⠀⠀⠀⎤
⎢⠀⠘⢧⡀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠹⣆⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠈⢳⡄⠀⎥
⎣⠀⠀⠀⠀⠀⠀⠙⢦⎦

let's fit a model

f = @formula 0 ~ 1 + condition
bf_vec = [Any => (f, basisfunction)]
m = fit(UnfoldModel, bf_vec, evts, data; eventfields = [:latency, :duration]);


# currently bugged for small matrices
# plot_designmatrix(designmatrix(m))
# thus using
heatmap(Matrix(modelmatrix(m))')
Example block output

As one can see, the designmatrix is nicely scaled

We can predict overlap-corrected results

p = predict(m; overlap = false)[1]
heatmap(p[1, :, :])
Example block output

note the missings which are displayed as white pixels.

Block-design predictors

In contrast, it is also possible to put scale_duration = true - which wil not scale the matrix as before, but introduce a step-function.

putting scale_duration = Interpolation.Linear() will introduce a Cameron-Hassall 2022 PNAS- Style basisfunction, that scales with the :duration column

basisfunction = firbasis(τ = (-1, 2), sfreq = 5, scale_duration = true)
╭──────────────────────────────────────────────────────────────────────────────╮23×16 SparseArrays.SparseMatrixCSC with 128 stored entries: │ ::BasisFunction │⎡⣷⣄⠀⠀⠀⠀⠀⠀⎤ │ name:  │⎢⣿⣿⣷⣄⠀⠀⠀⠀⎥ │ kerneltype: FIRBasis │⎢⠈⠻⣿⣿⣷⣄⠀⠀⎥ │ width: 16 │⎢⠀⠀⠈⠻⣿⣿⣷⣄⎥ │ height: 16 │⎢⠀⠀⠀⠀⠈⠻⣿⣿⎥ │ colnames: [-1.0, -0.8 ... 2.0] │⎣⠀⠀⠀⠀⠀⠀⠈⠻⎦ │ times: [-1.0, -0.8 ... 2.0] │ │ collabel: time │ │ shift_onset: -5 │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯

Two examples with duration = 10

Unfold.kernel(basisfunction, [0, 10])
25×16 SparseArrays.SparseMatrixCSC{Int64, Int64} with 160 stored entries:
⎡⣷⣄⠀⠀⠀⠀⠀⠀⎤
⎢⣿⣿⣷⣄⠀⠀⠀⠀⎥
⎢⠻⣿⣿⣿⣷⣄⠀⠀⎥
⎢⠀⠈⠻⣿⣿⣿⣷⣄⎥
⎢⠀⠀⠀⠈⠻⣿⣿⣿⎥
⎢⠀⠀⠀⠀⠀⠈⠻⣿⎥
⎣⠀⠀⠀⠀⠀⠀⠀⠈⎦

and duration = 20

Unfold.kernel(basisfunction, [0, 20])
35×16 SparseArrays.SparseMatrixCSC{Int64, Int64} with 320 stored entries:
⎡⣷⣄⠀⠀⠀⠀⠀⠀⎤
⎢⣿⣿⣷⣄⠀⠀⠀⠀⎥
⎢⣿⣿⣿⣿⣷⣄⠀⠀⎥
⎢⣿⣿⣿⣿⣿⣿⣷⣄⎥
⎢⣿⣿⣿⣿⣿⣿⣿⣿⎥
⎢⠈⠻⣿⣿⣿⣿⣿⣿⎥
⎢⠀⠀⠈⠻⣿⣿⣿⣿⎥
⎢⠀⠀⠀⠀⠈⠻⣿⣿⎥
⎣⠀⠀⠀⠀⠀⠀⠈⠻⎦

let's fit a model

f = @formula 0 ~ 1 + condition
bf_vec = [Any => (f, basisfunction)]
m = fit(UnfoldModel, bf_vec, evts, data; eventfields = [:latency, :duration]);


heatmap(Matrix(modelmatrix(m))')
Example block output

as one can see, now the designmatrix is not stretched - but rather "block"-ed

p = predict(m; overlap = false)[1]
heatmap(p[1, :, :])
Example block output

This page was generated using Literate.jl.