Skip to content

Commit

Permalink
predict tutorial (#219)
Browse files Browse the repository at this point in the history
* up predict.jl

update predict function for partial overlap, so that keep_basis input is converted to vector.

* add assert

* Update src/predict.jl

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update src/predict.jl

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* rename variable

rename variable to be Julia standard compliant

* Update src/predict.jl

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* update assert in predict.jl

* minor predict improvements

* small typo

* Update README.md

* Update README.md

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update src/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

* Update docs/literate/explanations/predict.jl

Co-authored-by: René Skukies <[email protected]>

---------

Co-authored-by: ReneSkukies <[email protected]>
Co-authored-by: René Skukies <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Oct 15, 2024
1 parent 4d57950 commit 65cfcc7
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 9 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ Pkg.add("Unfold")

Please check out [the documentation](https://unfoldtoolbox.github.io/Unfold.jl/dev) for extensive tutorials, explanations and more!

### Tipp on Docs
You can read the docs online: [![Docs][Doc-img]][Doc-url] - or use the `?fit`, `?effects` julia-REPL feature. To filter docs, use e.g. `?fit(::UnfoldModel)`

Here is a quick overview on what to expect.

### What you need
Expand Down
63 changes: 63 additions & 0 deletions docs/literate/explanations/predict.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# # The predict-family
## Setup
using Unfold
using UnfoldSim
using CairoMakie

dat, evts = UnfoldSim.predef_eeg(noiselevel = 5)
design = [
"car" => (@formula(0 ~ 1 + continuous), firbasis= (-0.5, 1), sfreq = 100)),
"face" => (@formula(0 ~ 1 + continuous), firbasis= (-0.3, 0.5), sfreq = 100)),
]

m = fit(UnfoldModel, design, evts, dat; eventcolumn = :condition);

# # Overview
# In a linear model $EEG = Xβ + e$, predictions boil down to finding $\hat{EEG} = Xβ$, thus EEG data without any error term.
# Different types of predictions can be generated by modifying the $X$ accordingly.
#
# !!! note
# We simulated only a single channel, all results generalize to the multi channel case
#
# # Different types of predictions
# ## Time-Continuous case
# Let's start with the cases, where the EEG was not epoched before using Unfold, i.e. the EEG was analysed with e.g. FIR-deconvolution

# ### Continuous EEG
# In the most simple case, we can predict the continuously modelled EEG - This returns $EEG = Xβ$
p = predict(m) # same as predict(m, overlap = true)
lines(p[1, 1:1000])


# ### No-overlap
# We can also predict each epoch without any overlap - This results in one prediction Array per event (in our case we have two events "car" and "face", thus ```size(p[1]) = 2```
p = predict(m, overlap = false)
size(p)
# Each Array has the size (1, samples, epochs):
size(p[1])

# Visualizing the 1000 events
series(range(-0.5, 1, step = 1 / 100), p[1][1, :, :]', solid_color = :orange)
series!(range(-0.3, 0.5, step = 1 / 100), p[2][1, :, :]', solid_color = :teal)
current_figure()

# !!! note
# At ~0.3s we can see a split between the predicted EEG single trials into 10 "strands" - this is the granularity of our continuous predictor. You could use `effects` to improve upon this granularity / customize it.
#
# ### With-overlap, epoched
# Sometimes helpful is to add in the overlap we removed via the deconvolution.
p = predict(m, epoch_to = ["car"], eventcolumn = :condition)
series(range(-0.5, 1, step = 1 / 100), p[1, :, 1:3]', solid_color = :orange)

# ### Partial-overlap
# We can also include/exclude certain events with "partial-overlap", i.e. only overlap with kept events.
p_car = predict(m, keep_basis = ["car"], eventcolumn = :condition)
p_face = predict(m, exclude_basis = ["car"], eventcolumn = :condition) # same as keep_basis=["face"]
f = lines(p_car[1, 1:1000])
lines!(p_face[1, 1:1000])
f

# In the plot, we see the two partial predictions for car and face. They are respectively "0" outside the basisfunction windows

# !!! note
# The above options can be combined as well, e.g. to get an `epoch_to`, `exclude_basis` version. `epoch_timewindow` can be specified as well.
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ makedocs(
"About basisfunctions" => "./explanations/basisfunctions.md",
"Non-Linear effects" => "./generated/explanations/nonlinear_effects.md",
"Window Length Effect" => "./generated/explanations/window_length.md",
"Predictions" => "./generated/explanations/predict.md",
],
"Reference" => [
"Overview of package extensions" => "references/extensions.md",
Expand Down
20 changes: 11 additions & 9 deletions src/predict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ predict(uf::UnfoldModel, evts::DataFrame; overlap = false, kwargs...) = predict(

"""
function predict(
uf,
uf::UnfoldModel,
f::Vector{<:FormulaTerm},
evts::Vector{<:DataFrame};
overlap = true,
overlap::Bool = true,
kwargs...
)
Returns a predicted ("y_hat = X*b") `Array`.
Expand All @@ -173,21 +173,23 @@ Returns a predicted ("y_hat = X*b") `Array`.
## kwargs:
if `overlap = true` (default), overlap based on the `latency` column of 'evts` will be simulated, or in the case of `!ContinuousTimeTrait` just X*coef is returned.
if `overlap = true` (default), overlap based on the `latency` column of `evts` will be simulated, or in the case of `!ContinuousTimeTrait` just X*coef is returned.
if `overlap = false`, returns predictions without overlap (models with `ContinuousTimeTrait` (=> with basisfunction / deconvolution) only), via `predict_no_overlap`
if `keep_basis` or `exclude_basis` is defined, then `predict_partial_overlap` is called, which allows to selective introduce overlap based on specified (or excluded respective) events/basisfunctions
`epoch_to` and `epoch_timewindow` currently only defined for partial_overlap, calculated (partial) overlap controlled predictions, but returns them at the specified `epoch_at` event, with the times `epoch_timewindow` in samples.
`eventcolumn` can be specified as well if different from the default `event`
`epoch_to` and `epoch_timewindow`: calculate (partial) overlap controlled predictions, but returns them at the specified `epoch_at` event, with the times `epoch_timewindow` (default is taken from the basisfunction) in samples.
`eventcolumn` can be specified as well if different from the default `event`.
Hint: all vectors can be "single" types, and will be containered in a vector
Hint: all `kwargs` can be `Vector`, or if e.g. `string` types are provided, will be put into a `length==1` vector.
## Output
- If `overlap=false`, returns a 3D-Array
- If `overlap=true` and `epoch_to=nothing` (default), returns a 2D-array
- If `overlap=true` and `epoch_to!=nothing`, returns a 3D array
- If `overlap=true` and `epoch_to = nothing` (default), returns a 2D-array
- If `overlap=true` and `epoch_to != nothing`, returns a 3D array
"""
function predict(
uf,
Expand All @@ -211,7 +213,7 @@ function predict(
coefs = coef(uf)

if overlap
if isempty(keep_basis) & isempty(exclude_basis)
if isnothing(epoch_to) && (isempty(keep_basis) & isempty(exclude_basis))
@debug "full-overlap"
if events(uf) == evts
@debug "original design predict"
Expand Down

0 comments on commit 65cfcc7

Please sign in to comment.