UnfoldSim.AutoRegressiveNoise — Type
AutoRegressiveNoise <: AbstractNoiseNot implemented.
UnfoldSim.EffectsDesign — Type
EffectsDesign <: AbstractDesignThis design evaluates the nested design at the marginalized effects specified in the effects_dict. That is, it calculates all combination of the variables in the effects_dict while setting all other variables to a "typical" value (i.e. the mean for numerical variables).
Fields
design::AbstractDesign: The design of your (main) simulation.effects_dict::Dict: Effects.jl style dictionary specifying variable effects. See also Unfold.jl marginal effects
Examples
design =
SingleSubjectDesign(;
conditions = Dict(
:condition => ["bike", "face"],
:continuous => range(0, 5, length = 10),
),
) |> x -> RepeatDesign(x, 5);
effects_dict = Dict(:condition => ["bike", "face"])
effects_design = EffectsDesign(design, effects_dict)UnfoldSim.ExponentialNoise — Type
ExponentialNoise <: AbstractNoiseNoise with exponential decay in AR spectrum. Implements the algorithm (3) from Markus Deserno 2002: https://www.cmu.edu/biolphys/deserno/pdf/corrgaussianrandom.pdf "How to generate exponentially correlated Gaussian random numbers"
The noise has std of 1 and mean of 0 (over many samples)
The factor "τ" defines the decay over samples.
Fields
noiselevel = 1(optional): Factor that is used to scale the noise.τ: Exponential factor of AR decay "tau" in samples. Must be > 0. This factor should depend on your sampling rate, therefore no default is provided.
Examples
julia> noise = ExponentialNoise(τ = 10)
julia> using StableRNGs
julia> simulate_noise(StableRNG(1), noise, 5)
5-element Vector{Float64}:
-0.5325200748641231
-0.43992168173929114
-0.07751069370021019
-0.42927675219497446
-1.2458281425035913See also PinkNoise, RedNoise, NoNoise, WhiteNoise.
UnfoldSim.Hartmut — Type
Hartmut <: AbstractHeadmodelType for accessing the HArtMuT model (Harmening et al., 2022), a head model which includes not only brain sources but also muscular and ocular sources.
Note: Use Hartmut() to create an instance of this head model.
Fields
artefacual::Any: Dict with artefactual sources (e.g. muscular and ocular) containing label, leadfield, orientation and position.cortical::Any: Dict with cortical sources containing label, leadfield, orientation and position.electrodes::Any: Dict with electrode labels and their positions in 3D space.
Examples
julia> h = Hartmut()
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
HArtMuT-Headmodel
227 electrodes: (AF3,AF3h...) - hartmut.electrodes
2004 source points: (Left Middle Temporal Gyrus, posterior division,...) - hartmut.cortical
4260 source points: (Muscle_DepressorLabii_left,...) - hartmut.artefactual
In addition to UnfoldSim.jl please cite:
HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
# Access artefactual sources
julia> h.artefactual
Dict{String, Any} with 4 entries:
"label" => ["Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Muscle_DepressorLabii_left", "Mu…
"leadfield" => [-0.00148682 -0.00229078 … -0.307231 -0.321305; 0.0141537 0.0134523 … -0.141177 -0.140583; … ; 0.108747 0.1091 … 0.106614 0.117126; 0.0257134 0.0248186 … 0.00105064 -0.00433068;;; 0.0898798 0.0891799 … 2.02466 1.81…
"orientation" => [0.54244 0.482395 -0.687789; 0.546949 0.507161 -0.666059; … ; 0.193693 -0.979684 0.0519768; 0.327641 -0.944196 -0.0338583]
"pos" => [-29.3728 58.6061 -120.829; -28.4183 59.0185 -121.448; … ; -21.8088 70.5968 -28.7679; -21.1545 70.1282 -31.4184]UnfoldSim.LinearModelComponent — Type
LinearModelComponent <: AbstractComponentA multiple regression component for one subject.
All fields can be named. Works best with SingleSubjectDesign.
Fields
basis::Any: an object, if accessed, provides a 'basis function', e.g.hanning(40)::Vector, this defines the response at a single event. It will be weighted by the model prediction. It is also possible to provide a function that evaluates to anVectorofVectors, with thedesignas input to the function, the outer vector has to havenrows(design), one for each event. The inner vector represents the basis functions which can be of different size (a ragged array). Alternatively, one can also return a Matrix with the second dimension representingnrows(design). In the case of providing a function, one has to specify themaxlengthas well in a tuple. E.g.basis=(myfun,40), which would automatically cut the output ofmyfunto 40 samples. If your design depends onrng, e.g. because ofevent_order_function=shuffleor some specialSequenceDesign, then you can provide a two-arguments function(rng,design)->....formula::Any: StatsModelsformulaobject, e.g.@formula 0 ~ 1 + cond(left-hand side must be 0).β::VectorVector of betas/coefficients, must fit the formula.contrasts::Dict(optional): Determines which coding scheme to use for which categorical variables. Default is empty which corresponds to dummy coding. For more information see https://juliastats.org/StatsModels.jl/stable/contrasts.offset::Int = 0: Can be used to shift the basis function in time (in samples).
Examples
julia> LinearModelComponent(;
basis = hanning(40),
formula = @formula(0 ~ 1 + cond),
β = [1., 2.],
contrasts = Dict(:cond => EffectsCoding())
)
LinearModelComponent
basis: Array{Float64}((40,)) [0.0, 0.006474868681043577, 0.02573177902642726, 0.0572719871733951, 0.10027861829824952, 0.1536378232452003, 0.21596762663442215, 0.28565371929847283, 0.3608912680417737, 0.43973165987233853 … 0.43973165987233853, 0.3608912680417737, 0.28565371929847283, 0.21596762663442215, 0.1536378232452003, 0.10027861829824952, 0.0572719871733951, 0.02573177902642726, 0.006474868681043577, 0.0]
formula: StatsModels.FormulaTerm{StatsModels.ConstantTerm{Int64}, Tuple{StatsModels.ConstantTerm{Int64}, StatsModels.Term}}
β: Array{Float64}((2,)) [1.0, 2.0]
contrasts: Dict{Symbol, EffectsCoding}See also MixedModelComponent, MultichannelComponent.
UnfoldSim.LogNormalOnset — Type
LogNormalOnset <: AbstractOnsetLog-normal inter-event distances (in samples) using the Distributions.jl truncated LogNormal distribution (code and mathematical reference).
Be careful with large μ and σ values, as they are on logscale. σ>8 can quickly give you out-of-memory sized signals!
Tip: To manually generate inter-event distance samples use the simulate_interonset_distances function.
Fields
μ: The mean of the log-transformed variable (the log-normal random variable's logarithm follows a normal distribution).σ: The standard deviation of the log-transformed variable.offset = 0(optional): The minimal distance between events.truncate_upper = nothing(optional): Upper limit (in samples) at which the distribution is truncated.
Examples
julia> onset_distribution = LogNormalOnset(3, 0.25, 10, 25)
LogNormalOnset
μ: Int64 3
σ: Float64 0.25
offset: Int64 10
truncate_upper: Int64 25See also UniformOnset, NoOnset.
UnfoldSim.LogNormalOnsetFormula — Type
LogNormalOnsetFormula <: AbstractOnsetProvide a Log-normal Distribution of the inter-event distances, but with regression formulas for the distribution's parameters offset, μ and σ.
This is helpful if your overlap/event-distribution should be dependent on some condition, e.g. more overlap in cond = 'A' than cond = 'B'.
μ: The mean of the log-transformed variable (the log-normal random variable's logarithm follows a normal distribution). σ: The standard deviation of the log-transformed variable. offset: The minimal distance between events - aka a shift of the LogNormal distribution.
Fields
μ_formula = @formula(0~1)(optional): Choose a formula depending on yourdesignμ_β::Vector: Choose aVectorof betas, number needs to fit the formula chosen.μ_contrasts::Dict = Dict()(optional): Choose a contrasts-Dictionary according to the StatsModels specifications.σ_formula = @formula(0~1)(optional): Choose a formula depending on yourDesign.σ_β::Vector: Choose aVectorof betas, number needs to fit the formula chosen.σ_contrasts::Dict = Dict()(optional) : Choose a contrasts-Dictionary according to the StatsModels specifications.offset_formula = @formula(0~1)(optional): Choose a formula depending on yourdesignfor the offset.offset_β::Vector = [0](optional): Choose aVectorof betas. The number of betas needs to fit the formula chosen.offset_contrasts::Dict = Dict()(optional): Choose a contrasts-Dictionary according to the StatsModels specifications.truncate_upper::nothing(optional): Upper limit (in samples) at which the distribution is truncated (formula for truncation currently not implemented)truncate_lower::nothing(optional): Lower limit (in samples) at which the distribution is truncated (formula for truncation currently not implemented)
Combined with ShiftOnsetByOne
Sometimes one wants to bias not the inter-onset distance prior to the current event, but after the current event. This is possible by using ShiftOnsetByOne(LogNormalOnset(...)), effectively shifting the inter-onset-distance vector by one. See ?ShiftOnsetByOne for a visualization.
Examples
julia> o = LogNormalOnsetFormula(
σ_formula = @formula(0 ~ 1 + cond),
σ_β = [0.25, 0.5],
μ_β = [2],
)See also LogNormalOnset for a simplified version without linear regression specifications.
UnfoldSim.MixedModelComponent — Type
MixedModelComponent <: AbstractComponentA component that adds a hierarchical relation between parameters according to a Linear Mixed Model (LMM) defined via MixedModels.jl.
All fields can be named. Works best with MultiSubjectDesign.
Fields
basis::Any: an object, if accessed, provides a 'basis function', e.g.hanning(40)::Vector, this defines the response at a single event. It will be weighted by the model prediction. It is also possible to provide a function that evaluates to anVectorofVectors, with thedesignas input to the function, the outer vector has to havenrows(design), one for each event. The inner vector represents the basis functions which can be of different size (a ragged array). Alternatively, one can also return a Matrix with the second dimension representingnrows(design). In the case of providing a function, one has to specify themaxlengthas well in a tuple. E.g.basis=(myfun,40), which would automatically cut the output ofmyfunto 40 samples. If your design depends onrng, e.g. because ofevent_order_function=shuffleor some specialSequenceDesign, then you can provide a two-arguments function(rng,design)->....formula::Any: Formula-object in the style of MixedModels.jl e.g.@formula 0 ~ 1 + cond + (1|subject). The left-hand side is ignored.β::VectorVector of betas (fixed effects), must fit the formula.σs::DictDict of random effect variances, e.g.Dict(:subject => [0.5, 0.4])or to specify correlation matrixDict(:subject=>[0.5,0.4,I(2,2)],...). Technically, this will be passed to the MixedModels.jlcreate_refunction, which creates the θ matrices.contrasts::Dict(optional): Dict in the style of MixedModels.jl. Determines which coding scheme to use for which categorical variables. Default is empty which corresponds to dummy coding. For more information see https://juliastats.org/StatsModels.jl/stable/contrasts.
Examples
julia> MixedModelComponent(;
basis = hanning(40),
formula = @formula(0 ~ 1 + cond + (1 + cond|subject)),
β = [1., 2.],
σs= Dict(:subject => [0.5, 0.4]),
contrasts=Dict(:cond => EffectsCoding())
)
MixedModelComponent
basis: Array{Float64}((40,)) [0.0, 0.006474868681043577, 0.02573177902642726, 0.0572719871733951, 0.10027861829824952, 0.1536378232452003, 0.21596762663442215, 0.28565371929847283, 0.3608912680417737, 0.43973165987233853 … 0.43973165987233853, 0.3608912680417737, 0.28565371929847283, 0.21596762663442215, 0.1536378232452003, 0.10027861829824952, 0.0572719871733951, 0.02573177902642726, 0.006474868681043577, 0.0]
formula: StatsModels.FormulaTerm{StatsModels.ConstantTerm{Int64}, Tuple{StatsModels.ConstantTerm{Int64}, StatsModels.Term, StatsModels.FunctionTerm{typeof(|), Vector{StatsModels.AbstractTerm}}}}
β: Array{Float64}((2,)) [1.0, 2.0]
σs: Dict{Symbol, Vector{Float64}}
contrasts: Dict{Symbol, EffectsCoding}See also LinearModelComponent, MultichannelComponent.
UnfoldSim.MultiSubjectDesign — Type
MultiSubjectDesign <: AbstractDesignA type for specifying the experimental design for multiple subjects (based on the given random-effects structure).
Tip: Check the resulting dataframe using the generate_events function.
Please note that the number of items n_items has to be a multiple of the number of between-item levels. The sample applies for n_subjects and the number of between-subject levels.
Fields
n_subjects::Int: Number of subjects.n_items::Int: Number of items/stimuli (sometimes ≈ trials).subjects_between::Dict{Symbol,Vector}: Effects between subjects, e.g. young vs old.items_between::Dict{Symbol,Vector}: Effects between items, e.g. natural vs artificial images, (but shown to all subjects if not specified also insubjects_between).both_within::Dict{Symbol,Vector}: Effects completely crossed i.e. conditions/covariates that are both within-subject and within-item.event_order_function = (rng, x) -> x: Can be used to sort, or shuffle the events e.g.(rng, x) -> shuffle(rng, x)(or shorter justevent_order_function = shuffle). The default is the identify function, i.e. not changing the order of the events.
Examples
# Declaring the same condition both between-subject and between-item results in a full between-subject/item design.
julia> design = MultiSubjectDesign(;
n_items = 10,
n_subjects = 30,
subjects_between = Dict(:cond => ["levelA", "levelB"]),
items_between = Dict(:cond => ["levelA", "levelB"]),
)
MultiSubjectDesign
n_subjects: Int64 30
n_items: Int64 10
subjects_between: Dict{Symbol, Vector}
items_between: Dict{Symbol, Vector}
both_within: Dict{Symbol, Vector}
event_order_function: #3 (function of type UnfoldSim.var"#3#7")
julia> generate_events(StableRNG(1), design)
150×3 DataFrame
Row │ subject cond item
│ String String String
─────┼─────────────────────────
1 │ S01 levelA I01
2 │ S01 levelA I03
3 │ S01 levelA I05
⋮ │ ⋮ ⋮ ⋮
148 │ S30 levelB I06
149 │ S30 levelB I08
150 │ S30 levelB I10
144 rows omittedSee also SingleSubjectDesign, RepeatDesign
UnfoldSim.MultichannelComponent — Type
MultichannelComponent <: AbstractComponentProjects a AbstractComponent to multiple "channels" via the projection vector.
Optionally, noise can be added to the source prior to projection. By default a MultichannelComponent can be constructed using one of the following options for projection:
projection::AbstractVector: Directly pass a custom projection vector.projection::Pair{<:AbstractHeadmodel,String}: Generate a projection vector by specifying which headmodel to use and which sources should be active.
Fields
component::AbstractComponent: The component that should be projected to the sensors.projection::AbstractVectororprojection::Pair{<:AbstractHeadmodel,String}: Vectorpthat projects the (source) componentc[t](wheretis time) to the sensorss. The length ofpequals the number of sensorss. Typically, it is a slice of the leadfield matrix.out[s,t] = p[s]*c[t].noise::AbstractNoise(optional): Noise added in the source space. Default isNoNoise.
Examples
# Variant 1: Specify the projection vector manually
julia> c1 = LinearModelComponent(; basis = p100(), formula = @formula(0 ~ 1), β = [1]);
julia> mc1 = UnfoldSim.MultichannelComponent(c, [1, 2, -1, 3, 5, 2.3, 1])
MultichannelComponent
component: LinearModelComponent
projection: Array{Float64}((7,)) [1.0, 2.0, -1.0, 3.0, 5.0, 2.3, 1.0]
noise: NoNoise NoNoise()
# Variant 2: Use a headmodel and specify a source
julia> c2 = LinearModelComponent(; basis = p300(), formula = @formula(0 ~ 1), β = [1]);
julia> hart = headmodel(type = "hartmut");
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
julia> mc2 = UnfoldSim.MultichannelComponent(c2, hart => "Right Occipital Pole")
MultichannelComponent
component: LinearModelComponent
projection: Array{Float64}((227,)) [-0.03461859471337842, -0.04321094803502425, 0.0037088347968313525, -0.014722528968861278, -0.0234889834534478, 0.02731807504242923, 0.038863688452528036, 0.1190531258070562, -0.09956890221613562, -0.0867729334438599 … 0.37435404409695094, -0.020863789022627935, 0.25627478723535513, -0.05777985212119245, 0.37104376432271147, -0.19446620423767172, 0.2590764703721097, -0.12923837607416555, 0.1732886690359311, 0.4703016561960567]
noise: NoNoise NoNoise()See also LinearModelComponent, MixedModelComponent.
UnfoldSim.NoNoise — Type
NoNoise <: AbstractNoiseA type for simulations without noise; return zeros instead of noise.
Tip: To manually create noise samples use the simulate_noise function.
Examples
julia> noise = NoNoise()
NoNoise()
julia> using StableRNGs
julia> simulate_noise(StableRNG(1), noise, 3)
3-element Vector{Float64}:
0.0
0.0
0.0See also PinkNoise, RedNoise, ExponentialNoise, WhiteNoise.
UnfoldSim.NoOnset — Type
NoOnset <: AbstractOnsetFor cases where the user wants to simulate epoched data without any overlap between consecutive events.
Examples
julia> onset_distribution = NoOnset()
NoOnset()See also UniformOnset, LogNormalOnset.
UnfoldSim.PinkNoise — Type
PinkNoise <: AbstractNoiseA type for generating Pink Noise using the SignalAnalysis.jl implementation.
The noise values are sampled from a standard normal distribution 𝒩(μ=0, σ=1). That means that ~95% of the values are between ~-2 and ~2 (with noiselevel = 1).
Tip: To manually create noise samples use the simulate_noise function.
Fields
noiselevel = 1(optional): Factor that is used to scale the noise.func = SignalAnalysis.PinkGaussian(optional): Function that is used to create the noise samples. This field is for internal use and should not typically be modified directly by the user. Changes to this field may result in unexpected behavior.
Examples
julia> noise = PinkNoise(noiselevel = 3)
PinkNoise
noiselevel: Int64 3
func: UnionAll
julia> using StableRNGs
julia> simulate_noise(StableRNG(1), noise, 5)
5-element Vector{Float64}:
2.578878369756878
3.4972108606501786
2.878568584946028
2.2725654770788384
3.5291669151888683See also RedNoise, WhiteNoise, ExponentialNoise, NoNoise.
UnfoldSim.RealisticNoise — Type
RealisticNoise <: AbstractNoiseNot implemented - planned to use Artifacts.jl to provide real EEG data to add.
UnfoldSim.RedNoise — Type
RedNoise <: AbstractNoiseA type for generating Red Noise using the SignalAnalysis.jl implementation.
The noise values are sampled from a standard normal distribution 𝒩(μ=0, σ=1). That means that ~95% of the values are between ~-2 and ~2 (with noiselevel = 1).
Tip: To manually create noise samples use the simulate_noise function.
Fields
noiselevel = 1(optional): Factor that is used to scale the noise.func = SignalAnalysis.RedGaussian(optional): Function that is used to create the noise samples. This field is for internal use and should not typically be modified directly by the user. Changes to this field may result in unexpected behavior.
Examples
julia> noise = RedNoise(noiselevel = 2)
RedNoise
noiselevel: Int64 2
func: UnionAll
julia> using StableRNGs
julia> simulate_noise(StableRNG(2), noise, 3)
3-element Vector{Float64}:
-0.34153942884005967
-0.4651387715669636
-0.4951538876376382See also PinkNoise, WhiteNoise, ExponentialNoise, NoNoise.
UnfoldSim.RepeatDesign — Type
RepeatDesign{T} <: AbstractDesignRepeat a design (and the corresponding events DataFrame) multiple times to mimick repeatedly recorded trials.
Tip: Check the resulting dataframe using the generate_events function. Please note that when using an event_order_function(e.g. shuffle) in a RepeatDesign, the corresponding RNG is shared across repetitions and not deep-copied for each repetition. As a result, the order of events will differ for each repetition.
Fields
design::T: The experimental design that should be repeated.repeat::Int = 1: The number of repetitions.
Examples
julia> using StableRNGs # For using the `generate_events` function in a reproducible way
julia> design_once =
SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 2),
),
event_order_function = shuffle,
);
julia> generate_events(StableRNG(1), design_once)
4×2 DataFrame
Row │ contrast_level stimulus_type
│ Float64 String
─────┼───────────────────────────────
1 │ 1.0 natural
2 │ 1.0 artificial
3 │ 0.0 natural
4 │ 0.0 artificial
julia> design = RepeatDesign(design_once, 2)
RepeatDesign{SingleSubjectDesign}
design: SingleSubjectDesign
repeat: Int64 2
julia> generate_events(StableRNG(1), design)
8×2 DataFrame
Row │ contrast_level stimulus_type
│ Float64 String
─────┼───────────────────────────────
1 │ 1.0 natural
2 │ 1.0 artificial
3 │ 0.0 natural
4 │ 0.0 artificial
5 │ 1.0 artificial
6 │ 0.0 natural
7 │ 1.0 natural
8 │ 0.0 artificialSee also SingleSubjectDesign, MultiSubjectDesign
UnfoldSim.SequenceDesign — Type
SequenceDesign{T} <: AbstractDesignCreate a sequence of events for each entry of a provided AbstractDesign.
The sequence string can contain any number of char, but the _ character is used to indicate a break between events without any overlap and has to be at the end of the sequence string. There can only be one _ character in a sequence string. Important: The exact same variable sequence is used for current rows of a design. Only, if you later nest in a RepeatDesign then each RepeatDesign repetition will gain a new variable sequence. If you need imbalanced designs, please refer to the ImbalancedDesign tutorial.
Fields
design::AbstractDesign: The design that is generated for every sequence event.sequence::String = ""(optional): A string of characters depicting sequences. A variable sequence is defined using[]. For example,S[ABC]could result in any one sequenceSA,SB,SC. Experimental: It is also possible to define variable length sequences using{}. For example,A{10,20}would result in a sequence of 10 to 20As.
Examples
design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))
design = SequenceDesign(design, "SCR_")Would result in a generate_events(design)
6×2 DataFrame
Row │ condition event
│ String Char
─────┼──────────────────
1 │ one S
2 │ one C
3 │ one R
4 │ two S
5 │ two C
6 │ two RCombination of SequenceDesign and RepeatDesign
Sequence -> Repeat
design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))
design = SequenceDesign(design, "[AB]")
design = RepeatDesign(design,2)julia> generate_events(design)
4×2 DataFrame
Row │ condition event
│ String Char
─────┼──────────────────
1 │ one B
2 │ two B
3 │ one A
4 │ two ASequence -> Repeat: If a sequence design is repeated, then for each repetition a sequence is generated and applied. Events have different values.
Repeat -> Sequence
design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))
design = RepeatDesign(design,2)
design = SequenceDesign(design, "[AB]")julia> generate_events(design)
4×2 DataFrame
Row │ condition event
│ String Char
─────┼──────────────────
1 │ one B
2 │ two B
3 │ one B
4 │ two BRepeat -> Sequence: The design is first repeated, then for that design one sequence is generated and applied. All events are the same.
See also SingleSubjectDesign, MultiSubjectDesign, RepeatDesign
UnfoldSim.ShiftOnsetByOne — Type
ShiftOnsetByOne <:AbstractOnsetThis container AbstractOnset shifts the ShiftOnsetByOne.onset::AbstractOnset inter-onset-distance vector by one, adding a 0 to the front and removing the last inter-onset distance.
This is helpful in combination with LogNormalOnsetFormula or UniformOnsetFormula, to generate biased distances not of the previous, but of the next event.
Visualized:
|1| A |2| B |3| C Right now, the inter-onset distances are assigned in the order 1,2,3 inbetween the events A,B,C. After ShiftOnsetByOne we would have
|0| A |1| B |2| C
with 0 being a new distance of 0, and the 3 removed (it would describe the distance after C, because there is nothing coming, the signal is not further prolonged).
Examples
julia> o = UniformOnset(10,20)
julia> d = SingleSubjectDesign(conditions=Dict(:trial=>[1,2,3]))
julia> simulate_interonset_distances(MersenneTwister(1),o,d)'
> 26 30 27
julia> simulate_interonset_distances(MersenneTwister(1),ShiftOnsetByOne(o),d)'
> 0 26 30UnfoldSim.Simulation — Type
Simulation{T<:Numeric}A type to store all "ingredients" for a simulation including their parameters.
Can either be created by the user or will be created automatically when calling the simulate function with the required "ingredients". Tip: Use the subtypes function to get an overview of the implemented "ingredients", e.g. subtypes(AbstractDesign).
The parametric type T (default Float64), defines the element type of the data returned by the simulate function. AbstractComponent needs to produce a compatible data-type, e.g. Simulation{Int} and an AbstractComponent returning Float64 would not work because Float64's cannot be cast into an Vector{Int} the reverse would work though.
Fields
design::AbstractDesign: Experimental design.components::Vector{AbstractComponent}: Response function(s) for the events (e.g. the ERP shape in EEG).onset::AbstractOnset: Inter-onset distance distribution.noisetype::AbstractNoise: Noise type.
Examples
julia> design = SingleSubjectDesign(; conditions = Dict(:stimulus_type => ["natural", "artificial"]));
julia> component = LinearModelComponent(;
basis = p100(),
formula = @formula(0 ~ 1 + stimulus_type),
β = [2, 3],
);
julia> onset = UniformOnset();
julia> noise = PinkNoise();
julia> simulation = Simulation(design, component, onset, noise);
julia> using StableRNGs
julia> data, events = simulate(StableRNG(1), simulation);
julia> events
2×2 DataFrame
Row │ stimulus_type latency
│ String Int64
─────┼────────────────────────
1 │ natural 20
2 │ artificial 70
julia> data
85-element Vector{Float64}:
0.8596261232522926
1.1657369535500595
0.9595228616486761
⋮
0.9925202143746904
0.2390652543395527
-0.11672523788068771UnfoldSim.SingleSubjectDesign — Type
SingleSubjectDesign <: AbstractDesignA type for specifying the experimental design for a single subject (based on the given conditions).
Tip: Check the resulting dataframe using the generate_events function.
The number of trials/rows in the output of generate_events([rng, ]design) depends on the full factorial of your conditions.
To increase the number of repetitions, e.g. by 5, simply use RepeatDesign(SingleSubjectDesign(...), 5).
If conditions are omitted (or set to nothing), a single trial is simulated with a column :dummy and content :dummy - this is for convenience.
Fields
conditions::Dict{Symbol,Vector}: Experimental conditions, e.g.Dict(:A => ["a_small","a_big"], :B => ["b_tiny","b_large"]).event_order_function = (rng, x) -> x: Can be used to sort by specifyingsort, or shuffling by providingshuffle, or custom functions following the interface(rng, x) -> my_shuffle(rng,x). The default is the identify function, i.e. not changing the order of the events.
Examples
julia> using StableRNGs # For using the `generate_events` function in a reproducible way
julia> design =
SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 5),
),
)
SingleSubjectDesign
conditions: Dict{Symbol, Vector}
event_order_function: #10 (function of type UnfoldSim.var"#10#14")
julia> generate_events(StableRNG(1), design)
10×2 DataFrame
Row │ contrast_level stimulus_type
│ Float64 String
─────┼───────────────────────────────
1 │ 0.0 natural
2 │ 0.25 natural
3 │ 0.5 natural
⋮ │ ⋮ ⋮
8 │ 0.5 artificial
9 │ 0.75 artificial
10 │ 1.0 artificial
4 rows omittedSee also MultiSubjectDesign, RepeatDesign
UnfoldSim.SubselectDesign — Type
Internal helper design to subset a sequence design in its individual components
UnfoldSim.UniformOnset — Type
UniformOnset <: AbstractOnsetProvide a Uniform Distribution for the inter-event distances (in samples).
Tip: To manually generate inter-event distance samples use the simulate_interonset_distances function.
Fields
width = 50(optional): Width of the uniform distribution (=> the "jitter"). Since the lower bound is 0,widthis also the upper bound.offset = 0(optional): The minimal distance between events. The maximal distance isoffset + width.
Examples
julia> onset_distribution = UniformOnset(width = 25, offset = 5)
UniformOnset
width: Int64 25
offset: Int64 5See also LogNormalOnset, NoOnset.
UnfoldSim.UniformOnsetFormula — Type
UniformOnsetFormula <: AbstractOnsetProvide a Uniform Distribution for the inter-event distances, but with regression formulas for the distribution's parameters offset and width.
This is helpful if your overlap/event-distribution should be dependent on some condition, e.g. more overlap in cond = 'A' than cond = 'B'. Offset affects the minimal distance. The maximal distance is offset + width.
Fields
offset_formula = @formula(0~1): Choose a formula depending on yourdesign.offset_β::Vector = [0](optional): Choose aVectorof betas. The number of betas needs to fit the formula chosen.offset_contrasts::Dict = Dict()(optional): Choose a contrasts-Dictionary according to the StatsModels specifications.width_formula =@formula(0~1): Choose a formula depending on yourDesign`.width_β::Vector: Choose aVectorof betas, number needs to fit the formula chosen.width_contrasts::Dict = Dict()(optional) : Choose a contrasts-Dictionary according to the StatsModels specifications.
Combined with ShiftOnsetByOne
Sometimes one wants to bias not the inter-onset distance prior to the current event, but after the current event. This is possible by using ShiftOnsetByOne(UniformOnset(...)), effectively shifting the inter-onset-distance vector by one. See ?ShiftOnsetByOne for a visualization.
Examples
julia> o = UnfoldSim.UniformOnsetFormula(
width_formula = @formula(0 ~ 1 + cond),
width_β = [50, 20],
)
UniformOnsetFormula
width_formula: StatsModels.FormulaTerm{StatsModels.ConstantTerm{Int64}, Tuple{StatsModels.ConstantTerm{Int64}, StatsModels.Term}}
width_β: Array{Int64}((2,)) [50, 20]
width_contrasts: Dict{Any, Any}
offset_formula: StatsModels.FormulaTerm{StatsModels.ConstantTerm{Int64}, StatsModels.ConstantTerm{Int64}}
offset_β: Array{Int64}((1,)) [0]
offset_contrasts: Dict{Any, Any}See also UniformOnset for a simplified version without linear regression specifications.
UnfoldSim.WhiteNoise — Type
WhiteNoise <: AbstractNoiseA type for generating White Noise using randn - thus Gaussian noise.
The noise values are sampled from a standard normal distribution 𝒩(μ=0, σ=1). That means that ~95% of the values are between ~-2 and ~2 (with noiselevel = 1).
Tip: To manually create noise samples use the simulate_noise function.
Fields
noiselevel = 1(optional): Factor that is used to scale the noise.imfilter = nothing(optional): Useimfilter > 0to smooth the noise usingImage.imfilterwith a Gaussian kernel withσ = imfilter.
Examples
julia> noise = WhiteNoise()
WhiteNoise
noiselevel: Int64 1
imfilter: Nothing nothing
julia> using StableRNGs
julia> simulate_noise(StableRNG(1), noise, 3)
3-element Vector{Float64}:
-0.5325200748641231
0.098465514284785
0.7528865221245234See also PinkNoise, RedNoise, ExponentialNoise, NoNoise.
Base.length — Method
Length is the product of all dimensions and equals the number of events in the corresponding events dataframe.
DSP.Windows.hanning — Method
hanning(duration, offset, sfreq)Generate a (potentially shifted) hanning window with a certain duration.
Note: This function extends the DSP.hanning function using multiple dispatch.
Arguments
width: in s.offset: in s, defines the location of the hanning peak i.e. shift of the hanning window. Must be >round(width/2)(otherwise the left part of the curve would be cut off).sfreq: Sampling rate in Hz.
Returns
Vector: Contains a shifted (i.e. zero-padded) hanning window.
Examples
julia> UnfoldSim.hanning(0.1, 0.3, 100)
35-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
⋮
0.9698463103929542
0.75
0.4131759111665348
0.116977778440511
0.0UnfoldSim.PuRF — Method
PuRF(; n = 10.1, tmax = 0.93, sfreq = 100)Default generator for PuRF Pupil Response Function. The canonical PRF is a gamma function and implemented according to Denison 2020 equation (2) going back to Hoeks & Levelt, 1993.
The pupil response is evaluated at t = 0:1/sfreq:3*tmax. The response is normalized by the peak-maximum at tmax, thus typically a pupil-response of 1 is returned (disregarding numerical issues).
Keyword arguments:
n = 10.1: shape parametertmax = 0.93: peak maximumsfreq = 100: sampling frequency
Returns
Vector: canonical pupil response with length(0:1/sfreq:3*tmax) entries.
Examples
julia> PuRF(; n = 5)
280-element Vector{Float64}:
0.0
2.0216617815131253e-8
6.130689396024061e-7
4.4118063684811444e-6
1.761817666835793e-5
⋮
0.012726253506722554
0.012280989091455786
0.011850525657416842
0.011434405338911133
0.011032182932283816UnfoldSim.add_responses! — Method
add_responses!(signal, responses::Vector, e, s, tvec, trial_idx)
add_responses!(signal, responses::Matrix, e, s, tvec, trial_idx)
add_responses!(signal, responses::AbstractArray, e, s, tvec, trial_idx)Add (in-place) the given responses to the signal, for both 2D (1 channel) and 3D (X channel case). Helper function.
Arguments
signal: Continuous EEG signal to be modified in place. Has the dimensionschannels x continuous_time x subjects.responses::Union{Vector, Matrix, AbstractArray}: Responses to be added. In the multi-channel case, the dimensions arechannels x maxlength(components) x length(simulation.design), elsemaxlength(components) x length(simulation.design). The data for all the subjects and their respective trials is concatenated.e: Index of the channel (insignal) for which to add the response.s: Index of the subject (insignal) for which to add the response.tvec: Time points (indices insignal) at which to add the response.trial_idx: Index of the particular trial (inresponses) from where the response is to be added.
Returns
Nothing:signalis modified in-place.
Examples
julia> signal, responses, tvec = zeros(5,15,2), ones(5,6), 1:5;
julia> UnfoldSim.add_responses!(signal, responses, 1, 2, tvec, 5);
julia> signal
5×15×2 Array{Float64, 3}:
[:, :, 1] =
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
[:, :, 2] =
1.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0UnfoldSim.apply_event_order_function — Method
apply_event_order_function(fun, rng, events)Apply fun(rng, events), raise an error if function fun is wrongly defined. Convenience function to not repeat the error handling at multiple places.
UnfoldSim.closest_src — Method
closest_src(coords_list::AbstractVector{<:AbstractVector}, pos)When applied to a vector of target coordinate vectors, return the index of the closest position in pos for each of the targets.
Returns
Vector: Index of the closest position inposfor each vector incoords_list.
Examples
julia> positions = [0 0 0; 2 2 2; 3 3 3; 4 4 4];
julia> target_coordinate_vectors = [[0.5, 0, 0.5], [10, 1, 8]];
julia> UnfoldSim.closest_src(target_coordinate_vectors, positions)
2-element Vector{Int64}:
1
4UnfoldSim.closest_src — Method
closest_src(head::Hartmut, label::String)Return the index of the Harmut model source point that is closest (using Euclidean distance) to the mean position of the source points matching the given label.
We use the average in Euclidean space, but the cortex is a curved surface. In most cases they will not overlap. Ideally we would calculate the average on the surface, but this is a bit more complex to do (you'd need to calculate the vertices etc.).
Arguments
head::Hartmut: Headmodel of typeHartmut.label::String: Label of the source point(s) of interest.
Returns
Int: Index of the closest Hartmut source point.
Examples
julia> hartmut = Hartmut();
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
julia> label = "Right Cingulate Gyrus, posterior division";
julia> UnfoldSim.closest_src(hartmut, label)
1875UnfoldSim.closest_src — Method
closest_src(coords::Vector{<:Real}, pos)Return the index of the position that is closest to the target coordinates (using the Euclidean distance).
Arguments
coords::Vector{<:Real}: Target coordinate vector (typically with length 3).pos: Matrix that contains all possible positions. Its dimensions are the number of positionsntimes number of entries in the position coordinate vectors (usually 3) i.e.n x 3.
Returns
Int: Row index of the position inposthat is closest tocoords.
Examples
julia> positions = [0 0 0; 2 2 2; 3 3 3; 4 4 4]
4×3 Matrix{Int64}:
0 0 0
2 2 2
3 3 3
4 4 4
julia> target_coordinates = [0.5, 0, 0.5]
3-element Vector{Float64}:
0.5
0.0
0.5
julia> UnfoldSim.closest_src(target_coordinates, positions)
1UnfoldSim.contains_design — Method
Returns true if the design, or any nested design contains the target design type
UnfoldSim.create_continuous_signal — Method
create_continuous_signal(rng, responses, simulation)Simulate onset latencies and add together a continuous signal, based on the given responses and simulation parameters. Helper function.
Arguments
rng: Random number generator, important to ensure reproducibility.responses: Responses to be combined with the given onsets.simulation: Simulation parameters, including design, components, onsets, and noisetype.
Returns
(signal, latencies)::Tuple{Array, Array}:signalcontains the generated signal. Has the dimensionschannels x continuous_time x subjects.latenciescontains the onset latencies.
Examples
julia> using StableRNGs # to get an RNG
julia> design = SingleSubjectDesign(; conditions = Dict(:cond => ["natural", "artificial"]));
julia> c1 = LinearModelComponent(; basis = p100(), formula = @formula(0 ~ 1 + cond), β = [1, 0.5]);
julia> c2 = LinearModelComponent(; basis = p300(), formula = @formula(0 ~ 1), β = [2]);
julia> simulation = Simulation(design, [c1, c2], UniformOnset(; width = 0, offset = 30), PinkNoise());
julia> responses = simulate_responses(StableRNG(1), [c1, c2], simulation)
45×2 Matrix{Float64}:
0.0 0.0
0.0 0.0
⋮
0.0233794 0.0233794
0.0 0.0
julia> signal, latencies = UnfoldSim.create_continuous_signal(StableRNG(1), responses, simulation);
julia> signal
106-element Vector{Float64}:
0.0
0.0
0.0
⋮
0.023379444289913343
0.0
0.0
julia> latencies
2-element Vector{Int64}:
31
61UnfoldSim.epoch — Method
epoch(data::AbstractVector, args...; kwargs...)
One channel case. The data is reshaped and then passed to the multi-channel epoch method.
UnfoldSim.epoch — Method
epoch(
data::AbstractArray{T,2},
events,
τ::Tuple{Number,Number},
sfreq;
eventtime::Symbol = :latency,
) where {T<:Union{Missing,Number}}Helper function to segment continuous data into epochs based on the given events and the time window τ.
Adapted from Unfold.jl: https://github.com/unfoldtoolbox/Unfold.jl/blob/b3 a21c2bb7e93d2f45ec64b0197f4663a6d7939a/src/utilities.jl#L40
Arguments
data::AbstractArray{T,2}: Continuous data with the dimensionschannels x continuous_time.events: Events data frame (based on the design) with latencies.τ::Tuple{Number,Number}: Time window for epoching in s.sfreq: Sampling frequency in Hz.
Keyword arguments
eventtime::Symbol = :latency: The name of the column ineventsthat contains the latencies.
Returns
Array: Epoched data with the dimensionschannels x times x event(ortimes x eventfor the single channel case).
Examples
# One channel example
julia> data, events = UnfoldSim.predef_eeg();
julia> size(data), size(events)
((120199,), (2000, 3))
julia> UnfoldSim.epoch(data, events, (-0.2, 1), 100)
121×2000 Matrix{Float64}:
0.114127 -0.105347 … -0.125485 1.6383
0.128198 0.0474874 0.0112935 1.28122
0.0547917 -0.0832977 -0.126181 0.850062
0.0992842 -0.230224 -0.0449072 0.496583
0.024461 -0.175023 -0.0223837 0.170389
0.165133 -0.000793527 … -0.0278197 0.104454
⋮ ⋱
-0.362249 2.91297 0.546699 0.0
-0.199265 2.58394 0.171159 0.0
-0.184075 2.34611 -0.0269841 0.0
-0.13901 2.11971 -0.0552873 0.0
-0.0674085 1.74561 … -0.187959 0.0UnfoldSim.evaluate_sequencestring — Method
evaluate_sequencestring(rng, str::String)
evaluate_sequencestring(rng, dS::SequenceDesign)
evaluate_sequencestring(rng, dR::RepeatDesign)Generate a sequence based on the reverse regex style string in str, dS.sequence or dR.design.sequence.
Directly converting to Automa.Compileis not possible, as we first need to match & evaluate the curly brackets. We simply detect and expand them.
Arguments
str::String: a string mimicking a regex, e.g. "b[lL]{3,4}a" should evaluate to e.g. "bLlLLa". E.g. "b+la{3,4}" should in principle evaluate to "bbbbaaa" or "bllllllllllllaaaa" - but right now we disallow+and `` - we should revisit why exactly though.
Returns
result::String: a simulated string
Examples
julia> evaluate_sequencestring(MersenneTwister(1),"bla{3,4}")
"blaaaa"See also rand_re
UnfoldSim.expand_grid — Method
expand_grid(design)calculate all possible combinations of the key/value pairs of the design-dict. Copied from Effects.jl
UnfoldSim.generate_designmatrix — Method
Helper function to generate a designmatrix from formula, events and contrasts.
UnfoldSim.generate_events — Function
generate_events(design::AbstractDesign)
generate_events(rng::AbstractRNG, design::AbstractDesign)Generate a full-factorial events DataFrame based on the experimental conditions and covariates defined in the design.
Arguments
design::AbstractDesign: Experimental design for which the events DataFrame should be created.rng::AbstractRNG(optional): Random number generator (RNG) to make the process reproducible. If none is given,MersenneTwister(1)will be used.
Returns
DataFrame: Each row corresponds to one combination of condition/covariate levels which is often equivalent to one stimulus or trial.
UnfoldSim.generate_events — Method
generate_events(rng, design::SequenceDesign)First generates a sequence-string using evaluate_sequencestring(rng,design.sequence). Then generates events from the nested design.design and repeats them according to the length of the sequence string. Finally, assigns the sequence-string-characters to the :event column in events.
UnfoldSim.generate_events — Method
generate_events(rng::AbstractRNG, design::EffectsDesign)Generate events to simulate marginal effects using an Effects.jl reference-grid dictionary. Every covariate that is in the EffectsDesign but not in the effects_dict will be set to a typical_value (i.e. the mean).
Examples
julia> effects_dict = Dict(:conditionA => [0, 1]);
julia> design = SingleSubjectDesign(; conditions = Dict(:conditionA => [0, 1, 2]));
julia> eff_design = EffectsDesign(design,effects_dict);
julia> generate_events(MersenneTwister(1),eff_design)
2×1 DataFrame
Row │ conditionA
│ Int64
─────┼────────────
1 │ 0
2 │ 1UnfoldSim.generate_events — Method
generate_events(design::MultiSubjectDesign)
generate_events(rng::AbstractRNG, design::MultiSubjectDesign)Generate the events Dataframe according to MixedModelsSim.jl's simdat_crossed function.
Afterwards apply design.event_order_function and finally sort by :subject.
Note: No condition can be named dv which is used internally in MixedModelsSim / MixedModels as a dummy left-side
Examples
julia> using Random # for shuffling
julia> using StableRNGs
julia> design = MultiSubjectDesign(;
n_items = 4,
n_subjects = 5,
both_within = Dict(:condition => ["red", "green"]),
event_order_function = shuffle,
);
julia> generate_events(StableRNG(1), design)
40×3 DataFrame
Row │ subject item condition
│ String String String
─────┼────────────────────────────
1 │ S1 I2 red
2 │ S1 I2 green
3 │ S1 I3 green
⋮ │ ⋮ ⋮ ⋮
38 │ S5 I3 red
39 │ S5 I2 red
40 │ S5 I4 green
34 rows omittedUnfoldSim.generate_events — Method
UnfoldSim.generate_events([rng::AbstractRNG, ]design::RepeatDesign{T})For a RepeatDesign, iteratively call generate_events for the underlying {T} design and concatenate the results.
In case of MultiSubjectDesign, sort by subject.
Please note that when using an event_order_function(e.g. shuffle) in a RepeatDesign, the corresponding RNG is shared across repetitions and not deep-copied for each repetition. As a result, the order of events will differ for each repetition.
UnfoldSim.generate_events — Method
generate_events(design::SingleSubjectDesign)
generate_events(rng::AbstractRNG, design::SingleSubjectDesign)Generate the events DataFrame based on design.conditions and afterwards apply design.event_order_function.
If design.conditions is nothing, a single trial is simulated with a column :dummy and content :dummy - this is for convenience.
Examples
julia> using Random # for shuffling
julia> using StableRNGs
julia> design = SingleSubjectDesign(;
conditions = Dict(:A => ["small", "large"], :B => range(1, 5, length = 3)),
event_order_function = shuffle,
);
# Variant 1: Without specifying an RNG, MersenneTwister(1) will be used for the shuffling specified as `event_order_function`.
julia> generate_events(design)
6×2 DataFrame
Row │ A B
│ String Float64
─────┼─────────────────
1 │ large 5.0
2 │ large 1.0
3 │ large 3.0
4 │ small 1.0
5 │ small 3.0
6 │ small 5.0
# Variant 2: Use a custom RNG.
julia> generate_events(StableRNG(1), design)
6×2 DataFrame
Row │ A B
│ String Float64
─────┼─────────────────
1 │ large 5.0
2 │ large 3.0
3 │ small 1.0
4 │ small 3.0
5 │ large 1.0
6 │ small 5.0UnfoldSim.get_basis — Method
get_basis([rng],c::AbstractComponent)Return the basis of the component (typically c.basis). rng is optional and ignored, but exists to have the same interface as get_basis(c,design).
UnfoldSim.get_basis — Method
get_basis(c::AbstractComponent, design)
get_basis(rng, c::AbstractComponent, design)Evaluate the basis, if basis is a vector, directly returns it. If basis is a tuple (f::Function, maxlength::Int), evaluate the function with input design. Cut the resulting vector or Matrix at maxlength.
To ensure the same design can be generated, rng should be the global simulation rng. If not specified, MersenneTwister(1) is used.
UnfoldSim.get_offset — Method
get_offset(AbstractComponent)Should the basis be shifted? Returns c.offset for most components, if not implemented for a type, returns 0. Can be positive or negative, but has to be an Integer.
UnfoldSim.hartmut_citation — Method
Return the citation string for HArtMuT.
UnfoldSim.hrf — Method
hrf(;
TR = 1,
peak = 6.0,
post_undershoot = 16,
length = 32.0,
peak_width = 1.0,
post_undershoot_width = 1,
amplitude = 6,
shift = 0)Generate a parameterized BOLD haemodynamic response function (HRF) kernel based on gamma-functions.
Implementation and default parameters were taken from the SPM-toolbox.
Note: TR = 1/sfreq
Keyword arguments
TR = 1: repetition time, 1/sfreq.length = 32.0: total length of the kernel in seconds.amplitude = 6: maximal amplitude.peak = 6.0: peak timing.peak_width = 1.0: width of the peak.post_undershoot = 16: post-undershoot timing.post_undershoot_width = 1: post-undershoot width.shift = 0: shift the whole HRF.
Returns
Vector: HRF BOLD response.
Examples
julia> hrf()
33-element Vector{Float64}:
0.0
0.0007715994433635659
0.019784004131204957
0.08202939459091822
0.158157713522699
⋮
-0.0006784790038792572
-0.00042675060451877775
-0.000263494738348278
-0.00015990722628360688
-9.548780093799345e-5UnfoldSim.init_epoch_data — Method
Initializes an Array with zeros. Returns either a 2-dimensional for component-length x length(design), or a 3-D for channels x component-length x length(design)
UnfoldSim.leadfield — Method
leadfield(hart::Hartmut; type = "cortical")Return the leadfield for the (cortical or artefacual) sources of the HArtMuT model.
Keyword arguments
type = "cortical": Defines whether the "cortical" or "artefactual" leadfield should be returned.
Returns
Array{Float64, 3}: Leadfield values i.e. how much each source contributes to the potential measured at each electrode. The output dimensions areelectrodes x sources x spatial dimension.
Examples
julia> h = Hartmut();
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
julia> lf = leadfield(h);
julia> size(lf)
(227, 2004, 3)
# Access the leadfield for one spatial dimension
julia> lf[:,:,1]
227×2004 Matrix{Float64}:
0.151429 0.0284341 … -0.119927 -0.158114
0.1732 0.0441432 -0.110515 -0.165316
0.249592 0.10857 -0.000593027 -0.0206122
0.245206 0.104854 -0.0200251 -0.055392
0.126496 0.0118467 -0.128248 -0.146107
0.253092 0.111688 … 0.02277 0.0184387
⋮ ⋱
0.20306 0.138837 0.133486 0.18334
-0.689154 -1.00904 -0.108276 -0.105398
0.192729 0.148448 0.0924756 0.142322
-1.26181 -1.59936 -0.140598 -0.133054
0.213982 0.145953 … 0.115515 0.170698
0.0731569 0.0794415 0.0485819 0.107929UnfoldSim.limit_basis — Method
limit_basis(b::AbstractVector{<:AbstractVector}, maxlength)
Cut all basis vectors to `maxlength` and pad them with 0s if they are shorter than `maxlength`.UnfoldSim.magnitude — Method
magnitude(headmodel::AbstractHeadmodel)Return the magnitude for the given headmodel based on the leadfield (and potentially the source orientations) specified in the headmodel.
If the headmodel includes source orientations these are used in the calculations, otherwise the leadfield is returned assuming that the source orientations are already included. Please note that (at least in the case of the HArtMuT model) the leadfield for the cortical sources is used.
Returns
Matrix{Float64}: Contribution of each source to the potential measured at each electrode taking into account the orientation of the sources. The output dimensions areelectrodes x sources.
Examples
# Using the HArtMut model as an example
julia> h = Hartmut();
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
julia> magnitude(h)
227×2004 Matrix{Float64}:
0.111706 … 0.301065
0.0774359 0.332934
⋮ ⋱
-0.164539 -0.246555UnfoldSim.magnitude — Method
magnitude(lf::AbstractArray{T,3}, orientation::AbstractArray{T,2}) where {T<:Real}Return the magnitude of the leadfield lf along the given orientation.
Arguments
lf::AbstractArray{T,3}: Leadfield with the dimensionselectrodes x sources x spatial dimension.orientation::AbstractArray{T,2}: Source orientations with the dimensionssources x spatial dimensions.
Examples
# Specify the leadfield (often given by a headmodel)
julia> lf = cat([1 0; 0 1; 0 0], [1 1; 0 0; 0.5 0.5], dims=3);
# Specify the source orientations
julia> ori = [1.0 0; 0 1]
# Calculate the magnitude
julia> magnitude(lf, ori)
3×2 Matrix{Float64}:
1.0 1.0
0.0 0.0
0.0 0.5UnfoldSim.maxlength — Method
maxlength(c::Vector{<:AbstractComponent}) = maximum(length.(c))
maxlength(components::Dict)Return the maximum of the individual components' lengths.
UnfoldSim.n170 — Method
n170(; sfreq = 100)Generate a Hanning window mimicking an N170 EEG component with a negative (!) peak at 170ms and a width of 150ms.
Keyword arguments
sfreq = 100: Sampling frequency in Hz.
Returns
Vector: Contains a shifted (i.e. zero-padded) hanning window.
Examples
julia> n170(; sfreq = 120)
28-element Vector{Float64}:
-0.0
-0.0
-0.0
-0.0
-0.0
⋮
-0.45386582026834904
-0.2771308221117309
-0.1304955413896705
-0.03376388529782215
-0.0UnfoldSim.n400 — Method
n400(; sfreq = 100)Generate a Hanning window mimicking an N400 EEG component with a negative (!) peak at 400ms and a width of 400ms.
Keyword arguments
sfreq = 100: Sampling frequency in Hz.
Returns
Vector: Contains a shifted (i.e. zero-padded) hanning window.
Examples
julia> n400(; sfreq = 250)
150-element Vector{Float64}:
-0.0
-0.0
-0.0
-0.0
-0.0
⋮
-0.016025649301821876
-0.009035651368646647
-0.00402259358460233
-0.0010066617640578368
-0.0UnfoldSim.n_channels — Method
n_channels(c::AbstractComponent)Return the number of channels for the given component c. By default = 1.
UnfoldSim.n_channels — Method
n_channels(c::MultichannelComponent)For MultichannelComponent return the length of the projection vector.
UnfoldSim.n_channels — Method
n_channels(c::Vector{<:AbstractComponent})
n_channels(c::Dict)For a vector of MultichannelComponents or a Dict of components, return the number of channels for the first component but assert all are of equal length.
UnfoldSim.orientation — Method
orientation(hart::Hartmut; type = "cortical")Return the orientations of the (cortical or artefacual) sources of the HArtMuT model.
The norm of the orientation vectors is 1 and the values are between -1 and 1.
Keyword arguments
type = "cortical": Defines whether the "cortical" or "artefactual" orientations should be returned.
Returns
Matrix{Float64}: Orientations in 3D space. The output dimensions aresources x spatial dimensions.
Examples
julia> h = Hartmut();
Please cite: HArtMuT: Harmening Nils, Klug Marius, Gramann Klaus and Miklody Daniel - 10.1088/1741-2552/aca8ce
julia> orientation(h)
2004×3 Matrix{Float64}:
-0.921919 0.364292 0.131744
-0.900757 -0.415843 -0.125345
-0.954087 0.117479 -0.27553
-0.814613 -0.55344 0.17352
-0.790526 0.276849 -0.546281
⋮
-0.962905 0.20498 -0.17549
-0.828358 0.557468 -0.0552498
-0.963785 -0.265607 -0.0239074
-0.953909 0.203615 0.22045
-0.75762 0.128027 0.640016UnfoldSim.p100 — Method
p100(; sfreq = 100)Generate a Hanning window mimicking a P100 EEG component with a peak at 100ms and a width of 100ms.
Keyword arguments
sfreq = 100: Sampling frequency in Hz.
Returns
Vector: Contains a shifted (i.e. zero-padded) hanning window.
Examples
julia> p100(; sfreq = 200)
30-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
⋮
0.37725725642960045
0.22652592093878665
0.10542974530180327
0.02709137914968268
0.0UnfoldSim.p300 — Method
p300(; sfreq = 100)Generate a Hanning window mimicking a P300 EEG component with a peak at 300ms and a width of 300ms.
Keyword arguments
sfreq = 100: Sampling frequency in Hz.
Returns
Vector: Contains a shifted (i.e. zero-padded) hanning window.
Examples
julia> p300(; sfreq = 150)
67-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
⋮
0.07937323358440934
0.04518400232274078
0.02025351319275137
0.005089279059533658
0.0UnfoldSim.pad_array — Method
pad_array(arr::Vector, len::Int, val)
pad_array(arr::Vector, len::Tuple, val)Pads the input array arr with a specified value val either before or after the existing elements, based on the sign of len.
Arguments
arr::Vector: The input array to be padded.len::Union{Int, Tuple}: The number of times thatvalshould be added. Iflenis negative, the values are added before the existing array. Otherwise, they are added after the existing array. Iflenis a tuple,pad_arrayis called twice which enables padding the array before and after in one function call.val: The value to be used for padding.
Returns
Vector: Padded vector with the new lengthlength(arr) + sum(abs.(len)).
Examples
# Create an array that will be padded
julia> my_array = rand(5)
5-element Vector{Float64}:
0.0420017254437951
0.19179144973603235
0.5388760239550549
0.6973699906283798
0.9966598131018376
# Pad the array with zeros before the original array
julia> pad_array(my_array, -2, 0)
7-element Vector{Float64}:
0.0
0.0
0.0420017254437951
0.19179144973603235
0.5388760239550549
0.6973699906283798
0.9966598131018376
# Pad the array with the value 5 before and after the original array
julia> pad_array(my_array, (-2, 1), 5)
8-element Vector{Float64}:
5.0
5.0
0.0420017254437951
0.19179144973603235
0.5388760239550549
0.6973699906283798
0.9966598131018376
5.0UnfoldSim.predef_2x2 — Method
predef_2x2(rng::AbstractRNG; <keyword arguments>)Simulate data for a 2x2 design i.e. a design with two conditions with two levels each.
Note that this function is mainly used for demonstration and internal testing purposes or whenever a quick simulation is needed.
The most used keyword argument is: return_epoched = true which returns already epoched data. If you want epoched data without overlap, specify onset = NoOnset() and return_epoched = true.
Be careful if you modify n_items with n_subjects = 1, n_items has to be a multiple of 4 (or your equivalent conditions factorial, e.g. all combinations length).
In difference to predef_EEG, predef_2x2 is sample based (no sampling rate to be specified), and also has a 2x2 design, instead of a 2-categorical, 1-continuous design.
Keyword arguments
Design
n_items = 100: Number of items.n_subjects = 1: Number of subjects.conditions = Dict(:A => ["a_small","a_big"], :B => ["b_tiny","b_large"]): Experimental conditions with their levels.event_order_function = shuffle: Random trial order.
Component / Signal
signalsize = 100: Length of simulated hanning window.basis = hanning(signalsize): The actual "function".signalsizeis only used here.β = [1, -0.5, .5, +1]: The parameters for the fixed effects.σs = Dict(:subject => [1, 0.5, 0.5, 0.5],:item => [1]): Only needed in n_subjects >= 2 cases; specifies the random effects.contrasts = Dict(:A => EffectsCoding(), :B => EffectsCoding()): Effect coding by default.formula = n_subjects == 1 ? @formula(0 ~ 1 + A*B) : @formula(dv ~ 1 + A*B + (A*B|subject) + (1|item)): Model formula with interaction.
Onset
overlap = (0.5,0.2),onset = UniformOnset(; offset = signalsize * overlap[1], width = signalsize * overlap[2]), # Put offset to 1 for no overlap. put width to 0 for no jitter
Noise
noiselevel = 0.2.noise = PinkNoise(; noiselevel = noiselevel).
Other parameters
return_epoched = false: If true, already epoched data is returned. Otherwise, continuous data is returned.
Returns
(data, events)::Tuple{Array, DataFrame}:datacontains the simulated EEG data. Its dimensionality depends on the status ofreturn_epochedand whether nsubjects >=2: `continuoustime,continuoustime x nsubjects,times x size(design)[1]ortimes x size(design)[1] x n_subjects`.eventscontains the event combinations based on the experimental design. Each row corresponds to one combination of condition levels which is often equivalent to one stimulus or trial.
Examples
julia> data, events = UnfoldSim.predef_2x2();
julia> events
100×3 DataFrame
Row │ A B latency
│ String String Int64
─────┼───────────────────────────
1 │ a_small b_large 62
2 │ a_big b_tiny 132
⋮ │ ⋮ ⋮ ⋮
99 │ a_big b_large 5883
100 │ a_big b_tiny 5935
96 rows omitted
julia> data
6035-element Vector{Float64}:
0.32384561187165956
0.4108799249488322
0.4715514814540277
0.5936253009785152
⋮
0.047664408295942984
0.051193074728432035
-0.08951617593287822
-0.14456000097460356See also predef_eeg.
UnfoldSim.predef_eeg — Method
predef_eeg(; <keyword arguments>)
predef_eeg(rng; <keyword arguments>)
predef_eeg(rng, n_subjects; <keyword arguments>)Simulate data for a P1/N1/P3 component complex.
Note that this function is mainly used for demonstration and internal testing purposes or whenever a quick simulation is needed.
In case n_subjects is defined - MixedModelComponents are generated (multi-subject simulation), else LinearModelComponents (single-subject simulation).
The most used keyword argument is: return_epoched = true which returns already epoched data. If you want epoched data without overlap, specify onset = NoOnset() and return_epoched = true.
Keyword arguments
Design
n_repeats = 100: Number of times the experimental design is repeated. Only used in the single-subject case.n_items = 100: Number of items. Only used in the multi-subject case.event_order_function = shuffle: Random trial order. Useevent_order_function = (rng, x) -> xto deactivate.conditions = Dict(:condition => ["car", "face"], :continuous => range(-5, 5, length = 10)): Conditions and covariates used in this predefined design.
Component / Signal
sfreq = 100: Sampling frequency.p1 = (p100(; sfreq = sfreq), @formula(0 ~ 1), [5], Dict()): P1 with amplitude 5; no effects.n1 = (n170(; sfreq = sfreq), @formula(0 ~ 1 + condition), [5, 3], Dict()): N1 with amplitude 5, dummy-coded condition effect (levels "car", "face") of 3.p3 = (p300(; sfreq = sfreq), @formula(0 ~ 1 + continuous), [5, 1], Dict()): P3 with amplitude 5, continuous effect range [-5,5] with slope 1.
Onset
overlap = (0.5,0.2), # convenient parameterization for the defaultonset::UniformOnset. (offset,width) in seconds. If you do not want any overlap, either useonset=NoOnset(), or put the offset to a value larger than the maximum used component length, e.g.overlap=(1,0.2). Put thewidthto0to have no jitter between events.onset = UniformOnset(; offset = sfreq * 0.5 * overlap[1], width = sfreq * 0.5 * overlap[2]),
Noise
noiselevel = 0.2.noise = PinkNoise(; noiselevel = noiselevel).
Multichannel
multichannel = nothing: to receive 227 channels multichannel data, provide a list ofhartmut.cortical["label"]. If set totrueuses:["Right Occipital Pole", "Left Postcentral Gyrus", "Left Superior Frontal Gyrus",].
Other parameters
return_epoched = false: If true, already epoched data is returned. Otherwise, continuous data is returned.
Returns
(data, events)::Tuple{Array, DataFrame}:datacontains the simulated EEG data. Its dimensionality depends on the status ofreturn_epochedand whether it's a single- or multisubject simulation (1D, 2D or 3D array):continuous_time,continuous_time x n_subjects,times x size(design)[1]ortimes x size(design)[1] x n_subjects.eventscontains the event combinations based on the experimental design. Each row corresponds to one combination of condition/covariate levels which is often equivalent to one stimulus or trial.
Examples
julia> data, events = UnfoldSim.predef_eeg();
julia> events
2000×3 DataFrame
Row │ continuous condition latency
│ Float64 String Int64
──────┼────────────────────────────────
1 │ 2.77778 car 62
2 │ -5.0 face 132
⋮ │ ⋮ ⋮ ⋮
1999 │ -0.555556 face 120096
2000 │ -2.77778 car 120154
julia> data
120199-element Vector{Float64}:
0.31631798033146774
0.40338935529989906
0.46409775558165056
0.5862082040156747
⋮
-0.1879589005111152
-0.3163314509311509
-0.22230944464885682
-0.01320095208877194See also predef_2x2.
UnfoldSim.rand_re — Method
rand_re(rng::AbstractRNG, machine::Automa.Machine)Mimicks a reverse-regex, generating strings from regex instead of matching. Based on Automata.jl
Arguments
machine::Automa.Machine: A Automa.Machine, typically output ofAutoma.Compile(RE("mystring"))
Returns
result::String: A string following the rules inAutoma.Machine.{}are not supported, but e.g.+,*
Examples
julia> using Automa
julia> machine = Automa.compile(Automa.RegExp.RE("b+l+a+"))
julia> rand_re(MersenneTwister(2),machine)
"bbbbblllaaa"UnfoldSim.simulate — Function
simulate(
rng::AbstractRNG,
design::AbstractDesign,
components,
onset::AbstractOnset,
noise::AbstractNoise = NoNoise();
return_epoched = false,
)
simulate(
design::AbstractDesign,
components,
onset::AbstractOnset,
noise::AbstractNoise = NoNoise();
return_epoched = false,
)Simulate continuous or epoched signal, given design, [Array of] component, onset and optional noise and rng. Main simulation function.
Arguments
rng::AbstractRNG(optional): Random number generator, important to ensure reproducibility.design::AbstractDesign: Desired experimental design.components:Component(s) for the desired signal.onset::AbstractOnset: Desired inter-onset distance distribution.noise::AbstractNoise = NoNoise()(optional): Desired noise.
Keyword arguments
return_epoched::Bool = false: If set totrueepoched data is returned, otherwise a continuous signal is returned (see also Notes below).
Returns
(signal, events)::Tuple{Array, DataFrame}:signal: Generated signal. Depending on the design, on the components and onreturn_epoched, the output can be a 1-D, 2-D, 3-D or 4-D Array. For example, a 4-D Array would have the dimensionschannels x time x trials x subjects.events: Generated events data frame with latencies.
Examples
Adapted from the quickstart tutorial in the UnfoldSim docs.
julia> using Random # to get an RNG
julia> design =
SingleSubjectDesign(; conditions = Dict(:cond_A => ["level_A", "level_B"])) |>
x -> RepeatDesign(x, 10);
julia> component = LinearModelComponent(;
basis = [0, 0, 0, 0.5, 1, 1, 0.5, 0, 0],
formula = @formula(0 ~ 1 + cond_A),
β = [1, 0.5],
);
julia> onset = UniformOnset(; width = 20, offset = 4);
julia> noise = PinkNoise(; noiselevel = 0.2);
# Variant 1: Use a custom RNG.
julia> data, events = simulate(MersenneTwister(2), design, component, onset, noise);
julia> data
293-element Vector{Float64}:
-0.013583193323430123
0.09159433856866195
⋮
-0.25190584567097907
-0.20179992275876316
julia> events
20×2 DataFrame
Row │ cond_A latency
│ String Int64
─────┼──────────────────
1 │ level_A 9
2 │ level_B 20
3 │ level_A 27
4 │ level_B 37
⋮ │ ⋮ ⋮
18 │ level_B 257
19 │ level_A 271
20 │ level_B 284
13 rows omitted
# Variant 2: Without specifying an RNG, MersenneTwister(1) will be used for the simulation.
julia> data1, events1 = simulate(design, component, onset, noise);
┌ Warning: No random generator defined, used the default (`Random.MersenneTwister(1)`) with a fixed seed. This will always return the same results and the user is strongly encouraged to provide their own random generator!Notes
Some remarks on how the noise is added:
- If
return_epoched = trueandonset = NoOnset()the noise is added to the epoched data matrix. - If
onsetis notNoOnset, a continuous signal is created and the noise is added to this i.e. this means that the noise won't be the same as in theonset = NoOnset()case even ifreturn_epoched = true. - The case
return_epoched = falseandonset = NoOnset()is not possible and therefore covered by an assert statement.
Additional remarks on the overlap of adjacent signals when return_epoched = true:
- If
onset = NoOnset()there will not be any overlapping signals in the data because the onset calculation and conversion to a continuous signal is skipped. - If an inter-onset distance distribution is given, a continuous signal(potentially with overlap) is constructed and partitioned into epochs afterwards.
UnfoldSim.simulate_and_add! — Method
simulate_and_add!(epoch_data::AbstractMatrix, c, simulation, rng)
simulate_and_add!(epoch_data::AbstractArray, c, simulation, rng)Helper function to call simulate_component and add it to a provided Array epoch_data.
UnfoldSim.simulate_component — Method
simulate_component(rng, c::AbstractComponent, simulation::Simulation)By default call simulate_component with (rng, c::Abstractcomponent, design::AbstractDesign) instead of the whole simulation. This function exist solely to provide a "hook" if for a custom component something else than the design is necessary, e.g. a dependency on the onsets, noise or similar.
UnfoldSim.simulate_component — Method
simulate_component(rng, c::LinearModelComponent, design::AbstractDesign)Generate a linear model design matrix, weight it by the coefficients c.β and multiply the result with the given basis vector.
Returns
Matrix{Float64}: Simulated component for each event in the events data frame. The output dimensions arelength(get_basis(c.basis)) x length(design).
Examples
julia> design = SingleSubjectDesign(; conditions = Dict(:cond => ["natural", "artificial"]));
julia> c = UnfoldSim.LinearModelComponent([0, 1, 1, 0], @formula(0 ~ 1 + cond), [1, 2], Dict());
julia> using StableRNGs
julia> simulate_component(StableRNG(1), c, design)
4×2 Matrix{Float64}:
0.0 0.0
3.0 1.0
3.0 1.0
0.0 0.0UnfoldSim.simulate_component — Method
simulate_component(rng, c::MixedModelComponent, design::AbstractDesign, return_parameters = false)Generate a MixedModel and simulate data according to the given parameters c.β and c.σs.
Keyword arguments
return_parameters::Bool = false: Can be used to return the per-event parameters used to weight the basis function. Sometimes useful to inspect what is simulated.
Returns
Matrix{Float64}: Simulated component for each event in the events data frame. The output dimensions arelength(get_basis(basis)) x length(design).
Notes
- MixedModels/Sim does not allow simulation of data without white noise of the residuals. Because we want our own noise, we use the following trick to remove the MixedModels-Noise:
Practically, we upscale the specified σs by factor 10000, and request a white-noise-level of σ = 0.0001. Internally in MixedModels/Sim, σs are relative to σ, and thus are normalized correctly, while keeping the noise 10000 times smaller than the random effects
We cannot exclude that this trick runs into strange numerical issues if the random effect σs are very large compared to the fixed effects.
- Currently, it is not possible to use a different basis for fixed and random effects. If this is needed, some code-scaffold is available but commented out at the moment and requires a bit of implementation work.
Examples
julia> design = MultiSubjectDesign(; n_subjects = 2, n_items = 6, items_between = Dict(:cond => ["A", "B"]));
julia> c = UnfoldSim.MixedModelComponent([0, 1, 1, 0], @formula(0 ~ 1 + cond + (1|subject)), [1, 2], Dict(:subject => [2],), Dict());
julia> using StableRNGs
julia> simulate_component(StableRNG(1), c, design)
4×12 Matrix{Float64}:
-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 0.0 0.0 0.0 0.0 0.0 0.0
-2.70645 -0.706388 -2.70632 -0.706482 -2.7066 -0.706424 0.325722 2.32569 0.325627 2.32564 0.325468 2.32565
-2.70645 -0.706388 -2.70632 -0.706482 -2.7066 -0.706424 0.325722 2.32569 0.325627 2.32564 0.325468 2.32565
-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 0.0 0.0 0.0 0.0 0.0 0.0
julia> simulate_component(StableRNG(1), c, design, return_parameters = true)
1×12 Matrix{Float64}:
-2.70645 -0.706388 -2.70632 -0.706482 -2.7066 -0.706424 0.325722 2.32569 0.325627 2.32564 0.325468 2.32565UnfoldSim.simulate_component — Method
simulate_component(rng, c::MultichannelComponent, design::AbstractDesign)Return the projection of a MultichannelComponent c from "source" to "sensor" space.
Returns
Array{Float64,3}: Projected simulated component for each event in the events data frame. The output dimensions arelength(c.projection) x length(get_basis(c)) x length(design).
Examples
julia> design = SingleSubjectDesign(; conditions = Dict(:cond => ["natural", "artificial"]));
julia> c = LinearModelComponent(; basis = p100(), formula = @formula(0 ~ 1 + cond), β = [1, 0.5]);
julia> mc = UnfoldSim.MultichannelComponent(c, [1, 2, -1, 3, 5, 2.3, 1], PinkNoise());
julia> using StableRNGs
julia> simulate_component(StableRNG(1), mc, design)
7×15×2 Array{Float64, 3}:
[:, :, 1] =
0.859626 1.16574 0.959523 0.757522 1.17639 1.65156 1.3742 1.76706 2.76971 2.0306 1.17429 1.00922 1.09519 0.754659 2.25662
1.71925 2.33147 1.91905 1.51504 2.35278 3.30312 2.7484 3.53412 5.53942 4.0612 2.34858 2.01845 2.19039 1.50932 4.51324
-0.859626 -1.16574 -0.959523 -0.757522 -1.17639 -1.65156 -1.3742 -1.76706 -2.76971 -2.0306 -1.17429 -1.00922 -1.09519 -0.754659 -2.25662
2.57888 3.49721 2.87857 2.27257 3.52917 4.95469 4.1226 5.30118 8.30913 6.09179 3.52287 3.02767 3.28558 2.26398 6.76985
4.29813 5.82868 4.79761 3.78761 5.88194 8.25781 6.871 8.8353 13.8485 10.153 5.87145 5.04612 5.47597 3.7733 11.2831
1.97714 2.68119 2.2069 1.7423 2.70569 3.79859 3.16066 4.06424 6.37033 4.67037 2.70087 2.32121 2.51894 1.73572 5.19022
0.859626 1.16574 0.959523 0.757522 1.17639 1.65156 1.3742 1.76706 2.76971 2.0306 1.17429 1.00922 1.09519 0.754659 2.25662
[:, :, 2] =
0.859626 1.16574 0.959523 0.757522 1.17639 1.65156 1.31571 1.56047 2.39471 1.54567 0.689367 0.634223 0.888605 0.69617 2.25662
1.71925 2.33147 1.91905 1.51504 2.35278 3.30312 2.63142 3.12094 4.78942 3.09135 1.37873 1.26845 1.77721 1.39234 4.51324
-0.859626 -1.16574 -0.959523 -0.757522 -1.17639 -1.65156 -1.31571 -1.56047 -2.39471 -1.54567 -0.689367 -0.634223 -0.888605 -0.69617 -2.25662
2.57888 3.49721 2.87857 2.27257 3.52917 4.95469 3.94713 4.68142 7.18413 4.63702 2.0681 1.90267 2.66582 2.08851 6.76985
4.29813 5.82868 4.79761 3.78761 5.88194 8.25781 6.57855 7.80236 11.9735 7.72837 3.44684 3.17112 4.44303 3.48085 11.2831
1.97714 2.68119 2.2069 1.7423 2.70569 3.79859 3.02613 3.58909 5.50783 3.55505 1.58554 1.45871 2.04379 1.60119 5.19022
0.859626 1.16574 0.959523 0.757522 1.17639 1.65156 1.31571 1.56047 2.39471 1.54567 0.689367 0.634223 0.888605 0.69617 2.25662UnfoldSim.simulate_interonset_distances — Function
simulate_interonset_distances(rng, onset::AbstractOnset, design::AbstractDesign)Generate the inter-onset distance vector by sampling from the respective distribution (in samples).
Arguments
rng: Random number generator (RNG) to make the process reproducible.onset::AbstractOnset: Inter-onset distance distribution to sample from.design::AbstractDesign: Experimental design with conditions and covariates.
Returns
Vector{Integer}: Inter-onset distances in samples. Note that these are distances between onsets and no latencies.
Examples
# Create an experimental design
julia> design_single = SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 3),
),
);
# Create an inter-onset distance distribution
julia> onset_distribution = LogNormalOnset(3, 0.5, 5, nothing);
julia> using StableRNGs
julia> simulate_interonset_distances(StableRNG(1), onset_distribution, design_single)
6-element Vector{Int64}:
20
26
34
18
12
23See also simulate_onsets.
UnfoldSim.simulate_interonset_distances — Method
simulate_interonset_distances(rng, onsets::ShiftOnsetByOne, design)Same functionality as simulate_interonset_distances(rng,onsets::AbstractOnset) except that it shifts the resulting vector by one, adding a 0 to the front and removing the last simuluated distance.
UnfoldSim.simulate_noise — Function
simulate_noise(rng, t::AbstractNoise, n::Int, [Simulation::Simulation])
simulate_noise(rng, t::AbstractNoise, n::Tuple, [Simulation::Simulation])Generate noise samples of the given type t.
For details, see the documentation of the individual noise types. Use subtypes(AbstractNoise) for a list of the implemented noise types.
Arguments
rng::AbstractRNG: Random number generator (RNG) to make the process reproducible.t::AbstractNoise: Instance of a noise type e.g.PinkNoise().n::Int: The number of noise samples that should be generated. If a tuple is provided, theprod(n)samples are generated. Future usage might generate multi-dimensional noise more directly.simulation::Simulation(optional): Currently not used, but provided for future extensions where the noise generation might depend on the simulation design.
Returns
Vector: Vector of lengthncontaining the noise samples.
Examples
# Here we use White Noise as an example but it works in the same way for the other noise types.
julia> noise = WhiteNoise()
WhiteNoise
noiselevel: Int64 1
imfilter: Int64 0
julia> using StableRNGs
julia> simulate_noise(StableRNG(1), noise, 3)
3-element Vector{Float64}:
-0.5325200748641231
0.098465514284785
0.7528865221245234UnfoldSim.simulate_onsets — Method
simulate_onsets(rng, onset::AbstractOnset, simulation::Simulation)Call simulate_interonset_distances to generate distances between events and then add them up to generate the actual latencies in samples.
Please note that this function is mainly for internal use in the context of simulate function calls.
Also note that the accumulation of onsets starts at 1 to avoid indexing problems in the case that the first sampled onset is 0.
In case of a SequenceDesign with a '_' no-overlap indicator, we use twice the maxlength(components) as the distance following that sequence character.
Arguments
rng: Random number generator (RNG) to make the process reproducible.onset::AbstractOnset: Inter-onset distance distribution which is passed tosimulate_interonset_distances.simulation::Simulation: Simulation object which contains design, component(s), inter-onset distance distribution and noise.
Returns
Examples
# Create Simulation object
julia> design_single = SingleSubjectDesign(;
conditions = Dict(
:stimulus_type => ["natural", "artificial"],
:contrast_level => range(0, 1, length = 3),
),
);
julia> p1_component = LinearModelComponent(; basis = p100(), formula = @formula(0 ~ 1), β = [5]);
julia> simulation = Simulation(design_single, p1_component, UniformOnset(), NoNoise());
julia> using StableRNGs
# Simulate onsets for this simulation
julia> simulate_onsets(StableRNG(1), simulation.onset, simulation)
6-element Vector{Int64}:
20
70
97
110
150
182See also simulate_interonset_distances.
UnfoldSim.simulate_responses — Method
simulate_responses(
rng,
event_component_dict::Dict,
s::Simulation)Per event, simulate the components specified by the Dict, and return the epocheddata array. Internally wraps the designs in a SubselectDesign for each event type, so that only the relevant trials are simulated. If a `` is present, it is ignored.
Arguments
rng: Random number generator.event_component_dict::Dict: Dictionary mapping character event types to Vector of componentss::Simulation: Simulation object containing design and other parameters.
UnfoldSim.simulate_responses — Method
simulate_responses(
rng,
components::Vector{<:AbstractComponent},
simulation::Simulation)Simulate multiple component responses and accumulate them on a per-event basis.
Returns
epoch_data:Matrix(orArrayin the multi-channel case) of combined simulated components. The output dimensions aremaxlength(components) x length(simulation.design)for single-channel components andn_channels(components) x maxlength(components) x length(simulation.design)for multi-channel components.
Examples
julia> design = SingleSubjectDesign(; conditions = Dict(:cond => ["natural", "artificial"]));
julia> c1 = LinearModelComponent(; basis = p100(), formula = @formula(0 ~ 1 + cond), β = [1, 0.5]);
julia> c2 = LinearModelComponent(; basis = p300(), formula = @formula(0 ~ 1), β = [2]);
julia> simulation = Simulation(design, [c1, c2], UniformOnset(; width = 0, offset = 30), PinkNoise());
julia> using StableRNGs
julia> simulate_responses(StableRNG(1), [c1, c2], simulation)
45×2 Matrix{Float64}:
0.0 0.0
0.0 0.0
0.0 0.0
0.0 0.0
0.0 0.0
⋮
0.352614 0.352614
0.203907 0.203907
0.0924246 0.0924246
0.0233794 0.0233794
0.0 0.0UnfoldSim.typical_value — Method
typical_value(v)
typical_value(v::Vector{<:Number})Return the typical value, either the mean (if a vector) or all unique levels otherwise. Copied from Effects.jl
UnfoldSim.weight_σs — Method
weight_σs(σs::Dict, b_σs::Float64, σ_lmm::Float64)Weight a σs Dict for MixedModels.jl by b_σs, a scaling factor typically from a basis.
Finally scales it again by σ_lmm, as a trick to simulate noise-free LMMs (see MixedModelsComponent)
In the future, we anticipate a function function weight_σs(σs::Dict,b_σs::Dict,σ_lmm::Float64) where each σs entry can be weighted individually by a matching b_σs, but it is not implemented.
Arguments
σs::Dict= a Dict of random effects as output of MixedModels.create_reb_σs::Float64= a scaling factor, typically one entry of a basis function from a componentσ_lmm::Float64= a scaling factor to simulate near-zero noise LMMs
Returns
`NamedTuple` of the weighted random effects