Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

predef eeg multichannel #123

Open
wants to merge 64 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8405a1a
FormulaOnsets
behinger Feb 23, 2024
afc69d7
initial sequence tryout
behinger Feb 25, 2024
49db0ba
fix bug in predef_eeg
behinger Feb 25, 2024
41d7306
fix \beta missing
behinger Feb 26, 2024
8e6de75
merge
behinger Feb 28, 2024
cd489c4
forgot the end
behinger Feb 28, 2024
6c35f3c
half way through to success for sequence designs or something
behinger Feb 28, 2024
809dc2e
everythinig except sequencelength seems to be working now
behinger Feb 28, 2024
31c69e6
added string sequence tests
behinger Feb 28, 2024
54e9334
small doc update
behinger Mar 1, 2024
7114cd6
added jitter to the '_' trial divisor
behinger Mar 9, 2024
b4a8ac8
generalized LinearModelComponent to arbitrary functions instead of ve…
behinger Mar 10, 2024
0c3a11d
bugfix with endless loop due to multiple dispatch
behinger Mar 10, 2024
de8c2a8
function component for multi-subject
behinger Mar 10, 2024
57a7f68
forgot to define offset in LinearModelFunction
behinger Mar 11, 2024
85f037b
Improve documentation especially quickstart, home and power analysis
jschepers Jul 10, 2024
2c65a7f
adapted the order of reference overviews and adapted titles
jschepers Jul 18, 2024
ee9e00a
Updated quickstart page
jschepers Jul 22, 2024
ac0d3bd
minor changes
jschepers Jul 22, 2024
373e535
fixed docstrings for predef_eeg and predef_2x2
jschepers Jul 23, 2024
b073526
added draft of design types reference page
jschepers Jul 23, 2024
89c09a1
Update quickstart.jl
behinger Jul 24, 2024
1e2694f
Add UnfoldSim logo to the documentation
jschepers Jul 24, 2024
9ab04aa
Finished experimental design reference page
jschepers Jul 24, 2024
5671e2e
Replace logo file
jschepers Jul 25, 2024
10c77a3
Update logo file
jschepers Jul 25, 2024
ea25ddc
Delete docs/src/assets/logo.svg
jschepers Jul 25, 2024
e0c2310
Add logo as png file
jschepers Jul 25, 2024
34ce887
Added intro paragraph for Simulate ERP tutorial
jschepers Jul 30, 2024
b5a60bf
Improved docstrings for single- and multi-subject design
jschepers Jul 30, 2024
b81b605
Fixed simulate docstring
jschepers Jul 30, 2024
369faff
Added cross references in docstrings
jschepers Jul 31, 2024
541cf67
Added intro sentences, matched titles and sidebar, reordered pages an…
jschepers Jul 31, 2024
f449083
Update noise.jl
behinger Jul 26, 2024
fe00ec2
Update src/noise.jl
behinger Jul 26, 2024
f93a3c1
Update src/noise.jl
behinger Jul 26, 2024
aca3fd2
Update src/noise.jl
jschepers Jul 31, 2024
e868a14
add empty line for formatting reasons
jschepers Jul 31, 2024
43dd57c
Update docs/literate/reference/noisetypes.jl
jschepers Aug 1, 2024
68ce722
Update docs/literate/reference/overview.jl
jschepers Aug 1, 2024
2dfd44b
Update docs/literate/reference/overview.jl
jschepers Aug 1, 2024
2c34e5e
Update docs/literate/reference/noisetypes.jl
jschepers Aug 1, 2024
8848209
Merge branch 'main' into sequence
behinger Aug 7, 2024
9749685
merge joss doc changes
behinger Aug 7, 2024
3a3bd3d
added docstring
behinger Aug 9, 2024
43e28e8
merge
behinger Aug 9, 2024
f5300c3
Merge branch 'main' into onsetFormulas
behinger Aug 9, 2024
b8d85b9
renamed to have the formula at the end
behinger Aug 9, 2024
b204234
merge sequence & componentfunction
behinger Aug 9, 2024
04a1ed3
Merge branch 'onsetFormulas' into v4.0
behinger Aug 9, 2024
e05a9c6
merge fix, double definition of function
behinger Aug 9, 2024
8aa7591
component function test + docstring
behinger Aug 9, 2024
395b5ee
better docs
behinger Aug 9, 2024
da51a16
added unittests
behinger Aug 9, 2024
9603df9
fixed small wording
behinger Sep 3, 2024
612786a
fix tutorial with v0.3 renaming of simulate to simulate_component
behinger Sep 3, 2024
272eb02
fix newComponent tutorial
behinger Sep 3, 2024
59f5364
initial version of TRF simulation
behinger Sep 20, 2024
5726ec2
merge fini
behinger Sep 20, 2024
4f93a54
improved tutorial
behinger Sep 20, 2024
26ad7fb
fix doilink
behinger Sep 20, 2024
8ea515b
maybe fix the interonset problem?
behinger Sep 20, 2024
73425fa
Apply suggestions from code review
behinger Nov 4, 2024
8ecf7c8
added multichannel predef
behinger Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name = "UnfoldSim"
uuid = "ed8ae6d2-84d3-44c6-ab46-0baf21700804"
authors = ["Benedikt Ehinger", "Luis Lips", "Judith Schepers", "Maanik Marathe"]
version = "0.3.2"
version = "0.4.0"

[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Automa = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Expand Down Expand Up @@ -34,7 +35,7 @@ MixedModels = "4"
MixedModelsSim = "0.2"
Parameters = "0.12"
Random = "1"
SignalAnalysis = "0.4, 0.5,0.6"
SignalAnalysis = "0.4, 0.5,0.6,0.7,0.8"
Statistics = "1"
StatsModels = "0.6,0.7"
ToeplitzMatrices = "0.7, 0.8"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ data, events = simulate(
PinkNoise(),
);
```
All components (design, components, onsets, noise) can be easily modified and you can simply plugin your own!
All simulation ingredients (design, components, onsets, noise) can be easily modified and you can simply plugin your own!

## Contributions
Contributions of any kind are very welcome. Please have a look at [CONTRIBUTING.md](https://github.com/unfoldtoolbox/UnfoldSim.jl/blob/main/CONTRIBUTING.md) for guidance on contributing to UnfoldSim.jl.
Expand Down
59 changes: 59 additions & 0 deletions docs/literate/HowTo/componentfunction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# # Component Functions
# HowTo put arbitrary functions into components

using UnfoldSim
using Unfold
using Random
using DSP
using CairoMakie, UnfoldMakie

sfreq = 100;

# ## Design
# Let's generate a design with a categorical effect and a continuous duration effect
design = UnfoldSim.SingleSubjectDesign(;
conditions = Dict(
:category => ["dog", "cat"],
:duration => Int.(round.(20 .+ rand(100) .* sfreq)),
),
);


# Instead of defining a boring vector basis function e.g. `[0,0,1,2,3,3,2,1,0,0,0]`, let's use function, modulating a hanning windows by the experimental design's duration.
# !!! important
# because any function depending on `design` can be used, two things have to be taken care of:
#
# 1. in case a random component exist in the function, specify a `<:AbstractRNG` within the function call , the basis might be evaluated multiple times inside `simulate`
# 2. a `maxlength` has to be specified via a tuple `(function,maxlength)``
mybasisfun = design -> hanning.(generate_events(design).duration)
signal = LinearModelComponent(;
basis = (mybasisfun, 100),
formula = @formula(0 ~ 1 + category),
β = [1, 0.5],
);

erp = UnfoldSim.simulate_component(MersenneTwister(1), signal, design);


# Finally, let's plot it, sorted by duration

f = Figure()
df = UnfoldMakie.eeg_array_to_dataframe(erp')
df.duration = repeat(generate_events(design).duration, inner = size(erp, 1))
plot_erp!(
f[1, 1],
df,
mapping = (; group = :duration, color = :duration),
categorical_color = false,
categorical_group = true,
layout = (; legend_position = :left),
)
plot_erpimage!(
f[2, 1],
erp,
sortvalues = generate_events(design).duration,
layout = (; legend_position = :bottom),
)
f

# The scaling by the two `condition`` effect levels and the modified event duration by the `duration` are clearly visible
18 changes: 15 additions & 3 deletions docs/literate/HowTo/multichannel.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
# # Generate multi channel data

# Here you will learn how to simulate EEG data for multiple channels/electrodes.
# The idea is to specify a signal on source level and then use a head model or a manual projection matrix to project the source signal to a number of electrodes.

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
## Load required packages
using UnfoldSim
using UnfoldMakie
using CairoMakie
using DataFrames
using Random

# ```@raw html
# </details >
# ```

# ## Specifying a design

Expand All @@ -17,7 +29,7 @@ c2 = LinearModelComponent(; basis = p300(), formula = @formula(0 ~ 1), β = [1])


# ## The multichannel component
# next similar to the nested design above, we can nest the component in a `MultichannelComponent`. We could either provide the projection marix manually, e.g.:
# Next, similar to the nested design above, we can nest the component in a `MultichannelComponent`. We could either provide the projection matrix manually, e.g.:
mc = UnfoldSim.MultichannelComponent(c, [1, 2, -1, 3, 5, 2.3, 1])

# or maybe more convenient: use the pair-syntax: Headmodel=>Label which makes use of a headmodel (HaRTmuT is currently easily available in UnfoldSim)
Expand All @@ -26,7 +38,7 @@ mc = UnfoldSim.MultichannelComponent(c, hart => "Left Postcentral Gyrus")
mc2 = UnfoldSim.MultichannelComponent(c2, hart => "Right Occipital Pole")

# !!! hint
# You could also specify a noise-specific component which is applied prior to projection & summing with other components
# You could also specify a noise-specific component which is applied prior to projection & summing with other components.
#
# finally we need to define the onsets of the signal
onset = UniformOnset(; width = 20, offset = 4);
Expand Down
26 changes: 20 additions & 6 deletions docs/literate/HowTo/newComponent.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# # New component: Duration + Shift
# # Define a new component (with variable duration and shift)

# We want a new component that changes its duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases
# We want a new component that changes its duration and shift depending on a column in the event design. This is somewhat already implemented in the HRF + Pupil bases.
# !!! hint
# if you are just interested to use duration-dependency in your simulation, check out the component-function tutorial


# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
using UnfoldSim
import UnfoldSim.simulate_component
using Unfold
using Random
using DSP
using CairoMakie, UnfoldMakie

sfreq = 100;
# ```@raw html
# </details >
# ```

# ## Design
# Let's generate a design with two columns, shift + duration
Expand All @@ -19,7 +32,8 @@ design = UnfoldSim.SingleSubjectDesign(;
)


# We also need a new AbstractComponent
# ## Implement a new AbstractComponent
# We also need a new `AbstractComponent`
struct TimeVaryingComponent <: AbstractComponent
basisfunction::Any
maxlength::Any
Expand All @@ -29,7 +43,7 @@ end
Base.length(c::TimeVaryingComponent) = length(c.maxlength)

# While we could have put the TimeVaryingComponent.basisfunction directly into the simulate function, I thought this is a bit more modular
function UnfoldSim.simulate(rng, c::TimeVaryingComponent, design::AbstractDesign)
function UnfoldSim.simulate_component(rng, c::TimeVaryingComponent, design::AbstractDesign)
evts = generate_events(design)
return c.basisfunction(evts, c.maxlength)
end
Expand All @@ -51,8 +65,8 @@ function basis_shiftduration(evts, maxlength)
end
end


erp = UnfoldSim.simulate(
# ## Simulate data with the new component type
erp = UnfoldSim.simulate_component(
MersenneTwister(1),
TimeVaryingComponent(basis_shiftduration, 50),
design,
Expand Down
23 changes: 17 additions & 6 deletions docs/literate/HowTo/newDesign.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
# # Define a new (imbalanced) design

# A design specifies how much data is generated, and how the event-table(s)
# should be generated. Already implemented examples are `MultiSubjectDesign` and `SingleSubjectDesign`.

# We need 3 things for a new design: a `struct<:AbstractDesign`, a `size` and a `generate` function.

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
using UnfoldSim
using StableRNGs
using DataFrames
using Parameters
# ## Define a new Design
# A design specifies how much data is generated, and how the event-table(s)
# should be generated. Already implemented examples are `MultiSubjectDesign` and `SingleSubjectDesign`
#
# We need 3 things for a new design: a `struct<:AbstractDesign`, a `size` and a `generate` function
#
# ```@raw html
# </details>
# <br />
# ```

# #### 1) `type`
# We need a `ImbalanceSubjectDesign` struct. You are free to implement it as you wish, as long as the other two functions are implemented
#
Expand Down
10 changes: 10 additions & 0 deletions docs/literate/HowTo/predefinedData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@

# Let's say you want to use the events data frame (containing the levels of the experimental variables and the event onsets (latencies)) from a previous study in your simulation.

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
## Load required packages
using UnfoldSim
using DataFrames
using Random
using CairoMakie # for plotting
# ```@raw html
# </details >
# <br />
# ```

# From a previous study, we (somehow, e.g. by using [pyMNE.jl](https://unfoldtoolbox.github.io/Unfold.jl/dev/HowTo/pymne/)) imported an event data frame like this:
my_events = DataFrame(:condition => [:A, :B, :B, :A, :A], :latency => [7, 13, 22, 35, 41])
Expand Down
6 changes: 2 additions & 4 deletions docs/literate/HowTo/repeatTrials.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using UnfoldSim


# ## Repeating Design entries
# # [Get multiple trials with identical subject/item combinations](@id howto_repeat_design)
# Sometimes we want to repeat a design, that is, have multiple trials with identical values, but it is not always straight forward to implement.
# For instance, there is no way to easily modify `MultiSubjectDesign` to have multiple identical subject/item combinations,
# without doing awkward repetitions of condition-levels or something.

# If you struggle with this problem `RepeatDesign` is an easy tool for you:
using UnfoldSim

designOnce = MultiSubjectDesign(;
n_items = 2,
Expand Down
77 changes: 77 additions & 0 deletions docs/literate/HowTo/sequence.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Base: add_sum
using UnfoldSim
using CairoMakie
using StableRNGs

# ## Stimulus - Response design

# Let's say we want to simulate a stimulus response, followed by a button press response.
#
# First we generate the minimal design of the experiment by specifying our conditins (a one-condition-two-levels design in our case)
design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))
generate_events(design)
# Next we use the `SequenceDesign` and nest our initial design in it. "`SR_`" is code for an "`S`" event and an "`R`" event - only single letter events are supported! The "`_`" is a signal for the Onset generator to generate a bigger pause - no overlap between adjacend "`SR`" pairs
design = SequenceDesign(design, "SR_", StableRNG(1))
generate_events(design)
# The main thing that happened is that the design was repeated for every event (each 'letter') of the sequence, and an `eventtype` column was added.
# !!! hint
# more advaned sequences are possible as well, like "SR{1,3}", or "A[BC]". Infinite sequences are **not** possible like "AB*".

# Finally, let's repeat the current design 4 times
design = RepeatDesign(design, 4)
generate_events(design)

# This results in 16 trials that nicely follow our sequence

# !!! hint
# There is a difference between `SequenceDesign(RepeatDesign)` and `RepeatDesign(SequenceDesign)` for variable sequences e.g. "A[BC]", where in the former case, one sequence is drawn e.g. "AC" and applied to all repeated rows, in the latter, one sequence for each repeat is drawn.


# Next we have to specify for both events `S` and `R` what the responses should look like.
p1 = LinearModelComponent(;
basis = p100(),
formula = @formula(0 ~ 1 + condition),
β = [1, 0.5],
)

n1 = LinearModelComponent(;
basis = n170(),
formula = @formula(0 ~ 1 + condition),
β = [1, 0.5],
)

p3 = LinearModelComponent(;
basis = UnfoldSim.hanning(Int(0.5 * 100)), # sfreq = 100 for the other bases
formula = @formula(0 ~ 1 + condition),
β = [1, 0],
)

resp = LinearModelComponent(;
basis = UnfoldSim.hanning(Int(0.5 * 100)), # sfreq = 100 for the other bases
formula = @formula(0 ~ 1 + condition),
β = [1, 2],
offset = -10,
)
nothing ## hide


# We combine them into a dictionary with a sequence-`Char` as key and simulate
components = Dict('S' => [p1, n1, p3], 'R' => [resp])

data, evts = simulate(
StableRNG(1),
design,
components,
UniformOnset(offset = 40, width = 10),
NoNoise(),
)
nothing ## hide

# Finally we can plot the results
lines(data)
vlines!(evts.latency[evts.event.=='S'], color = (:darkblue, 0.5))
vlines!(evts.latency[evts.event.=='R'], color = (:darkred, 0.5))
xlims!(0, 500)
current_figure()

# As visible, the `R` response always follows the `S` response. Due to the "`_`" we have large breaks between the individual sequences.
23 changes: 16 additions & 7 deletions docs/literate/reference/basistypes.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
using UnfoldSim
using CairoMakie
using DSP
using StableRNGs

# ## Basistypes
# # Overview: Basis function (component) types
# There are several basis types directly implemented. They can be easily used for the `components`.
#
# !!! note
# You can use any arbitrary shape defined by yourself! We often make use of `hanning(50)` from the DSP.jl package.

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
## Load required packages
using UnfoldSim
using CairoMakie
using DSP
using StableRNGs
# ```@raw html
# </details >
# ```

# ## EEG
# By default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(;sfreq=300)
# By default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(; sfreq=300)
f = Figure()
ax = f[1, 1] = Axis(f)
for b in [p100, n170, p300, n400]
Expand Down
Loading