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

sequence #83

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 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
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
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
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
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.3.2"

[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
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
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
18 changes: 14 additions & 4 deletions docs/literate/HowTo/newComponent.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
# # 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.

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
using UnfoldSim
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 +28,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 Down Expand Up @@ -51,7 +61,7 @@ function basis_shiftduration(evts, maxlength)
end
end


# ## Simulate data with the new component type
erp = UnfoldSim.simulate(
MersenneTwister(1),
TimeVaryingComponent(basis_shiftduration, 50),
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)

#design = UnfoldSim.AddSaccadeAmplitudeDesign4(design,:rt,Normal(0,1),MersenneTwister(1))
#generate_events(design)


# This results in 16 trials that nicely follow our sequence

# 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
# As visible, the `R` response always follows the `S` response. Due to the "`_`" we have large breaks between the individual sequences.
# 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
107 changes: 107 additions & 0 deletions docs/literate/reference/designtypes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# # Overview: Experimental design types

# The experimental design specifies the experimental conditions and other variables that are supposed to have an influence on the simulated data.
# Currently, there are three types of designs implemented: `SingleSubjectDesign`, `MultiSubjectDesign` and `RepeatDesign`.

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

# ## Single-subject designs
# As the name suggests, the `SingleSubjectDesign` type can be used to specify the experimental design for a single subject. Using the `conditions` arguments,
# the user can specify all relevant conditions or predictors and their levels or value range.

# The current implementation assumes a full factorial design (also called fully crossed design)
# in which each level of a factor occurs with each level of the other factors. Moreover, in the current implementation, there is exactly one instance of each of these factor combinations.

# Example:
design_single = SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 3),
),
);

# In order to inspect the design, we can use the `generate_events` function to create an event table based on the design we specified.
generate_events(design_single)

# To change the order of the trials e.g. to sort or shuffle them, one can use the `event_order_function` argument.
# Example: Randomize the order of trials
design_single_shuffled = SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 3),
),
event_order_function = x -> shuffle(MersenneTwister(42), x),
);
# ```@raw html
# <details>
# <summary>Click to expand event table </summary>
# ```
generate_events(design_single_shuffled)
# ```@raw html
# </details >
# ```

# ## Multi-subject designs
# The `MultiSubjectDesign` type can be used to simulate data for an experiment with multiple subjects. Internally, it uses the [MixedModelsSim.jl package](https://github.com/RePsychLing/MixedModelsSim.jl).
# One needs to specify the number of subjects `n_subjects` and the number of items `n_items` i.e. stimuli.
# In addition, one needs to decide for every experimental factor whether it should be between- or within-subject (and item).

# !!! note
# For factors that are not listed in `items_between` it is assumed that they vary within-item (accordingly for `subjects_between`).

design_multi = MultiSubjectDesign(
n_subjects = 6,
n_items = 4,
items_between = Dict(:colour => ["red", "blue"]),
subjects_between = Dict(:age_group => ["young", "old"]),
both_within = Dict(:luminance => range(0, 1, length = 3)),
);

# ```@raw html
# <details>
# <summary>Click to expand event table </summary>
# ```
generate_events(design_multi)
# ```@raw html
# </details >
# <br />
# ```

# As with the `SingleSubjectDesign` one can use the `event_order_function` argument to determine the order of events/trials.

# !!! important
# The number of subjects/items has to be a divisor of the number of factor level combinations, i.e. it is assumed that the design is balanced
# which means that there is an equal number of observations for all possible factor level combinations.

# ## Repeat designs
# The `RepeatDesign` type is a functionality to encapsulate single- or multi-subject designs. It allows to repeat a generated event table multiple times.
# In other words, the `RepeatDesign` type allows to have multiple instances of the same item/subject/factor level combination.

# Example:
# Assume, we have the following single-subject design from above:
# ```@raw html
# <details>
# <summary>Click to expand event table </summary>
# ```
generate_events(design_single)
# ```@raw html
# </details >
# <br />
# ```


# But instead of having only one instance of the factor combinations e.g. `stimulus_type`: `natural` and `contrast_level`: `0`, we will repeat the design three times such that there are three occurrences of each combination.
design_repeated = RepeatDesign(design_single, 3);
generate_events(design_repeated)

# [Here](@ref howto_repeat_design) one can find another example of how to repeat design entries for multi-subject designs.
Loading
Loading