diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 3bf1c06..250f3c3 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-06-06T08:32:46","documenter_version":"1.4.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-06-19T10:01:35","documenter_version":"1.4.1"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index bc39989..44310e5 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,45 +1,45 @@ -API / DocStrings · UnfoldSim.jl
UnfoldSim.ExponentialNoiseType
ExponentialNoise <: AbstractNoise

Noise with exponential decay in AR spectrum.

noiselevel is used to scale the noise

Warning

Current implementation: Cholesky of NxN matrix needs to be calculated, which might need lots of RAM.

source
UnfoldSim.LinearModelComponentType

A multiple regression component for one subject

  • basis: an object, if accessed, provides a 'basis-function', e.g. hanning(40), this defines the response at a single event. It will be weighted by the model-prediction
  • formula: StatsModels Formula-Object @formula 0~1+cond (left side must be 0)
  • β Vector of betas, must fit the formula
  • contrasts: Dict. Default is empty, e.g. Dict(:condA=>EffectsCoding())

All arguments can be named, in that case contrasts is optional

Works best with SingleSubjectDesign

LinearModelComponent(;
+API / DocStrings · UnfoldSim.jl
UnfoldSim.ExponentialNoiseType
ExponentialNoise <: AbstractNoise

Noise with exponential decay in AR spectrum.

noiselevel is used to scale the noise

Warning

Current implementation: Cholesky of NxN matrix needs to be calculated, which might need lots of RAM.

source
UnfoldSim.LinearModelComponentType

A multiple regression component for one subject

  • basis: an object, if accessed, provides a 'basis-function', e.g. hanning(40), this defines the response at a single event. It will be weighted by the model-prediction
  • formula: StatsModels Formula-Object @formula 0~1+cond (left side must be 0)
  • β Vector of betas, must fit the formula
  • contrasts: Dict. Default is empty, e.g. Dict(:condA=>EffectsCoding())

All arguments can be named, in that case contrasts is optional

Works best with SingleSubjectDesign

LinearModelComponent(;
     basis=hanning(40),
     formula=@formula(0~1+cond),
     β = [1.,2.],
     contrasts=Dict(:cond=>EffectsCoding())
 )
-
source
UnfoldSim.LogNormalOnsetType
@with_kw struct LogNormalOnset <: AbstractOnset

Log-normal inter-event distances using the Distributions.jl truncated LogNormal distribution.

Be careful with large μ and σ values, as they are on logscale. σ>8 can quickly give you out-of-memory sized signals!

source
UnfoldSim.MixedModelComponentType

A component that adds a hierarchical relation between parameters according to a LMM defined via MixedModels.jl

  • basis: an object, if accessed, provides a 'basis-function', e.g. hanning(40), this defines the response at a single event. It will be weighted by the model-prediction
  • formula: Formula-Object in the style of MixedModels.jl e.g. @formula 0~1+cond + (1|subject) - left-handside is ignored
  • β Vector of betas, must fit the formula
  • σs Dict of random effect variances, e.g. Dict(:subject=>[0.5,0.4]) or to specify correlationmatrix Dict(:subject=>[0.5,0.4,I(2,2)],...). Technically, this will be passed to MixedModels.jl create_re function, which creates the θ matrices.
  • contrasts: Dict in the style of MixedModels.jl. Default is empty.

All arguments can be named, in that case contrasts is optional

Works best with MultiSubjectDesign

MixedModelComponent(;
+
source
UnfoldSim.LogNormalOnsetType
@with_kw struct LogNormalOnset <: AbstractOnset

Log-normal inter-event distances using the Distributions.jl truncated LogNormal distribution.

Be careful with large μ and σ values, as they are on logscale. σ>8 can quickly give you out-of-memory sized signals!

source
UnfoldSim.MixedModelComponentType

A component that adds a hierarchical relation between parameters according to a LMM defined via MixedModels.jl

  • basis: an object, if accessed, provides a 'basis-function', e.g. hanning(40), this defines the response at a single event. It will be weighted by the model-prediction
  • formula: Formula-Object in the style of MixedModels.jl e.g. @formula 0~1+cond + (1|subject) - left-handside is ignored
  • β Vector of betas, must fit the formula
  • σs Dict of random effect variances, e.g. Dict(:subject=>[0.5,0.4]) or to specify correlationmatrix Dict(:subject=>[0.5,0.4,I(2,2)],...). Technically, this will be passed to MixedModels.jl create_re function, which creates the θ matrices.
  • contrasts: Dict in the style of MixedModels.jl. Default is empty.

All arguments can be named, in that case contrasts is optional

Works best with MultiSubjectDesign

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())
 )
-
source
UnfoldSim.MultiSubjectDesignType
MultiSubjectDesign
  • n_subjects::Int -> number of subjects
  • n_items::Int -> number of items (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 in subjects_between)
  • both_within = Dict{Symbol,Vector} -> effects completly crossed
  • event_order_function = x->x; # can be used to sort, or e.g. x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

tipp: check the resulting dataframe using generate_events(design)

# declaring same condition both sub-between and item-between results in a full between subject/item design
+
source
UnfoldSim.MultiSubjectDesignType
MultiSubjectDesign
  • n_subjects::Int -> number of subjects
  • n_items::Int -> number of items (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 in subjects_between)
  • both_within = Dict{Symbol,Vector} -> effects completly crossed
  • event_order_function = x->x; # can be used to sort, or e.g. x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

tipp: check the resulting dataframe using generate_events(design)

# declaring same condition both sub-between and item-between results in a full between subject/item design
 design = MultiSubjectDesign(;
 		n_items = 10,
 		n_subjects = 30,
 		subjects_between = Dict(:cond => ["levelA", "levelB"]),
 		items_between = Dict(:cond => ["levelA", "levelB"]),
-		);
source
UnfoldSim.MultichannelComponentType

Wrapper for an AbstractComponent to project it to multiple target-channels via projection. optional adds noise to the source prior to projection.

source
UnfoldSim.NoOnsetType
struct NoOnset <: AbstractOnset end

In the case that the user directly wants no overlap to be simulated (=> epoched data).

source
UnfoldSim.PinkNoiseType
PinkNoise <: AbstractNoise

Generate Pink Noise using the SignalAnalysis.jl implementation.

noiselevel is used to scale the noise

source
UnfoldSim.RedNoiseType
RedNoise <: AbstractNoise

Generate Red Noise using the SignalAnalysis.jl implementation.

noiselevel is used to scale the noise

source
UnfoldSim.RepeatDesignType
RepeatDesign{T}

Repeat a design DataFrame multiple times to mimick repeatedly recorded trials.

designOnce = MultiSubjectDesign(;
+		);
source
UnfoldSim.MultichannelComponentType

Wrapper for an AbstractComponent to project it to multiple target-channels via projection. optional adds noise to the source prior to projection.

source
UnfoldSim.NoOnsetType
struct NoOnset <: AbstractOnset end

In the case that the user directly wants no overlap to be simulated (=> epoched data).

source
UnfoldSim.PinkNoiseType
PinkNoise <: AbstractNoise

Generate Pink Noise using the SignalAnalysis.jl implementation.

noiselevel is used to scale the noise

source
UnfoldSim.RedNoiseType
RedNoise <: AbstractNoise

Generate Red Noise using the SignalAnalysis.jl implementation.

noiselevel is used to scale the noise

source
UnfoldSim.RepeatDesignType
RepeatDesign{T}

Repeat a design DataFrame multiple times to mimick repeatedly recorded trials.

designOnce = MultiSubjectDesign(;
 		n_items=2,
 		n_subjects = 2,
 		subjects_between =Dict(:cond=>["levelA","levelB"]),
 		items_between =Dict(:cond=>["levelA","levelB"]),
 		);
 
-design = RepeatDesign(designOnce,4);
source
UnfoldSim.SingleSubjectDesignType
  • conditions = Dict{Symbol,Vector} of conditions, e.g. Dict(:A=>["a_small","a_big"],:B=>["b_tiny","b_large"])
  • event_order_function = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

Number of trials / rows in generate_events(design) depend on the full factorial of your conditions.

To increase the number of repetitions 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.

tipp: check the resulting dataframe using generate_events(design)

source
UnfoldSim.UniformOnsetType
struct UniformOnset <: AbstractOnset

Provide a Uniform Distribution of the inter-event-distances. width is the width of the uniform distribution (=> the jitter). Since the lower bound is 0, width is also the upper bound. offset is the minimal distance. The maximal distance is offset + width.

source
UnfoldSim.SingleSubjectDesignType
  • conditions = Dict{Symbol,Vector} of conditions, e.g. Dict(:A=>["a_small","a_big"],:B=>["b_tiny","b_large"])
  • event_order_function = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

Number of trials / rows in generate_events(design) depend on the full factorial of your conditions.

To increase the number of repetitions 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.

tipp: check the resulting dataframe using generate_events(design)

source
UnfoldSim.UniformOnsetType
struct UniformOnset <: AbstractOnset

Provide a Uniform Distribution of the inter-event-distances. width is the width of the uniform distribution (=> the jitter). Since the lower bound is 0, width is also the upper bound. offset is the minimal distance. The maximal distance is offset + width.

source
UnfoldSim.WhiteNoiseType
WhiteNoise <: WhiteNoise
 noiselevel = 1
-imfilter = 0

Generate White Noise using randn - thus Gaussian noise. noiselevel is used to scale the noise

Using imfilter > 0 it is possible to smooth the noise using Image.imfilter.

source
DSP.Windows.hanningMethod

generate a hanning window

duration: in s offset: in s, defines hanning peak sfreq: sampling rate in Hz

source
UnfoldSim.add_noise!Method
add_noise!(rng, noisetype::AbstractNoise, signal)

Generate and add noise to a data matrix. Assumes that the signal can be linearized, that is, that the noise is stationary

source
UnfoldSim.add_responses!Method
add_responses!(signal, responses::Vector, e, s, tvec, erpvec)
+imfilter = 0

Generate White Noise using randn - thus Gaussian noise. noiselevel is used to scale the noise

Using imfilter > 0 it is possible to smooth the noise using Image.imfilter.

source
DSP.Windows.hanningMethod

generate a hanning window

duration: in s offset: in s, defines hanning peak sfreq: sampling rate in Hz

source
UnfoldSim.add_noise!Method
add_noise!(rng, noisetype::AbstractNoise, signal)

Generate and add noise to a data matrix. Assumes that the signal can be linearized, that is, that the noise is stationary

source
UnfoldSim.add_responses!Method
add_responses!(signal, responses::Vector, e, s, tvec, erpvec)
 add_responses!(signal, responses::Matrix, e, s, tvec, erpvec)
-add_responses!(signal, responses::AbstractArray, e, s, tvec, erpvec)

Helper function to add inplace the responses to the signal, but for both 2D (1 channel) and 3D (X channel case).

source
UnfoldSim.closest_srcMethod
closest_src(coords_list::AbstractVector{<:AbstractVector}, pos)
-closest_src(coords::Vector{<:Real}, pos)

Takes an array of 'm' target coordinate vector (size 3) (or vector of vectors) and a matrix (n-by-3) of all available positions, and returns an array of size 'm' containing the indices of the respective items in 'pos' that are nearest to each of the target coordinates.

source
UnfoldSim.closest_srcMethod
closest_src(head::Hartmut,label::String)

Returns src-ix of the Headmodel Hartmut which is closest to the average of the label.

Important

We use the average in eucledean 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.)

hartmut = headmodel()
-pos = closest_src(hartmut=>"Left Middle Temporal Gyrus, posterior division")
source
UnfoldSim.convertMethod

Obsolete - # TODO: Transfer function to Unfold.jl

Function to convert output similar to unfold (data, events)

source
UnfoldSim.create_continuous_signalMethod
create_continuous_signal(rng, responses, simulation)

Based on the responses and simulation parameters, simulate onset latencies and add together a continuous signal.

source
UnfoldSim.epochMethod
epoch(data::AbstractVector, args...; kwargs...)
+add_responses!(signal, responses::AbstractArray, e, s, tvec, erpvec)

Helper function to add inplace the responses to the signal, but for both 2D (1 channel) and 3D (X channel case).

source
UnfoldSim.closest_srcMethod
closest_src(coords_list::AbstractVector{<:AbstractVector}, pos)
+closest_src(coords::Vector{<:Real}, pos)

Takes an array of 'm' target coordinate vector (size 3) (or vector of vectors) and a matrix (n-by-3) of all available positions, and returns an array of size 'm' containing the indices of the respective items in 'pos' that are nearest to each of the target coordinates.

source
UnfoldSim.closest_srcMethod
closest_src(head::Hartmut,label::String)

Returns src-ix of the Headmodel Hartmut which is closest to the average of the label.

Important

We use the average in eucledean 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.)

hartmut = headmodel()
+pos = closest_src(hartmut=>"Left Middle Temporal Gyrus, posterior division")
source
UnfoldSim.convertMethod

Obsolete - # TODO: Transfer function to Unfold.jl

Function to convert output similar to unfold (data, events)

source
UnfoldSim.create_continuous_signalMethod
create_continuous_signal(rng, responses, simulation)

Based on the responses and simulation parameters, simulate onset latencies and add together a continuous signal.

source
UnfoldSim.epochMethod
epoch(data::AbstractVector, args...; kwargs...)
 epoch(
     data::AbstractArray{T,2},
     events,
     τ::Tuple{Number,Number},
     sfreq;
     eventtime::Symbol = :latency,
-) where {T<:Union{Missing,Number}}

Helper function to epoch data.

Adapted from Unfold.jl: https://github.com/unfoldtoolbox/Unfold.jl/blob/b3a21c2bb7e93d2f45ec64b0197f4663a6d7939a/src/utilities.jl#L40

source
UnfoldSim.generate_eventsMethod
generate_events(design::MultiSubjectDesign)

Generate full factorial Dataframe according to MixedModelsSim.jl 's simdat_crossed function. Note: n_items = you can think of it as trials or better, as stimuli.

Note: No condition can be named dv which is used internally in MixedModelsSim / MixedModels as a dummy left-side

Afterwards applies design.event_order_function`. Could be used to duplicate trials, sort, subselect etc.

Finally it sorts by :subject

julia> d = MultiSubjectDesign(;nsubjects = 10,nitems=20,bothwithin= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generateevents(d)

source
UnfoldSim.generate_eventsMethod
UnfoldSim.generate_events(design::RepeatDesign{T})

In a repeated design, iteratively calls the underlying {T} Design and concatenates. In case of MultiSubjectDesign, sorts by subject.

source
UnfoldSim.generate_eventsMethod

Generates full-factorial DataFrame of design.conditions

Afterwards applies design.eventorderfunction.

If conditions is nothing, a single trial is simulated with a column :dummy and content :dummy - this is for convenience.

julia> d = SingleSubjectDesign(;conditions= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate_events(d)

source
UnfoldSim.headmodelMethod

Load a headmodel, using Artifacts.jl automatically downloads the required files

Currently only type="hartmut" is implemented

source
UnfoldSim.hrfMethod

Generate a HRF kernel.

TR = 1/sfreq default parameters taken from SPM

Code adapted from Unfold.jl

source
UnfoldSim.magnitudeMethod

Extracts magnitude of the orientation-including leadfield.

By default uses the orientation specified in the headmodel

Fallback: along the third dimension using norm - the maximal projection

source
UnfoldSim.magnitudeMethod

magnitude(headmodel::Hartmut; type = "perpendicular") = Extract magnitude of 3-orientation-leadfield, type (default: "perpendicular") => uses the provided source-point orientations - otherwise falls back to norm.

source
UnfoldSim.magnitudeMethod
magnitude(lf::AbstractArray{T,3}) where {T<:Real}

If orientation is not specified, returns the maximal magnitude (norm of leadfield).

source
UnfoldSim.magnitudeMethod
magnitude(lf::AbstractArray{T,3}, orientation::AbstractArray{T,2}) where {T<:Real}

Return the magnitude along an orientation of the leadfield.

source
UnfoldSim.maxlengthMethod
maxlength(c::Vector{<:AbstractComponent}) = maximum(length.(c))

maximum of individual component lengths

source
UnfoldSim.n170Method
n170(;sfreq=100)

Generator for Hanning window, negative (!) peak at 170ms, width 150ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.n400Method
n400(;sfreq=100)

Generator for Hanning window, negative (!) peak at 400ms, width 400ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.n_channelsMethod
n_channels(c::MultichannelComponent)

For MultichannelComponent return the length of the projection vector.

source
UnfoldSim.n_channelsMethod

For a vector of MultichannelComponents, return the first but asserts all are of equal length.

source
UnfoldSim.p100Method
p100(;sfreq=100)

Generator for Hanning window, peak at 100ms, width 100ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.p300Method
p300(;sfreq=100)

Generator for Hanning window, peak at 300ms, width 300ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.predef_2x2Method
predef_2x2(rng::AbstractRNG;kwargs...)

The most used kwargs is: return_epoched=true which returns already epoched data. If you want epoched data without overlap, specify onset=NoOnset() and return_epoched=true

design

  • n_items=100,
  • n_subjects=1,
  • conditions = Dict(:A=>["asmall","abig"],:B=>["btiny","blarge"]),
  • event_order_function = x->shuffle(deepcopy(rng),x),

component / signal

  • signalsize = 100, length of simulated hanning window
  • basis= hanning(signalsize), the actual "function",signalsize` is only used here
  • β = [1,-0.5,.5,+1], the parameters
  • σs = Dict(:subject=>[1,0.5,0.5,0.5],:item=>[1]), - only in n_subjects>=2 case, specifies the random effects
  • contrasts = Dict(:A=>EffectsCoding(),:B=>EffectsCoding()) - effect coding by default
  • formula = n_subjects==1 ? @formula(0~1+AB) : @formula(dv~1+AB+(A*B|subject)+(1|item)),

noise

  • noiselevel = 0.2,
  • noise = PinkNoise(;noiselevel=noiselevel),

onset

  • overlap = (0.5,0.2),
  • onset=UniformOnset(;offset=signalsizeoverlap[1],width=signalsizeoverlap[2]), #put offset to 1 for no overlap. put width to 0 for no jitter

Careful if you modify nitems with nsubjects = 1, n_items has to be a multiple of 4 (or your equivalent conditions factorial, e.g. all combinations length)

source
UnfoldSim.predef_eegMethod

predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)

Generate a P1/N1/P3 complex. In case n_subjects is defined - MixedModelComponents are generated, else LinearModelComponents.

The most used kwargs is: return_epoched=true which returns already epoched data. If you want epoched data without overlap, specify onset=NoOnset() and return_epoched=true

Default params:

  • n_repeats = 100
  • eventorderfunction = x->shuffle(deepcopy(rng),x # random trial order
  • conditions = Dict(...),

component / signal

  • sfreq = 100,
  • p1 = (p100(;sfreq=sfreq), @formula(0~1),[5],Dict()), # P1 amp 5, no effects
  • n1 = (n170(;sfreq=sfreq), @formula(0~1+condition),[5,-3],Dict()), # N1 amp 5, dummycoded condition effect (levels "car", "face") of -3
  • p3 = (p300(;sfreq=sfreq), @formula(0~1+continuous),[5,1],Dict()), # P3 amp 5, continuous effect range [-5,5] with slope 1

noise

  • noiselevel = 0.2,
  • noise = PinkNoise(;noiselevel=noiselevel),

onset

  • overlap = (0.5,0.2), # offset + width/length of Uniform noise. put offset to 1 for no overlap. put width to 0 for no jitter
  • onset=UniformOnset(;offset=sfreq0.5overlap[1],width=sfreq0.5overlap[2]),
source
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.

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::AbstractComponent, simulation::Simulation)

By default call simulate_component with (::Abstractcomponent,::AbstractDesign) instead of the whole simulation. This allows users to provide a hook to do something completely different :)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::AbstractComponent, simulation::Simulation)

Generate a linear model design matrix, weight it by c.β and multiply the result with the given basis vector.

julia> c = UnfoldSim.LinearModelComponent([0,1,1,0],@formula(0~1+cond),[1,2],Dict()) julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,itemsbetween=(;:cond=>["A","B"])) julia> simulatecomponent(StableRNG(1),c,design)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::MixedModelComponent, design::AbstractDesign)

Generates a MixedModel and simulates data according to c.β and c.σs.

A trick is used to remove the Normal-Noise from the MixedModel which might lead to rare numerical instabilities. Practically, we upscale the σs by factor 10000, and provide a σ=0.0001. Internally this results in a normalization where the response scale is 10000 times larger than the noise.

Currently, it is not possible to use a different basis for fixed and random effects, but a code-stub exists (it is slow though).

  • return_parameters (Bool,false) - can be used to return the per-event parameters used to weight the basis function. Sometimes useful to see what is simulated

julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,items_between=(;:cond=>["A","B"])) julia> c = UnfoldSim.MixedModelComponent([0.,1,1,0],@formula(0~1+cond+(1|subject)),[1,2],Dict(:subject=>[2],),Dict()) julia> simulate(StableRNG(1),c,design)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng,c::MultichannelComponent,design::AbstractDesign)

Return the projection of a component from source to "sensor" space.

source
UnfoldSim.simulate_noiseMethod
simulate_noise(rng, t::Union{PinkNoise,RedNoise}, n::Int)

Generate Pink or Red Noise using the SignalAnalysis.jl implementation.

source
UnfoldSim.simulate_onsetsMethod
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.

main call from simulation

source
UnfoldSim.simulate_responsesMethod
simulate_responses(
+) where {T<:Union{Missing,Number}}

Helper function to epoch data.

Adapted from Unfold.jl: https://github.com/unfoldtoolbox/Unfold.jl/blob/b3a21c2bb7e93d2f45ec64b0197f4663a6d7939a/src/utilities.jl#L40

source
UnfoldSim.generate_eventsMethod
generate_events(design::MultiSubjectDesign)

Generate full factorial Dataframe according to MixedModelsSim.jl 's simdat_crossed function. Note: n_items = you can think of it as trials or better, as stimuli.

Note: No condition can be named dv which is used internally in MixedModelsSim / MixedModels as a dummy left-side

Afterwards applies design.event_order_function`. Could be used to duplicate trials, sort, subselect etc.

Finally it sorts by :subject

julia> d = MultiSubjectDesign(;nsubjects = 10,nitems=20,bothwithin= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generateevents(d)

source
UnfoldSim.generate_eventsMethod
UnfoldSim.generate_events(design::RepeatDesign{T})

In a repeated design, iteratively calls the underlying {T} Design and concatenates. In case of MultiSubjectDesign, sorts by subject.

source
UnfoldSim.generate_eventsMethod

Generates full-factorial DataFrame of design.conditions

Afterwards applies design.eventorderfunction.

If conditions is nothing, a single trial is simulated with a column :dummy and content :dummy - this is for convenience.

julia> d = SingleSubjectDesign(;conditions= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate_events(d)

source
UnfoldSim.headmodelMethod

Load a headmodel, using Artifacts.jl automatically downloads the required files

Currently only type="hartmut" is implemented

source
UnfoldSim.hrfMethod

Generate a HRF kernel.

TR = 1/sfreq default parameters taken from SPM

Code adapted from Unfold.jl

source
UnfoldSim.magnitudeMethod

Extracts magnitude of the orientation-including leadfield.

By default uses the orientation specified in the headmodel

Fallback: along the third dimension using norm - the maximal projection

source
UnfoldSim.magnitudeMethod

magnitude(headmodel::Hartmut; type = "perpendicular") = Extract magnitude of 3-orientation-leadfield, type (default: "perpendicular") => uses the provided source-point orientations - otherwise falls back to norm.

source
UnfoldSim.magnitudeMethod
magnitude(lf::AbstractArray{T,3}) where {T<:Real}

If orientation is not specified, returns the maximal magnitude (norm of leadfield).

source
UnfoldSim.magnitudeMethod
magnitude(lf::AbstractArray{T,3}, orientation::AbstractArray{T,2}) where {T<:Real}

Return the magnitude along an orientation of the leadfield.

source
UnfoldSim.maxlengthMethod
maxlength(c::Vector{<:AbstractComponent}) = maximum(length.(c))

maximum of individual component lengths

source
UnfoldSim.n170Method
n170(;sfreq=100)

Generator for Hanning window, negative (!) peak at 170ms, width 150ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.n400Method
n400(;sfreq=100)

Generator for Hanning window, negative (!) peak at 400ms, width 400ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.n_channelsMethod
n_channels(c::MultichannelComponent)

For MultichannelComponent return the length of the projection vector.

source
UnfoldSim.n_channelsMethod

For a vector of MultichannelComponents, return the first but asserts all are of equal length.

source
UnfoldSim.p100Method
p100(;sfreq=100)

Generator for Hanning window, peak at 100ms, width 100ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.p300Method
p300(;sfreq=100)

Generator for Hanning window, peak at 300ms, width 300ms, at kwargs sfreq (default 100). Returns a vector.

source
UnfoldSim.predef_2x2Method
predef_2x2(rng::AbstractRNG;kwargs...)

The most used kwargs is: return_epoched=true which returns already epoched data. If you want epoched data without overlap, specify onset=NoOnset() and return_epoched=true

design

  • n_items=100,
  • n_subjects=1,
  • conditions = Dict(:A=>["asmall","abig"],:B=>["btiny","blarge"]),
  • event_order_function = x->shuffle(deepcopy(rng),x),

component / signal

  • signalsize = 100, length of simulated hanning window
  • basis= hanning(signalsize), the actual "function",signalsize` is only used here
  • β = [1,-0.5,.5,+1], the parameters
  • σs = Dict(:subject=>[1,0.5,0.5,0.5],:item=>[1]), - only in n_subjects>=2 case, specifies the random effects
  • contrasts = Dict(:A=>EffectsCoding(),:B=>EffectsCoding()) - effect coding by default
  • formula = n_subjects==1 ? @formula(0~1+AB) : @formula(dv~1+AB+(A*B|subject)+(1|item)),

noise

  • noiselevel = 0.2,
  • noise = PinkNoise(;noiselevel=noiselevel),

onset

  • overlap = (0.5,0.2),
  • onset=UniformOnset(;offset=signalsizeoverlap[1],width=signalsizeoverlap[2]), #put offset to 1 for no overlap. put width to 0 for no jitter

Careful if you modify nitems with nsubjects = 1, n_items has to be a multiple of 4 (or your equivalent conditions factorial, e.g. all combinations length)

source
UnfoldSim.predef_eegMethod

predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)

Generate a P1/N1/P3 complex. In case n_subjects is defined - MixedModelComponents are generated, else LinearModelComponents.

The most used kwargs is: return_epoched=true which returns already epoched data. If you want epoched data without overlap, specify onset=NoOnset() and return_epoched=true

Default params:

  • n_repeats = 100
  • eventorderfunction = x->shuffle(deepcopy(rng),x # random trial order
  • conditions = Dict(...),

component / signal

  • sfreq = 100,
  • p1 = (p100(;sfreq=sfreq), @formula(0~1),[5],Dict()), # P1 amp 5, no effects
  • n1 = (n170(;sfreq=sfreq), @formula(0~1+condition),[5,-3],Dict()), # N1 amp 5, dummycoded condition effect (levels "car", "face") of -3
  • p3 = (p300(;sfreq=sfreq), @formula(0~1+continuous),[5,1],Dict()), # P3 amp 5, continuous effect range [-5,5] with slope 1

noise

  • noiselevel = 0.2,
  • noise = PinkNoise(;noiselevel=noiselevel),

onset

  • overlap = (0.5,0.2), # offset + width/length of Uniform noise. put offset to 1 for no overlap. put width to 0 for no jitter
  • onset=UniformOnset(;offset=sfreq0.5overlap[1],width=sfreq0.5overlap[2]),
source
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.

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::AbstractComponent, simulation::Simulation)

By default call simulate_component with (::Abstractcomponent,::AbstractDesign) instead of the whole simulation. This allows users to provide a hook to do something completely different :)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::AbstractComponent, simulation::Simulation)

Generate a linear model design matrix, weight it by c.β and multiply the result with the given basis vector.

julia> c = UnfoldSim.LinearModelComponent([0,1,1,0],@formula(0~1+cond),[1,2],Dict()) julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,itemsbetween=(;:cond=>["A","B"])) julia> simulatecomponent(StableRNG(1),c,design)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng, c::MixedModelComponent, design::AbstractDesign)

Generates a MixedModel and simulates data according to c.β and c.σs.

A trick is used to remove the Normal-Noise from the MixedModel which might lead to rare numerical instabilities. Practically, we upscale the σs by factor 10000, and provide a σ=0.0001. Internally this results in a normalization where the response scale is 10000 times larger than the noise.

Currently, it is not possible to use a different basis for fixed and random effects, but a code-stub exists (it is slow though).

  • return_parameters (Bool,false) - can be used to return the per-event parameters used to weight the basis function. Sometimes useful to see what is simulated

julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,items_between=(;:cond=>["A","B"])) julia> c = UnfoldSim.MixedModelComponent([0.,1,1,0],@formula(0~1+cond+(1|subject)),[1,2],Dict(:subject=>[2],),Dict()) julia> simulate(StableRNG(1),c,design)

source
UnfoldSim.simulate_componentMethod
simulate_component(rng,c::MultichannelComponent,design::AbstractDesign)

Return the projection of a component from source to "sensor" space.

source
UnfoldSim.simulate_noiseMethod
simulate_noise(rng, t::Union{PinkNoise,RedNoise}, n::Int)

Generate Pink or Red Noise using the SignalAnalysis.jl implementation.

source
UnfoldSim.simulate_onsetsMethod
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.

main call from simulation

source
UnfoldSim.simulate_responsesMethod
simulate_responses(
     rng,
     components::Vector{<:AbstractComponent},
-    simulation::Simulation)

Simulate multiple component responses and accumulates them on a per-event basis.

source
UnfoldSim.weight_σsMethod

Weights a σs Dict for MixedModels.jl by a Float64

Finally sales it by σ_lmm, as a trick to simulate noise-free LMMs

I anticipate a function function weight_σs(σs::Dict,b_σs::Dict,σ_lmm::Float64) where each σs entry can be weighted individually

source
+ simulation::Simulation)

Simulate multiple component responses and accumulates them on a per-event basis.

source
UnfoldSim.weight_σsMethod

Weights a σs Dict for MixedModels.jl by a Float64

Finally sales it by σ_lmm, as a trick to simulate noise-free LMMs

I anticipate a function function weight_σs(σs::Dict,b_σs::Dict,σ_lmm::Float64) where each σs entry can be weighted individually

source
diff --git a/dev/generated/HowTo/multichannel/index.html b/dev/generated/HowTo/multichannel/index.html index a366cd8..71ac8ec 100644 --- a/dev/generated/HowTo/multichannel/index.html +++ b/dev/generated/HowTo/multichannel/index.html @@ -42,4 +42,4 @@ visual = (; enlarge = 0.5, label_scatter = false), axis = (; limits = ((0, 1), (0, 0.9))), ) -fExample block output

This page was generated using Literate.jl.

+fExample block output

This page was generated using Literate.jl.

diff --git a/dev/generated/HowTo/newComponent/844d1bfe.png b/dev/generated/HowTo/newComponent/844d1bfe.png new file mode 100644 index 0000000..8e2f988 Binary files /dev/null and b/dev/generated/HowTo/newComponent/844d1bfe.png differ diff --git a/dev/generated/HowTo/newComponent/bce60ac1.png b/dev/generated/HowTo/newComponent/bce60ac1.png deleted file mode 100644 index af247d6..0000000 Binary files a/dev/generated/HowTo/newComponent/bce60ac1.png and /dev/null differ diff --git a/dev/generated/HowTo/newComponent/index.html b/dev/generated/HowTo/newComponent/index.html index 0424b20..be5a167 100644 --- a/dev/generated/HowTo/newComponent/index.html +++ b/dev/generated/HowTo/newComponent/index.html @@ -41,4 +41,4 @@ TimeVaryingComponent(basis_shiftduration, 50), design, ) -plot_erpimage(hcat(erp...), sortvalues = generate_events(design).shift)Example block output

This page was generated using Literate.jl.

+plot_erpimage(hcat(erp...), sortvalues = generate_events(design).shift)Example block output

This page was generated using Literate.jl.

diff --git a/dev/generated/HowTo/newDesign/index.html b/dev/generated/HowTo/newDesign/index.html index 8aa7f99..2ea1633 100644 --- a/dev/generated/HowTo/newDesign/index.html +++ b/dev/generated/HowTo/newDesign/index.html @@ -13,4 +13,4 @@ levels = vcat(repeat(["levelA"], nA), repeat(["levelB"], nB)) return DataFrame(Dict(:condition => levels)) end;

Finally, we can test the function and see whether it returns a Design-DataFrame as we requested

design = ImbalanceSubjectDesign(; nTrials = 6, balance = 0.2)
-generate_events(design)
6×1 DataFrame
Rowcondition
String
1levelA
2levelB
3levelB
4levelB
5levelB
6levelB
Important

It is the users task to ensure that each run is reproducible. So if you have a random process (e.g. shuffling), be sure to safe a RNG object in your struct and use it in your generate function.


This page was generated using Literate.jl.

+generate_events(design)
6×1 DataFrame
Rowcondition
String
1levelA
2levelB
3levelB
4levelB
5levelB
6levelB
Important

It is the users task to ensure that each run is reproducible. So if you have a random process (e.g. shuffling), be sure to safe a RNG object in your struct and use it in your generate function.


This page was generated using Literate.jl.

diff --git a/dev/generated/HowTo/predefinedData/index.html b/dev/generated/HowTo/predefinedData/index.html index 0c51397..2359c59 100644 --- a/dev/generated/HowTo/predefinedData/index.html +++ b/dev/generated/HowTo/predefinedData/index.html @@ -21,4 +21,4 @@ generate_events(simulation.design).latency
Hint

This is a bit of a trick, it relies that MyManualOnset is always used in combination with MyManualDesign. You could of course repeat the structure from MyManualDesign also for MyManualOnset and have an explicit field in the structure containing the onsets.

And that's it

data, events = simulate(MersenneTwister(1), mydesign, signal, MyManualOnset())
 lines(data) # plotting
 vlines!(my_events.latency, linestyle = :dash)
-current_figure()
Example block output

now everything matches, lovely!


This page was generated using Literate.jl.

+current_figure()Example block output

now everything matches, lovely!


This page was generated using Literate.jl.

diff --git a/dev/generated/HowTo/repeatTrials/index.html b/dev/generated/HowTo/repeatTrials/index.html index 3438efa..336ca3f 100644 --- a/dev/generated/HowTo/repeatTrials/index.html +++ b/dev/generated/HowTo/repeatTrials/index.html @@ -7,4 +7,4 @@ ); design = RepeatDesign(designOnce, 4); -generate_events(design)
8×3 DataFrame
Rowsubjectconditem
StringStringString
1S1levelAI1
2S1levelAI1
3S1levelAI1
4S1levelAI1
5S2levelBI2
6S2levelBI2
7S2levelBI2
8S2levelBI2

As you can see, the design was simply repeated.

Note

If you implemented your own AbstractDesign, you need to define the size function accordingly. E.g.: Base.size(design::RepeatDesign{SingleSubjectDesign}) = size(design.design).*design.repeat


This page was generated using Literate.jl.

+generate_events(design)
8×3 DataFrame
Rowsubjectconditem
StringStringString
1S1levelAI1
2S1levelAI1
3S1levelAI1
4S1levelAI1
5S2levelBI2
6S2levelBI2
7S2levelBI2
8S2levelBI2

As you can see, the design was simply repeated.

Note

If you implemented your own AbstractDesign, you need to define the size function accordingly. E.g.: Base.size(design::RepeatDesign{SingleSubjectDesign}) = size(design.design).*design.repeat


This page was generated using Literate.jl.

diff --git a/dev/generated/reference/basistypes/index.html b/dev/generated/reference/basistypes/index.html index 8d5b5ea..56670fa 100644 --- a/dev/generated/reference/basistypes/index.html +++ b/dev/generated/reference/basistypes/index.html @@ -44,4 +44,4 @@ axislegend(string(cfg[1]); merge = true) end -fExample block output

This page was generated using Literate.jl.

+fExample block output

This page was generated using Literate.jl.

diff --git a/dev/generated/reference/noisetypes/index.html b/dev/generated/reference/noisetypes/index.html index 4125121..03d1550 100644 --- a/dev/generated/reference/noisetypes/index.html +++ b/dev/generated/reference/noisetypes/index.html @@ -41,4 +41,4 @@ end f[1, 4] = Legend(f, ax_sig, "Noise type", tellheight = true) -fExample block output
Hint

We recommed for smaller signals the ExponentialNoise, maybe with a removed DC offset or a HighPass filter. For long signals, this Noise requires lots of memory though. maybe Pinknoise is a better choice then.


This page was generated using Literate.jl.

+fExample block output
Hint

We recommed for smaller signals the ExponentialNoise, maybe with a removed DC offset or a HighPass filter. For long signals, this Noise requires lots of memory though. maybe Pinknoise is a better choice then.


This page was generated using Literate.jl.

diff --git a/dev/generated/reference/onsettypes/index.html b/dev/generated/reference/onsettypes/index.html index f290553..51d703d 100644 --- a/dev/generated/reference/onsettypes/index.html +++ b/dev/generated/reference/onsettypes/index.html @@ -109,4 +109,4 @@ end axes_list[end].xlabel = "Time between events [samples]" linkyaxes!(axes_list...) -end

Overlap of subsequent events

Note

The overlap of subsequent events can be indirectly controlled by setting the offset parameter relative to the length of the component basis. Assuming that signal is a component e.g. LinearModelComponent,


This page was generated using Literate.jl.

+end

Overlap of subsequent events

Note

The overlap of subsequent events can be indirectly controlled by setting the offset parameter relative to the length of the component basis. Assuming that signal is a component e.g. LinearModelComponent,


This page was generated using Literate.jl.

diff --git a/dev/generated/reference/overview/index.html b/dev/generated/reference/overview/index.html index c9caa4a..1ec6713 100644 --- a/dev/generated/reference/overview/index.html +++ b/dev/generated/reference/overview/index.html @@ -16,4 +16,4 @@ RedNoise UnfoldSim.AutoRegressiveNoise UnfoldSim.RealisticNoise - WhiteNoise

This page was generated using Literate.jl.

+ WhiteNoise

This page was generated using Literate.jl.

diff --git a/dev/generated/tutorials/multisubject/index.html b/dev/generated/tutorials/multisubject/index.html index 98180b2..3b011e7 100644 --- a/dev/generated/tutorials/multisubject/index.html +++ b/dev/generated/tutorials/multisubject/index.html @@ -64,4 +64,4 @@ data, times, ) -plot_erp(coeftable(m), mapping = (; col = :group))Example block output

The first column shows the fixed effects, the latter the item and subject random effects as they evolve across time


This page was generated using Literate.jl.

+plot_erp(coeftable(m), mapping = (; col = :group))Example block output

The first column shows the fixed effects, the latter the item and subject random effects as they evolve across time


This page was generated using Literate.jl.

diff --git a/dev/generated/tutorials/poweranalysis/index.html b/dev/generated/tutorials/poweranalysis/index.html index b65dcab..cc08e93 100644 --- a/dev/generated/tutorials/poweranalysis/index.html +++ b/dev/generated/tutorials/poweranalysis/index.html @@ -25,4 +25,4 @@ # calculate a one-sided t-test pvals[seed] = pvalue(OneSampleTTest(y_big, y_small)) -end
  8.622222 seconds (16.43 M allocations: 2.853 GiB, 2.38% gc time, 83.94% compilation time: 12% of which was recompilation)

let's calculate the power

power = mean(pvals .< 0.05) * 100
67.0

This page was generated using Literate.jl.

+end
  8.441411 seconds (16.43 M allocations: 2.853 GiB, 2.32% gc time, 83.45% compilation time: 12% of which was recompilation)

let's calculate the power

power = mean(pvals .< 0.05) * 100
67.0

This page was generated using Literate.jl.

diff --git a/dev/generated/tutorials/quickstart/index.html b/dev/generated/tutorials/quickstart/index.html index d4cff89..816e4db 100644 --- a/dev/generated/tutorials/quickstart/index.html +++ b/dev/generated/tutorials/quickstart/index.html @@ -9,4 +9,4 @@ β = [1, 0.5], );

Onsets and Noise

We will start with a uniform (but overlapping, offset < length(signal.basis)) onset-distribution

onset = UniformOnset(; width = 20, offset = 4);

And we will use some noise

noise = PinkNoise(; noiselevel = 0.2);

Combine & Generate

Finally, we will simulate some data

data, events = simulate(MersenneTwister(1), design, signal, onset, noise);

Data is a n-sample Vector (but could be a Matrix for e.g. MultiSubjectDesign).

Events is a DataFrame that contains a column latency with the onsets of events.

Plot them!

lines(data; color = "black")
 vlines!(events.latency; color = ["orange", "teal"][1 .+ (events.condA.=="levelB")])
-current_figure()
Example block output

This page was generated using Literate.jl.

+current_figure()Example block output

This page was generated using Literate.jl.

diff --git a/dev/generated/tutorials/simulateERP/index.html b/dev/generated/tutorials/simulateERP/index.html index 1735030..4e85080 100644 --- a/dev/generated/tutorials/simulateERP/index.html +++ b/dev/generated/tutorials/simulateERP/index.html @@ -35,7 +35,7 @@ evts, data, );
┌ Warning: using `Dict(:A=>(@formula,times/basisfunction))` is deprecated, please use `[:A=>(@formula,times/basisfunction)]` from now on
-@ Unfold ~/.julia/packages/Unfold/UM7Tq/src/fit.jl:53

first the "pure" beta/linear regression parameters

plot_erp(
+@ Unfold ~/.julia/packages/Unfold/rCrkZ/src/fit.jl:53

first the "pure" beta/linear regression parameters

plot_erp(
     coeftable(m);
     axis = (
         title = "Estimated regression parameters",
@@ -57,4 +57,4 @@
 # Workaround to separate legend and colorbar (will be fixed in a future UnfoldMakie version)
 legend = f.content[2]
 f[:, 1] = legend
-current_figure()
Example block output

This page was generated using Literate.jl.

+current_figure()Example block output

This page was generated using Literate.jl.

diff --git a/dev/index.html b/dev/index.html index 4fe6566..980aabc 100644 --- a/dev/index.html +++ b/dev/index.html @@ -8,4 +8,4 @@ vlines!(evts.latency; color = ["orange", "teal"][1 .+ (evts.condition.=="car")]) current_figure()Example block output

Or simulate epoched data directly

data, evts = UnfoldSim.predef_eeg(; n_repeats = 20, noiselevel = 0.8, return_epoched = true)
-heatmap(data[:, sortperm(evts, [:condition, :continuous])])
Example block output +heatmap(data[:, sortperm(evts, [:condition, :continuous])])Example block output