diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 9c172c9..7c8a8fd 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.9.4","generation_timestamp":"2023-11-15T14:16:48","documenter_version":"1.1.2"}} \ No newline at end of file +{"documenter":{"julia_version":"1.9.4","generation_timestamp":"2023-11-20T19:48:23","documenter_version":"1.1.2"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 10448f9..68176f0 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -5,26 +5,26 @@ β = [1.,2.], contrasts=Dict(:cond=>EffectsCoding()) ) -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.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
  • n_subjects::Int -> number of subjects
  • n_items::Int -> number of items (sometimes ≈trials)
  • subjects_between = nothing -> effects between subjects, e.g. young vs old
  • items_between = nothing -> effects between items, e.g. natural vs artificial images, but shown to all subjects
  • both_within = nothing -> effects completly crossed
  • tableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

tipp: check the resulting dataframe using generate(design)

# declaring same condition both sub-between and item-between results in a full between subject/item design
+
source
UnfoldSim.MultiSubjectDesignType
  • n_subjects::Int -> number of subjects
  • n_items::Int -> number of items (sometimes ≈trials)
  • subjects_between = nothing -> effects between subjects, e.g. young vs old
  • items_between = nothing -> effects between items, e.g. natural vs artificial images, but shown to all subjects
  • both_within = nothing -> effects completly crossed
  • tableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!

tipp: check the resulting dataframe using generate(design)

# declaring same condition both sub-between and item-between results in a full between subject/item design
 design = MultiSubjectDesignjectDesign(;
         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.RepeatDesignType

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.RepeatDesignType

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 of conditions, e.g. Dict(:A=>["a_small","a_big"],:B=>["b_tiny","b_large"])
  • tableModifyFun = 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(design) depend on the full factorial of your conditions.

To increase the number of repetitions simply use RepeatDesign(SingleSubjectDesign(...),5)

tipp: check the resulting dataframe using generate(design)

source
Base.sizeMethod

Returns dimension of experiment design

source
DSP.Windows.hanningMethod

generate a hanning window

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

source
UnfoldSim.adderp!Method

Helper function to add inplace the erps to the EEG, 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

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

source
UnfoldSim.gen_noiseMethod
gen_noise(t::RealisticNoise, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.gen_noiseMethod
gen_noise(t::Union{PinkNoise, RedNoise}, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.gen_noiseMethod
gen_noise(t::WhiteNoise, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.generateMethod

Generates full factorial Dataframe according to MixedModelsSim.jl 's simdatcrossed function Note: nitems = 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 expdesign.tableModifyFun. Could be used to duplicate trials, sort, subselect etc.

Finally it sorts by :subject

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

source
UnfoldSim.generateMethod

Generates full-factorial DataFrame of expdesign.conditions

Afterwards applies expdesign.tableModifyFun.

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

source
UnfoldSim.hartmut_citationMethod

Returns citation-string for HArtMuT

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.leadfieldMethod

Returns the leadfield

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

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

source
UnfoldSim.n_channelsMethod

Returns the number of channels. By default = 1

source
UnfoldSim.n_channelsMethod

for MultichannelComponent returns the length of the projection vector

source
UnfoldSim.padarrayMethod

Pads array with specified value, length padarray(arr, len, val)

source
UnfoldSim.predef_2x2Method

todo

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

Generates a P1/N1/P3 complex. predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)

In case of n_subjects, MixedModelComponents are generated

Default params: n_repeats=100 tableModifyFun = 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.predef_eegMethod

predefeeg(rng,nsubjects;kwargs...) Runs predefeeg(rng;kwargs...) nsubject times and concatenates the results.

source
UnfoldSim.simulateMethod

by default call simulate with ::Abstractcomponent,::AbstractDesign`, but allow for custom types

making use of other information in simulation

source
UnfoldSim.simulateMethod

simulate a linearModel

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

source
UnfoldSim.simulateMethod

simulate MixedModelComponent

julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_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.simulateMethod

Simulates erp data given the specified parameters

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
+design = RepeatDesign(designOnce,4);source
UnfoldSim.SingleSubjectDesignType
  • conditions = Dict of conditions, e.g. Dict(:A=>["a_small","a_big"],:B=>["b_tiny","b_large"])
  • tableModifyFun = 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(design) depend on the full factorial of your conditions.

To increase the number of repetitions simply use RepeatDesign(SingleSubjectDesign(...),5)

tipp: check the resulting dataframe using generate(design)

source
Base.sizeMethod

Returns dimension of experiment design

source
DSP.Windows.hanningMethod

generate a hanning window

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

source
UnfoldSim.adderp!Method

Helper function to add inplace the erps to the EEG, 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

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

source
UnfoldSim.gen_noiseMethod
gen_noise(t::RealisticNoise, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.gen_noiseMethod
gen_noise(t::Union{PinkNoise, RedNoise}, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.gen_noiseMethod
gen_noise(t::WhiteNoise, n::Int)

Generate noise of a given type t and length n

source
UnfoldSim.generateMethod

Generates full factorial Dataframe according to MixedModelsSim.jl 's simdatcrossed function Note: nitems = 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 expdesign.tableModifyFun. Could be used to duplicate trials, sort, subselect etc.

Finally it sorts by :subject

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

source
UnfoldSim.generateMethod

Generates full-factorial DataFrame of expdesign.conditions

Afterwards applies expdesign.tableModifyFun.

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

source
UnfoldSim.hartmut_citationMethod

Returns citation-string for HArtMuT

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.leadfieldMethod

Returns the leadfield

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

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

source
UnfoldSim.n_channelsMethod

Returns the number of channels. By default = 1

source
UnfoldSim.n_channelsMethod

for MultichannelComponent returns the length of the projection vector

source
UnfoldSim.padarrayMethod

Pads array with specified value, length padarray(arr, len, val)

source
UnfoldSim.predef_2x2Method

todo

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

Generates a P1/N1/P3 complex. predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)

In case of n_subjects, MixedModelComponents are generated

Default params: n_repeats=100 tableModifyFun = 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.predef_eegMethod

predefeeg(rng,nsubjects;kwargs...) Runs predefeeg(rng;kwargs...) nsubject times and concatenates the results.

source
UnfoldSim.simulateMethod

by default call simulate with ::Abstractcomponent,::AbstractDesign`, but allow for custom types

making use of other information in simulation

source
UnfoldSim.simulateMethod

simulate a linearModel

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

source
UnfoldSim.simulateMethod

simulate MixedModelComponent

julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_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.simulateMethod

Simulates erp data given the specified parameters

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 b10a70d..211f55b 100644 --- a/dev/generated/HowTo/multichannel/index.html +++ b/dev/generated/HowTo/multichannel/index.html @@ -1,15 +1,13 @@ -Multi Channel Data · UnfoldSim.jl
using UnfoldSim
+Multi Channel Data · UnfoldSim.jl
using UnfoldSim
 using UnfoldMakie
 using CairoMakie
 using DataFrames
-using Random
-
-# Specifcy design

We are using a one-level design for testing here.

design = SingleSubjectDesign(conditions=Dict(:condA=>["levelA"]))
SingleSubjectDesign
+using Random

Specify design

We are using a one-level design for testing here.

design = SingleSubjectDesign(conditions=Dict(:condA=>["levelA"]))
SingleSubjectDesign
   conditions: Dict{Symbol, Vector{String}}
   tableModifyFun: #10 (function of type UnfoldSim.var"#10#14")
 

Next we generate two simple components at two different times without any formula attached (we have a single condition anyway)

c = LinearModelComponent(;basis=p100(),formula = @formula(0~1),β = [1]);
-c2 = LinearModelComponent(;basis=p300(),formula = @formula(0~1),β = [1]);

next similar to the nested design above, we can nest the component in a MultichannelComponent. We could either provide the projection marix manually, e.g.:

mc = UnfoldSim.MultichannelComponent(c, [1,2,-1,3,5,2.3,1])
MultichannelComponent
+c2 = LinearModelComponent(;basis=p300(),formula = @formula(0~1),β = [1]);

next similar to the nested design above, we can nest the component in a MultichannelComponent. We could either provide the projection matrix manually, e.g.:

mc = UnfoldSim.MultichannelComponent(c, [1,2,-1,3,5,2.3,1])
MultichannelComponent
   component: LinearModelComponent
   projection: Array{Float64}((7,)) [1.0, 2.0, -1.0, 3.0, 5.0, 2.3, 1.0]
   noise: NoNoise NoNoise()
@@ -19,38 +17,13 @@
   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()
-
Hint

You could also specify a noise-specific component which is applied prior to projection & summing with other components

finally we need to define the onsets of the signal

onset = UniformOnset(;width=20,offset=4);
-
-# Simulation + Plotting
UniformOnset
-  width: Int64 20
-  offset: Int64 4
-

Now as usual we simulate data. Inspecting data shows our result is now indeed ~230 Electrodes large! Nice!

data,events = simulate(MersenneTwister(1),design, [mc,mc2],  onset, NoNoise())
-size(data)
(227, 62)

Let's plot using Butterfly & Topoplot first we convert the electrodes to positions usable in TopoPlots.jl

pos3d = hart.electrodes["pos"]
+
Info

You could also specify a noise-specific component which is applied prior to projection & summing with other components.

finally we need to define the onsets of the signal

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

Simulation + Plotting

Now as usual we simulate data. Inspecting data shows our result is now indeed ~230 Electrodes large! Nice!

data,events = simulate(MersenneTwister(1),design, [mc,mc2],  onset, NoNoise())
+size(data)
(227, 62)

Let's plot using Butterfly & Topoplot: first we convert the electrodes to positions usable in TopoPlots.jl

pos3d = hart.electrodes["pos"];
 
-pos2d = to_positions(pos3d')
-pos2d = [Point2f(p[1]+0.5,p[2]+0.5) for p in pos2d]
227-element Vector{GeometryBasics.Point{2, Float32}}:
- [0.40898308, 0.688676]
- [0.4295677, 0.6893123]
- [0.59101695, 0.688676]
- [0.5704323, 0.6893123]
- [0.38786337, 0.6868818]
- [0.6121366, 0.6868818]
- [0.3344946, 0.6886467]
- [0.6655054, 0.6886467]
- [0.4722479, 0.6547124]
- [0.5277521, 0.6547124]
- ⋮
- [0.25054252, 0.45768857]
- [0.7494575, 0.45768857]
- [0.25292397, 0.37572253]
- [0.74707603, 0.37572253]
- [0.28807896, 0.34849578]
- [0.71192104, 0.34849578]
- [0.27310896, 0.4239325]
- [0.72689104, 0.4239325]
- [0.5, 0.10441694]

let's plot!

f = Figure()
+pos2d = to_positions(pos3d');
+pos2d = [Point2f(p[1]+0.5,p[2]+0.5) for p in pos2d];

now plot!

f = Figure()
 df = DataFrame(:estimate => data[:],:channel => repeat(1:size(data,1),outer=size(data,2)),:time => repeat(1:size(data,2),inner=size(data,1)))
 plot_butterfly!(f[1,1:2],df;positions=pos2d)
 plot_topoplot!(f[2,1],df[df.time .== 28,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))
 plot_topoplot!(f[2,2],df[df.time .== 48,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))
-f
Example block output

This page was generated using Literate.jl.

+f
Example block output

This page was generated using Literate.jl.

diff --git a/dev/generated/HowTo/newComponent/08f54f01.png b/dev/generated/HowTo/newComponent/08f54f01.png deleted file mode 100644 index ab07bf7..0000000 Binary files a/dev/generated/HowTo/newComponent/08f54f01.png and /dev/null differ diff --git a/dev/generated/HowTo/newComponent/faaca575.png b/dev/generated/HowTo/newComponent/faaca575.png new file mode 100644 index 0000000..ce6f426 Binary files /dev/null and b/dev/generated/HowTo/newComponent/faaca575.png differ diff --git a/dev/generated/HowTo/newComponent/index.html b/dev/generated/HowTo/newComponent/index.html index 8e1a4ae..05c50ac 100644 --- a/dev/generated/HowTo/newComponent/index.html +++ b/dev/generated/HowTo/newComponent/index.html @@ -1,16 +1,11 @@ -New Duration/Shift-dependent Component · UnfoldSim.jl

New component: Duration + Shift

We want a new component that changes it's duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases

begin
-	using UnfoldSim
-    using Unfold
-	using Random
-	using DSP
-    using CairoMakie
-end
-
-
-sfreq = 100
+New Duration/Shift-dependent Component · UnfoldSim.jl

New component: Duration + Shift

We want a new component that changes its duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases

using UnfoldSim
+using Unfold
+using Random
+using DSP
+using CairoMakie
 
-# Design
100

Let's genrate a design with two columns, shift + duration

design = UnfoldSim.SingleSubjectDesign(;conditions= Dict(
+sfreq = 100;

Design

Let's generate a design with two columns, shift + duration

design = UnfoldSim.SingleSubjectDesign(;conditions= Dict(
             :shift => rand(100).*sfreq/5,
             :duration=>20 .+rand(100).*sfreq/5))
SingleSubjectDesign
   conditions: Dict{Symbol, Vector{Float64}}
@@ -18,7 +13,7 @@
 

We also need a new AbstractComponent

struct TimeVaryingComponent <: AbstractComponent
     basisfunction
     maxlength
-end

we have to define the length of a component

Base.length(c::TimeVaryingComponent) = length(c.maxlength)

While we could have put the TimeVaryingComponent.basisfunction directly into the simulate function, I thought this is a bit more modular

function UnfoldSim.simulate(rng,c::TimeVaryingComponent,design::AbstractDesign)
+end

We have to define the length of a component

Base.length(c::TimeVaryingComponent) = length(c.maxlength)

While we could have put the TimeVaryingComponent.basisfunction directly into the simulate function, I thought this is a bit more modular

function UnfoldSim.simulate(rng,c::TimeVaryingComponent,design::AbstractDesign)
     evts = generate(design)
     return c.basisfunction(evts,c.maxlength)
 end

finally, the actual function that does the shifting + duration

function basis_shiftduration(evts,maxlength)
@@ -39,4 +34,4 @@
 
 
 erp = UnfoldSim.simulate(MersenneTwister(1),TimeVaryingComponent(basis_shiftduration,50),design)
-heatmap(hcat(erp...))
Example block output

This page was generated using Literate.jl.

+heatmap(hcat(erp...))
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 2bbef0b..a00b436 100644 --- a/dev/generated/HowTo/newDesign/index.html +++ b/dev/generated/HowTo/newDesign/index.html @@ -2,10 +2,10 @@ New Experimental Design · UnfoldSim.jl
using UnfoldSim
 using StableRNGs
 using DataFrames
-using Parameters

Define a new Design

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

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

1) type

We need a ImbalanceSubjectDesign struct. You are free to implement it as you wish, as long as the other two functions are implemented

@with_kw struct ImbalanceSubjectDesign <: UnfoldSim.AbstractDesign
+using Parameters

Define a new Design

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

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

1) type

We need a ImbalanceSubjectDesign struct. You are free to implement it as you wish, as long as the other two functions are implemented

@with_kw struct ImbalanceSubjectDesign <: UnfoldSim.AbstractDesign
     nTrials::Int
     balance::Float64 = 0.5 # default balanced
-end;

2) size

we need a size(design::ImbalanceSubjectDesign) function to tell how many events we will have. This is used at different places, e.g. in the Default onset implementation

# note the trailling , to make it a Tuple
+end;

2) size

we need a size(design::ImbalanceSubjectDesign) function to tell how many events we will have. This is used at different places, e.g. in the Default onset implementation

# note the trailing , to make it a Tuple
 size(design::ImbalanceSubjectDesign) = (design.nTrials,);

3) generate

We need a type generate(design::ImbalanceSubjectDesign) function. This function should return the actual table as a DataFrame

function generate(design::ImbalanceSubjectDesign)
     nA = Int(round.(design.nTrials .* design.balance))
     nB = Int(round.(design.nTrials .* (1-design.balance)))
@@ -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(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(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/repeatTrials/index.html b/dev/generated/HowTo/repeatTrials/index.html index 876db26..a9caa02 100644 --- a/dev/generated/HowTo/repeatTrials/index.html +++ b/dev/generated/HowTo/repeatTrials/index.html @@ -1,10 +1,10 @@ -Repeating Trials within a Design · UnfoldSim.jl
using UnfoldSim

Repeating Design entries

Sometimes we want to repeat a design, that is, have multiple trials with identical values, but it is not always straight forward to implement For instance, there is no way to easily modify MultiSubjectDesign to have multiple identical subject/item combinations, without doing awkward repetitions of condition-levels or something.

If you struggle with this problem RepeatDesign is an easy tool for you

designOnce = MultiSubjectDesign(;
-        n_items=2,
-		n_subjects = 2,
-        subjects_between =Dict(:cond=>["levelA","levelB"]),
-		items_between =Dict(:cond=>["levelA","levelB"]),
-        );
+Repeating Trials within a Design · UnfoldSim.jl
using UnfoldSim

Repeating Design entries

Sometimes we want to repeat a design, that is, have multiple trials with identical values, but it is not always straight forward to implement. For instance, there is no way to easily modify MultiSubjectDesign to have multiple identical subject/item combinations, without doing awkward repetitions of condition-levels or something.

If you struggle with this problem RepeatDesign is an easy tool for you:

designOnce = MultiSubjectDesign(;
+    n_items=2,
+    n_subjects = 2,
+    subjects_between =Dict(:cond=>["levelA","levelB"]),
+    items_between =Dict(:cond=>["levelA","levelB"]),
+);
 
 design = RepeatDesign(designOnce,4);
-generate(design)
16×3 DataFrame
Rowsubjectconditem
StringStringString
1S1levelAI1
2S1levelBI2
3S1levelAI1
4S1levelBI2
5S1levelAI1
6S1levelBI2
7S1levelAI1
8S1levelBI2
9S2levelAI1
10S2levelBI2
11S2levelAI1
12S2levelBI2
13S2levelAI1
14S2levelBI2
15S2levelAI1
16S2levelBI2

As you can see, the design was simply repeated. As always, you can ignore the dv column, it is for internal consistency with MixedModelsSim.jl

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(design)
16×3 DataFrame
Rowsubjectconditem
StringStringString
1S1levelAI1
2S1levelBI2
3S1levelAI1
4S1levelBI2
5S1levelAI1
6S1levelBI2
7S1levelAI1
8S1levelBI2
9S2levelAI1
10S2levelBI2
11S2levelAI1
12S2levelBI2
13S2levelAI1
14S2levelBI2
15S2levelAI1
16S2levelBI2

As you can see, the design was simply repeated. As always, you can ignore the dv column, it is for internal consistency with MixedModelsSim.jl

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 15694f3..89373c3 100644 --- a/dev/generated/reference/basistypes/index.html +++ b/dev/generated/reference/basistypes/index.html @@ -1,9 +1,8 @@ -ComponentBasisTypes · UnfoldSim.jl
using UnfoldSim
+ComponentBasisTypes · UnfoldSim.jl
using UnfoldSim
 using CairoMakie
 using DSP
-using StableRNGs
-#

Basistypes

There are several bases types directly implemented. They can be easily used for the components.

Note

You can use any arbitrary shape defined by yourself! We often make use of hanning(50) from the DSP.jl package

EEG

by default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(;sfreq=300)

f = Figure()
+using StableRNGs

Basistypes

There are several basis types directly implemented. They can be easily used for the components.

Note

You can use any arbitrary shape defined by yourself! We often make use of hanning(50) from the DSP.jl package.

EEG

By default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(;sfreq=300)

f = Figure()
 ax = f[1,1] = Axis(f)
 for b in [p100,n170,p300,n400]
     lines!(ax,b(),label=string(b))
@@ -32,9 +31,7 @@
 
     axislegend(string(cfg[1]);merge=true,)
 end
-f
-
-# pupil
Example block output

we use the simplified PuRF from Hoeks & Levelt, 1993. Note that https://www.science.org/doi/10.1126/sciadv.abi9979 show some evidence in their supplementary material, that the convolution model is not fully applicable.

f = Figure()
+f
Example block output

Pupil

We use the simplified PuRF from Hoeks & Levelt, 1993. Note that https://www.science.org/doi/10.1126/sciadv.abi9979 show some evidence in their supplementary material, that the convolution model is not fully applicable.

f = Figure()
 plotConfig = (:n=>5:3:15,
              :tmax=>0.5:0.2:1.1,
              )
@@ -48,4 +45,4 @@
 
     axislegend(string(cfg[1]);merge=true,)
 end
-f
Example block output

This page was generated using Literate.jl.

+f
Example 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 3906fc1..c877c2f 100644 --- a/dev/generated/reference/noisetypes/index.html +++ b/dev/generated/reference/noisetypes/index.html @@ -3,7 +3,7 @@ using CairoMakie using DSP using StableRNGs -import StatsBase.autocor

What's the noise?

There are several noise-types directly implemented. Here is a comparison

f = Figure()
+import StatsBase.autocor

What's the noise?

There are several noise-types directly implemented. Here is a comparison:

f = Figure()
 ax_sig = f[1,1:2] = Axis(f;title="1.000 samples of noise")
 ax_spec = f[2,1] = Axis(f;title="Welch Periodigram")
 ax_auto = f[2,2] = Axis(f;title="Autocorrelogram (every 10th lag)")
@@ -27,4 +27,4 @@
 
 end
 f[1:2,3] = Legend(f,ax_sig,"NoiseType")
-f
Example block output

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


This page was generated using Literate.jl.

+fExample block output
Hint

We recommend 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/overview/index.html b/dev/generated/reference/overview/index.html index b05c0c2..f1aaf0b 100644 --- a/dev/generated/reference/overview/index.html +++ b/dev/generated/reference/overview/index.html @@ -1,12 +1,12 @@ -Toolbox Overview · UnfoldSim.jl

Overview of functionality

UnfoldSim has many modules, here we try to collect them to provide you with an overview

using UnfoldSim
+Toolbox Overview · UnfoldSim.jl

Overview of functionality

UnfoldSim has many modules, here we try to collect them to provide you with an overview.

using UnfoldSim
 using InteractiveUtils

Design

Designs define the experimental design. They can be nested, e.g. RepeatDesign(SingleSubjectDesign,10) would repeat the generated design-dataframe 10x.

subtypes(AbstractDesign)
3-element Vector{Any}:
  MultiSubjectDesign
  RepeatDesign
- SingleSubjectDesign

Component

components define a signal. Some components can be nested, e.g. LinearModelComponent|>MultichannelComponent, see the multi-channel tutorial for more information

subtypes(AbstractComponent)
3-element Vector{Any}:
+ SingleSubjectDesign

Component

Components define a signal. Some components can be nested, e.g. LinearModelComponent|>MultichannelComponent, see the multi-channel tutorial for more information.

subtypes(AbstractComponent)
3-element Vector{Any}:
  LinearModelComponent
  MixedModelComponent
- MultichannelComponent

Onsets

Onsets define the distance between events in the continuous signal

subtypes(AbstractOnset)
2-element Vector{Any}:
+ MultichannelComponent

Onsets

Onsets define the distance between events in the continuous signal.

subtypes(AbstractOnset)
2-element Vector{Any}:
  LogNormalOnset
  UniformOnset

Noise

Choose the noise you need!

subtypes(AbstractNoise)
7-element Vector{Any}:
  ExponentialNoise
@@ -15,4 +15,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/poweranalysis/index.html b/dev/generated/tutorials/poweranalysis/index.html index 20f4c56..42f4099 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
 15.129196 seconds (25.38 M allocations: 3.362 GiB, 4.58% gc time, 94.41% compilation time: 9% of which was recompilation)

let's calculate the power

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

This page was generated using Literate.jl.

+end
 15.980139 seconds (26.11 M allocations: 3.399 GiB, 4.51% gc time, 94.10% compilation time: 9% of which was recompilation)

let's calculate the power

power = mean(pvals .<0.05)*100
60.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 c0c7c57..6f1e547 100644 --- a/dev/generated/tutorials/quickstart/index.html +++ b/dev/generated/tutorials/quickstart/index.html @@ -1,12 +1,12 @@ -Quickstart · UnfoldSim.jl
using UnfoldSim
+Quickstart · UnfoldSim.jl
using UnfoldSim
 using Random
-using CairoMakie
Tipp

Use subtypes(AbstractNoise) (or subtypes(AbstractComponent) etc.) to find already implemented building blocks

"Experimental" Design

Define a 1 x 2 design with 20 trials. That is, one condition (condaA) with two levels.

design = SingleSubjectDesign(;
+using CairoMakie
Tip

Use subtypes(AbstractNoise) (or subtypes(AbstractComponent) etc.) to find already implemented building blocks.

"Experimental" Design

Define a 1 x 2 design with 20 trials. That is, one condition (condaA) with two levels.

design = SingleSubjectDesign(;
         conditions=Dict(:condA=>["levelA","levelB"])
-        ) |> x->RepeatDesign(x,10);

Component / Signal

Define a simple component and ground truth simulation formula. Akin to ERP components, we call one simulation signal a component.

Highlight

You could easily specify multiple components by providing a vector of components, which are automatically added at the same onsets. This procedure simplifies to generate some response that is independent of simulated condition, whereas other depends on it.

signal = LinearModelComponent(;
+        ) |> x->RepeatDesign(x,10);

Component / Signal

Define a simple component and ground truth simulation formula. Akin to ERP components, we call one simulation signal a component.

Note

You could easily specify multiple components by providing a vector of components, which are automatically added at the same onsets. This procedure simplifies to generate some response that is independent of simulated condition, whereas other depends on it.

signal = LinearModelComponent(;
         basis=[0,0,0,0.5,1,1,0.5,0,0],
         formula = @formula(0~1+condA),
         β = [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

We will put it all together in one Simulation type

simulation = Simulation(design, signal,  onset, noise);

finally, we will simulate some data

data,events = simulate(MersenneTwister(1),simulation);

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")
+        );

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

We will put it all together in one Simulation type

simulation = Simulation(design, signal,  onset, noise);

finally, we will simulate some data

data,events = simulate(MersenneTwister(1),simulation);

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 cbdcf4d..ecfccd9 100644 --- a/dev/generated/tutorials/simulateERP/index.html +++ b/dev/generated/tutorials/simulateERP/index.html @@ -1,11 +1,11 @@ -Simulate ERPs · UnfoldSim.jl
using UnfoldSim
+Simulate ERPs · UnfoldSim.jl
using UnfoldSim
 using CairoMakie
 using Random
 using Unfold
-using UnfoldMakie

ERP Complex

here we will learn how to simulate a typical ERP complex with P100, N170, P300

let's grab a SingleSubjectDesign and add a continuous predictor

design = SingleSubjectDesign(;
+using UnfoldMakie

ERP Complex

Here we will learn how to simulate a typical ERP complex with P100, N170, P300.

Let's grab a SingleSubjectDesign and add a continuous predictor

design = SingleSubjectDesign(;
         conditions=Dict(:condition=>["car","face"],:continuous=>range(-5,5,length=10))
-        ) |> x->RepeatDesign(x,100);

let's make use of the prespecified basis functions, but use different formulas + parameters for each!

p100 is unaffected by our design and has amplitude of 5

p1 =  LinearModelComponent(;
+        ) |> x->RepeatDesign(x,100);

Let's make use of the prespecified basis functions, but use different formulas + parameters for each!

p100 is unaffected by our design and has amplitude of 5

p1 =  LinearModelComponent(;
         basis = p100(),
         formula = @formula(0~1),
         β = [5]
@@ -17,29 +17,7 @@
         basis = p300(),
         formula = @formula(0~1+continuous),
         β = [5,1]
-        );

now we can simply combine the components and simulate

components = [p1,n1,p3]
-data,evts = simulate(MersenneTwister(1),design,[p1,n1,p3],UniformOnset(;width=0,offset=1000),PinkNoise());
-
-# Analysis
([-0.23791263767079127, 0.7795891463181703, 0.05361693547332502, 0.12380920288278903, 0.7569649245743058, 0.3111863187121404, -0.847739657112498, -0.40995674870938037, 0.0009473713863165388, -1.2529893062381732  …  6.40407333484387, 4.969979336030171, 4.478563726264574, 3.6967811153769046, 3.232807745307163, 1.6266788619810815, 0.8313055493944903, 0.9393347800539115, 1.0530779209290557, 1.3268954815109233], 2000×3 DataFrame
-  Row  continuous  condition  latency 
-      │ Float64     String     Int64   
-──────┼────────────────────────────────
-    1 │  -5.0       car           1000
-    2 │  -3.88889   car           2000
-    3 │  -2.77778   car           3000
-    4 │  -1.66667   car           4000
-    5 │  -0.555556  car           5000
-    6 │   0.555556  car           6000
-    7 │   1.66667   car           7000
-    8 │   2.77778   car           8000
-  ⋮   │     ⋮           ⋮         ⋮
- 1994 │  -1.66667   face       1994000
- 1995 │  -0.555556  face       1995000
- 1996 │   0.555556  face       1996000
- 1997 │   1.66667   face       1997000
- 1998 │   2.77778   face       1998000
- 1999 │   3.88889   face       1999000
- 2000 │   5.0       face       2000000
-                      1985 rows omitted)

Let's check that everything worked out well, by using Unfold

m = fit(UnfoldModel,Dict(Any=>(@formula(0~1+condition+continuous),firbasis(τ=[-0.1,1],sfreq=100,name="basis"))),evts,data);

first the "pure" beta/linear regression parameters

plot_erp(coeftable(m))
Example block output

and now beautifully visualized as marginal betas / predicted ERPs

plot_erp(effects(Dict(:condition=>["car","face"],:continuous=>-5:5),m);
+        );

Now we can simply combine the components and simulate

components = [p1,n1,p3]
+data,evts = simulate(MersenneTwister(1),design,[p1,n1,p3],UniformOnset(;width=0,offset=1000),PinkNoise());

Analysis

Let's check that everything worked out well, by using Unfold

m = fit(UnfoldModel,Dict(Any=>(@formula(0~1+condition+continuous),firbasis(τ=[-0.1,1],sfreq=100,name="basis"))),evts,data);

first the "pure" beta/linear regression parameters

plot_erp(coeftable(m))
Example block output

and now beautifully visualized as marginal betas / predicted ERPs

plot_erp(effects(Dict(:condition=>["car","face"],:continuous=>-5:5),m);
         mapping=(:color=>:continuous,linestyle=:condition,group=:continuous),
-        extra=(;categoricalColor=false))
Example block output

This page was generated using Literate.jl.

+ extra=(;categoricalColor=false))
Example block output

This page was generated using Literate.jl.

diff --git a/dev/index.html b/dev/index.html index 63e7e0d..f0ce09f 100644 --- a/dev/index.html +++ b/dev/index.html @@ -7,4 +7,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 diff --git a/dev/search_index.js b/dev/search_index.js index c3009f1..4f78321 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"EditURL = \"../../../literate/HowTo/newComponent.jl\"","category":"page"},{"location":"generated/HowTo/newComponent/#New-component:-Duration-Shift","page":"New Duration/Shift-dependent Component","title":"New component: Duration + Shift","text":"","category":"section"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"We want a new component that changes it's duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"begin\n\tusing UnfoldSim\n using Unfold\n\tusing Random\n\tusing DSP\n using CairoMakie\nend\n\n\nsfreq = 100\n\n# Design","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"Let's genrate a design with two columns, shift + duration","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"design = UnfoldSim.SingleSubjectDesign(;conditions= Dict(\n :shift => rand(100).*sfreq/5,\n :duration=>20 .+rand(100).*sfreq/5))","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"We also need a new AbstractComponent","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"struct TimeVaryingComponent <: AbstractComponent\n basisfunction\n maxlength\nend","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"we have to define the length of a component","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"Base.length(c::TimeVaryingComponent) = length(c.maxlength)","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"While we could have put the TimeVaryingComponent.basisfunction directly into the simulate function, I thought this is a bit more modular","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"function UnfoldSim.simulate(rng,c::TimeVaryingComponent,design::AbstractDesign)\n evts = generate(design)\n return c.basisfunction(evts,c.maxlength)\nend","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"finally, the actual function that does the shifting + duration","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"function basis_shiftduration(evts,maxlength)\n basis = hanning.(Int.(round.(evts.duration))) ## hanning as long as duration\n if \"shift\" ∈ names(evts)\n basis = padarray.(basis,Int.(round.(.-evts.shift)),0) ## shift by adding 0 in front\n end\n # we should make sure that all bases have maxlength by appending / truncating\n difftomax = maxlength .- length.(basis)\n if any(difftomax.<0)\n @warn \"basis longer than max length in at least one case. either increase maxlength or redefine function. Trying to truncate the basis\"\n basis[difftomax .>0] = padarray.(basis[difftomax .> 0],difftomax[difftomax .> 0],0)\n return [b[1:maxlength] for b in basis]\n else\n return padarray.(basis,difftomax,0)\n end\nend\n\n\nerp = UnfoldSim.simulate(MersenneTwister(1),TimeVaryingComponent(basis_shiftduration,50),design)\nheatmap(hcat(erp...))","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"EditURL = \"../../../literate/tutorials/simulateERP.jl\"","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"using UnfoldSim\nusing CairoMakie\nusing Random\nusing Unfold\nusing UnfoldMakie","category":"page"},{"location":"generated/tutorials/simulateERP/#ERP-Complex","page":"Simulate ERPs","title":"ERP Complex","text":"","category":"section"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"here we will learn how to simulate a typical ERP complex with P100, N170, P300","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"let's grab a SingleSubjectDesign and add a continuous predictor","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"design = SingleSubjectDesign(;\n conditions=Dict(:condition=>[\"car\",\"face\"],:continuous=>range(-5,5,length=10))\n ) |> x->RepeatDesign(x,100);\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"let's make use of the prespecified basis functions, but use different formulas + parameters for each!","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p100 is unaffected by our design and has amplitude of 5","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p1 = LinearModelComponent(;\n basis = p100(),\n formula = @formula(0~1),\n β = [5]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"n170 has a condition effect, faces are more negative than cars","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"n1 = LinearModelComponent(;\n basis = n170(),\n formula = @formula(0~1+condition),\n β = [5,-3]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p300 has a continuous effect, higher continuous values will result in larger P300's","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p3 = LinearModelComponent(;\n basis = p300(),\n formula = @formula(0~1+continuous),\n β = [5,1]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"now we can simply combine the components and simulate","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"components = [p1,n1,p3]\ndata,evts = simulate(MersenneTwister(1),design,[p1,n1,p3],UniformOnset(;width=0,offset=1000),PinkNoise());\n\n# Analysis","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Let's check that everything worked out well, by using Unfold","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"m = fit(UnfoldModel,Dict(Any=>(@formula(0~1+condition+continuous),firbasis(τ=[-0.1,1],sfreq=100,name=\"basis\"))),evts,data);\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"first the \"pure\" beta/linear regression parameters","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"plot_erp(coeftable(m))","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"and now beautifully visualized as marginal betas / predicted ERPs","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"plot_erp(effects(Dict(:condition=>[\"car\",\"face\"],:continuous=>-5:5),m);\n mapping=(:color=>:continuous,linestyle=:condition,group=:continuous),\n extra=(;categoricalColor=false))","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"EditURL = \"../../../literate/HowTo/newDesign.jl\"","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"using UnfoldSim\nusing StableRNGs\nusing DataFrames\nusing Parameters","category":"page"},{"location":"generated/HowTo/newDesign/#Define-a-new-Design","page":"New Experimental Design","title":"Define a new Design","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"A design specifies how much data is generated, and how the event-table(s) should be generated. Already implemented examples are MultiSubjectDesign and SingleSubjectdesign","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need 3 things for a new design: a struct<:AbstractDesign, a size and a generate function","category":"page"},{"location":"generated/HowTo/newDesign/#)-type","page":"New Experimental Design","title":"1) type","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need a ImbalanceSubjectDesign struct. You are free to implement it as you wish, as long as the other two functions are implemented","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"@with_kw struct ImbalanceSubjectDesign <: UnfoldSim.AbstractDesign\n nTrials::Int\n balance::Float64 = 0.5 # default balanced\nend;\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/#)-size","page":"New Experimental Design","title":"2) size","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"we need a size(design::ImbalanceSubjectDesign) function to tell how many events we will have. This is used at different places, e.g. in the Default onset implementation","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"# note the trailling , to make it a Tuple\nsize(design::ImbalanceSubjectDesign) = (design.nTrials,);\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/#)-generate","page":"New Experimental Design","title":"3) generate","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need a type generate(design::ImbalanceSubjectDesign) function. This function should return the actual table as a DataFrame","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"function generate(design::ImbalanceSubjectDesign)\n nA = Int(round.(design.nTrials .* design.balance))\n nB = Int(round.(design.nTrials .* (1-design.balance)))\n @assert nA + nB ≈ design.nTrials\n levels = vcat(repeat([\"levelA\"],nA),repeat([\"levelB\"],nB))\n return DataFrame(Dict(:condition=>levels))\nend;\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"Finally, we can test the function and see whether it returns a Design-DataFrame as we requested","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"design = ImbalanceSubjectDesign(;nTrials=6,balance=0.2)\ngenerate(design)","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"important: Important\nit 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.","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"EditURL = \"../../../literate/HowTo/multichannel.jl\"","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"using UnfoldSim\nusing UnfoldMakie\nusing CairoMakie\nusing DataFrames\nusing Random\n\n# Specifcy design","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"We are using a one-level design for testing here.","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"design = SingleSubjectDesign(conditions=Dict(:condA=>[\"levelA\"]))","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Next we generate two simple components at two different times without any formula attached (we have a single condition anyway)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"c = LinearModelComponent(;basis=p100(),formula = @formula(0~1),β = [1]);\nc2 = LinearModelComponent(;basis=p300(),formula = @formula(0~1),β = [1]);\nnothing #hide","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"next similar to the nested design above, we can nest the component in a MultichannelComponent. We could either provide the projection marix manually, e.g.:","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"mc = UnfoldSim.MultichannelComponent(c, [1,2,-1,3,5,2.3,1])","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"or maybe more convenient: use the pair-syntax: Headmodel=>Label which makes use of a headmodel (HaRTmuT is currently easily available in UnfoldSim)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"hart = headmodel(type=\"hartmut\")\nmc = UnfoldSim.MultichannelComponent(c, hart=>\"Left Postcentral Gyrus\")\nmc2 = UnfoldSim.MultichannelComponent(c2, hart=>\"Right Occipital Pole\")","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"hint: Hint\n","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"You could also specify a noise-specific component which is applied prior to projection & summing with other components","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"finally we need to define the onsets of the signal","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"onset = UniformOnset(;width=20,offset=4);\n\n# Simulation + Plotting","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Now as usual we simulate data. Inspecting data shows our result is now indeed ~230 Electrodes large! Nice!","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"data,events = simulate(MersenneTwister(1),design, [mc,mc2], onset, NoNoise())\nsize(data)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Let's plot using Butterfly & Topoplot first we convert the electrodes to positions usable in TopoPlots.jl","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"pos3d = hart.electrodes[\"pos\"]\n\npos2d = to_positions(pos3d')\npos2d = [Point2f(p[1]+0.5,p[2]+0.5) for p in pos2d]","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"let's plot!","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"f = Figure()\ndf = DataFrame(:estimate => data[:],:channel => repeat(1:size(data,1),outer=size(data,2)),:time => repeat(1:size(data,2),inner=size(data,1)))\nplot_butterfly!(f[1,1:2],df;positions=pos2d)\nplot_topoplot!(f[2,1],df[df.time .== 28,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))\nplot_topoplot!(f[2,2],df[df.time .== 48,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))\nf","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"This page was generated using Literate.jl.","category":"page"},{"location":"api/","page":"DocStrings","title":"DocStrings","text":"Modules = [UnfoldSim]","category":"page"},{"location":"api/#UnfoldSim.LinearModelComponent","page":"DocStrings","title":"UnfoldSim.LinearModelComponent","text":"A multiple regression component for one subject\n\nbasis: 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\nformula: StatsModels Formula-Object @formula 0~1+cond (left side must be 0)\nβ Vector of betas, must fit the formula\ncontrasts: Dict. Default is empty, e.g. Dict(:condA=>EffectsCoding())\n\nAll arguments can be named, in that case contrasts is optional\n\nWorks best with SingleSubjectDesign\n\nLinearModelComponent(;\n basis=hanning(40),\n formula=@formula(0~1+cond),\n β = [1.,2.],\n contrasts=Dict(:cond=>EffectsCoding())\n)\n\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MixedModelComponent","page":"DocStrings","title":"UnfoldSim.MixedModelComponent","text":"A component that adds a hierarchical relation between parameters according to a LMM defined via MixedModels.jl\n\nbasis: 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\nformula: Formula-Object in the style of MixedModels.jl e.g. @formula 0~1+cond + (1|subject) - left-handside is ignored\nβ Vector of betas, must fit the formula\nσ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.\ncontrasts: Dict in the style of MixedModels.jl. Default is empty.\n\nAll arguments can be named, in that case contrasts is optional\n\nWorks best with MultiSubjectDesign\n\nMixedModelComponent(;\n basis=hanning(40),\n formula=@formula(0~1+cond+(1+cond|subject)),\n β = [1.,2.],\n σs= Dict(:subject=>[0.5,0.4]),\n contrasts=Dict(:cond=>EffectsCoding())\n)\n\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MultiSubjectDesign","page":"DocStrings","title":"UnfoldSim.MultiSubjectDesign","text":"n_subjects::Int -> number of subjects\nn_items::Int -> number of items (sometimes ≈trials)\nsubjects_between = nothing -> effects between subjects, e.g. young vs old \nitems_between = nothing -> effects between items, e.g. natural vs artificial images, but shown to all subjects\nboth_within = nothing\t-> effects completly crossed\ntableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!\n\ntipp: check the resulting dataframe using generate(design)\n\n# declaring same condition both sub-between and item-between results in a full between subject/item design\ndesign = MultiSubjectDesignjectDesign(;\n n_items=10,\n\t\tn_subjects = 30,\n subjects_between=Dict(:cond=>[\"levelA\",\"levelB\"]),\n\t\titems_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n );\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MultichannelComponent","page":"DocStrings","title":"UnfoldSim.MultichannelComponent","text":"Wrapper for an AbstractComponent to project it to multiple target-channels via projection. optional adds noise to the source prior to projection.\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.RepeatDesign","page":"DocStrings","title":"UnfoldSim.RepeatDesign","text":"repeat a design DataFrame multiple times to mimick repeatedly recorded trials\n\ndesignOnce = MultiSubjectDesign(;\n n_items=2,\n\t\tn_subjects = 2,\n subjects_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n\t\titems_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n );\n\ndesign = RepeatDesign(designOnce,4);\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.SingleSubjectDesign","page":"DocStrings","title":"UnfoldSim.SingleSubjectDesign","text":"conditions = Dict of conditions, e.g. Dict(:A=>[\"a_small\",\"a_big\"],:B=>[\"b_tiny\",\"b_large\"])\ntableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!\n\nNumber of trials / rows in generate(design) depend on the full factorial of your conditions.\n\nTo increase the number of repetitions simply use RepeatDesign(SingleSubjectDesign(...),5)\n\ntipp: check the resulting dataframe using generate(design)\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.size-Tuple{MultiSubjectDesign}","page":"DocStrings","title":"Base.size","text":"Returns dimension of experiment design\n\n\n\n\n\n","category":"method"},{"location":"api/#DSP.Windows.hanning-Tuple{Any, Any, Any}","page":"DocStrings","title":"DSP.Windows.hanning","text":"generate a hanning window\n\nduration: in s offset: in s, defines hanning peak sfreq: sampling rate in Hz\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.adderp!-Tuple{Any, Vector, Vararg{Any, 4}}","page":"DocStrings","title":"UnfoldSim.adderp!","text":"Helper function to add inplace the erps to the EEG, but for both 2D (1 channel) and 3D (X channel case)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.closest_src-Tuple{AbstractVector{<:AbstractVector}, Any}","page":"DocStrings","title":"UnfoldSim.closest_src","text":"closest_src(coords_list::AbstractVector{<:AbstractVector}, pos)\nclosest_src(coords::Vector{<:Real}, pos)\n\nTakes 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.\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.closest_src-Tuple{Hartmut, String}","page":"DocStrings","title":"UnfoldSim.closest_src","text":"closest_src(head::Hartmut,label::String)\n\nReturns src-ix of the Headmodel Hartmut which is closest to the average of the label.\n\nimportant: Important\nWe 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.)\n\nhartmut = headmodel()\npos = closest_src(hartmut=>\"Left Middle Temporal Gyrus, posterior division\")\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.convert-NTuple{4, Any}","page":"DocStrings","title":"UnfoldSim.convert","text":"Function to convert output similar to unfold (data, evts)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, UnfoldSim.RealisticNoise, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::RealisticNoise, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, Union{PinkNoise, RedNoise}, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::Union{PinkNoise, RedNoise}, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, WhiteNoise, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::WhiteNoise, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.generate-Tuple{MultiSubjectDesign}","page":"DocStrings","title":"UnfoldSim.generate","text":"Generates full factorial Dataframe according to MixedModelsSim.jl 's simdatcrossed function Note: nitems = you can think of it as trials or better, as stimuli\n\nNote: No condition can be named dv which is used internally in MixedModelsSim / MixedModels as a dummy left-side\n\nAfterwards applies expdesign.tableModifyFun. Could be used to duplicate trials, sort, subselect etc.\n\nFinally it sorts by :subject\n\njulia> d = MultiSubjectDesign(;nsubjects = 10,nitems=20,both_within= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate(d)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.generate-Tuple{SingleSubjectDesign}","page":"DocStrings","title":"UnfoldSim.generate","text":"Generates full-factorial DataFrame of expdesign.conditions\n\nAfterwards applies expdesign.tableModifyFun.\n\njulia> d = SingleSubjectDesign(;conditions= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate(d)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.hartmut_citation-Tuple{}","page":"DocStrings","title":"UnfoldSim.hartmut_citation","text":"Returns citation-string for HArtMuT\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.headmodel-Tuple{}","page":"DocStrings","title":"UnfoldSim.headmodel","text":"Load a headmodel, using Artifacts.jl automatically downloads the required files\n\nCurrently only type=\"hartmut\" is implemented\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.hrf-Tuple{}","page":"DocStrings","title":"UnfoldSim.hrf","text":"Generate a HRF kernel. \n\nTR = 1/sfreq default parameters taken from SPM\n\nCode adapted from Unfold.jl\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.leadfield-Tuple{Hartmut}","page":"DocStrings","title":"UnfoldSim.leadfield","text":"Returns the leadfield\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.magnitude-Tuple{AbstractHeadmodel}","page":"DocStrings","title":"UnfoldSim.magnitude","text":"Extracts magnitude of the orientation-including leadfield.\n\nBy default uses the orientation specified in the headmodel\n\nFallback: along the third dimension using norm - the maximal projection\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.magnitude-Tuple{Hartmut}","page":"DocStrings","title":"UnfoldSim.magnitude","text":"Extract magnitude of 3-orientation-leadfield, type (default: \"perpendicular\") => uses the provided source-point orientations - otherwise falls back to norm\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.n_channels-Tuple{AbstractComponent}","page":"DocStrings","title":"UnfoldSim.n_channels","text":"Returns the number of channels. By default = 1\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.n_channels-Tuple{MultichannelComponent}","page":"DocStrings","title":"UnfoldSim.n_channels","text":"for MultichannelComponent returns the length of the projection vector\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.padarray-Tuple{Vector, Tuple, Any}","page":"DocStrings","title":"UnfoldSim.padarray","text":"Pads array with specified value, length padarray(arr, len, val)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_2x2-Tuple{Random.AbstractRNG}","page":"DocStrings","title":"UnfoldSim.predef_2x2","text":"todo\n\nCareful 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)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_eeg-Tuple{Any}","page":"DocStrings","title":"UnfoldSim.predef_eeg","text":"Generates a P1/N1/P3 complex. predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)\n\nIn case of n_subjects, MixedModelComponents are generated\n\nDefault params: n_repeats=100 tableModifyFun = x->shuffle(deepcopy(rng),x # random trial order conditions = Dict(...),\n\ncomponent / signal\n\nsfreq = 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\n\nnoise\n\nnoiselevel = 0.2, noise = PinkNoise(;noiselevel=noiselevel),\n\nonset\n\noverlap = (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]), \n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_eeg-Tuple{Random.AbstractRNG, Any}","page":"DocStrings","title":"UnfoldSim.predef_eeg","text":"predefeeg(rng,nsubjects;kwargs...) Runs predefeeg(rng;kwargs...) nsubject times and concatenates the results.\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, AbstractComponent, Simulation}","page":"DocStrings","title":"UnfoldSim.simulate","text":"by default call simulate with ::Abstractcomponent,::AbstractDesign`, but allow for custom types\n\nmaking use of other information in simulation\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, LinearModelComponent, AbstractDesign}","page":"DocStrings","title":"UnfoldSim.simulate","text":"simulate a linearModel\n\njulia> c = UnfoldSim.LinearModelComponent([0,1,1,0],@formula(0~1+cond),[1,2],Dict()) julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_between=(;:cond=>[\"A\",\"B\"])) julia> simulate(StableRNG(1),c,design)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, MixedModelComponent, AbstractDesign}","page":"DocStrings","title":"UnfoldSim.simulate","text":"simulate MixedModelComponent\n\njulia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_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)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, Vector{<:AbstractComponent}, Simulation}","page":"DocStrings","title":"UnfoldSim.simulate","text":"Simulates erp data given the specified parameters \n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.weight_σs-Tuple{Dict, Float64, Float64}","page":"DocStrings","title":"UnfoldSim.weight_σs","text":"Weights a σs Dict for MixedModels.jl by a Float64\n\nFinally sales it by σ_lmm, as a trick to simulate noise-free LMMs\n\nI anticipate a function function weight_σs(σs::Dict,b_σs::Dict,σ_lmm::Float64) where each σs entry can be weighted individually\n\n\n\n\n\n","category":"method"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"EditURL = \"../../../literate/reference/noisetypes.jl\"","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"using UnfoldSim\nusing CairoMakie\nusing DSP\nusing StableRNGs\nimport StatsBase.autocor","category":"page"},{"location":"generated/reference/noisetypes/#What's-the-noise?","page":"NoiseTypes","title":"What's the noise?","text":"","category":"section"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"There are several noise-types directly implemented. Here is a comparison","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"f = Figure()\nax_sig = f[1,1:2] = Axis(f;title=\"1.000 samples of noise\")\nax_spec = f[2,1] = Axis(f;title=\"Welch Periodigram\")\nax_auto = f[2,2] = Axis(f;title=\"Autocorrelogram (every 10th lag)\")\nfor n = [PinkNoise RedNoise WhiteNoise NoNoise ExponentialNoise]\n\n # generate\n noisevec = gen_noise(StableRNG(1),n(),10000)\n\n # plot 1000 samples\n lines!(ax_sig,noisevec[1:1000];label=string(n))\n\n # calc spectrum\n perio = welch_pgram(noisevec)\n\n # plot spectrum\n lines!(ax_spec,freq(perio),log10.(power(perio)))\n\n lags = 0:10:500\n autocor_vec = autocor(noisevec,lags)\n lines!(ax_auto,lags,autocor_vec)\n\nend\nf[1:2,3] = Legend(f,ax_sig,\"NoiseType\")\nf","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"!!! Recommendation We recommed for smaller signals the ExponentialNoise, maybe with a removed DC offset or a HighPass filter. For long signals, this Noise requires lot's of memory though. maybe Pinknoise is a better choice","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"EditURL = \"../../../literate/reference/basistypes.jl\"","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"using UnfoldSim\nusing CairoMakie\nusing DSP\nusing StableRNGs\n#","category":"page"},{"location":"generated/reference/basistypes/#Basistypes","page":"ComponentBasisTypes","title":"Basistypes","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"There are several bases types directly implemented. They can be easily used for the components.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"note: Note\nYou can use any arbitrary shape defined by yourself! We often make use of hanning(50) from the DSP.jl package","category":"page"},{"location":"generated/reference/basistypes/#EEG","page":"ComponentBasisTypes","title":"EEG","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"by default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(;sfreq=300)","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"f = Figure()\nax = f[1,1] = Axis(f)\nfor b in [p100,n170,p300,n400]\n lines!(ax,b(),label=string(b))\n scatter!(ax,b(),label=string(b))\nend\naxislegend(ax,merge=true)\nf","category":"page"},{"location":"generated/reference/basistypes/#fMRI","page":"ComponentBasisTypes","title":"fMRI","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"default hrf TR is 1. Get to know all your favourite shapes!","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"##--\nf = Figure()\nplotConfig = (:peak=>1:3:10,\n :psUnder=>10:5:30,\n :amplitude=>2:5,\n :shift=>0:3:10,\n :peak_width => 0.1:0.5:1.5,\n :psUnder_width => 0.1:0.5:1.5,\n )\n\nfor (ix,pl) = enumerate(plotConfig)\n col = (ix-1)%3 +1\n row = Int(ceil(ix/3))\n\n ax = f[row,col] = Axis(f)\n cfg = collect(pl)\n for k = cfg[2]\n lines!(ax,UnfoldSim.hrf(;TR=0.1,(cfg[1]=>k,)...),label=string(k))\n end\n\n axislegend(string(cfg[1]);merge=true,)\nend\nf\n\n# pupil","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"we use the simplified PuRF from Hoeks & Levelt, 1993. Note that https://www.science.org/doi/10.1126/sciadv.abi9979 show some evidence in their supplementary material, that the convolution model is not fully applicable.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"f = Figure()\nplotConfig = (:n=>5:3:15,\n :tmax=>0.5:0.2:1.1,\n )\n\nfor (ix,pl) = enumerate(plotConfig)\n ax = f[1,ix] = Axis(f)\n cfg = collect(pl)\n for k = cfg[2]\n lines!(ax,UnfoldSim.PuRF(;(cfg[1]=>k,)...),label=string(k))\n end\n\n axislegend(string(cfg[1]);merge=true,)\nend\nf","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"EditURL = \"../../../literate/tutorials/poweranalysis.jl\"","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"using UnfoldSim\nusing Unfold\nusing Statistics\nusing HypothesisTests\nusing DataFrames\nusing Random","category":"page"},{"location":"generated/tutorials/poweranalysis/#Simple-Poweranalysis-Script","page":"Poweranalysis","title":"Simple Poweranalysis Script","text":"","category":"section"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"For a power analysis, we will repeatedly simulate data, and check whether we can find a significant effect.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"We perform the power analysis on epoched data.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"pvals = fill(NaN,100)\n@time for seed = eachindex(pvals)\n # Simulate data of 30 subjects\n data,evts = UnfoldSim.predef_2x2(MersenneTwister(seed);\n n_subjects=20, ## 30 subjects\n overlap=(1,0), ## deactivate overlap\n noiselevel=10, ## add more noise to make it more challenging\n return_epoched=true, ## saves us the epoching step\n )\n\n\n # take the mean over a pre-specified timewindow\n evts.y = dropdims(mean(data[40:60,:],dims=1),dims=(1))\n\n # extract the two levels of condition A\n evts_reduced = combine(groupby(evts,[:subject,:A]),:y=>mean)\n y_big = evts_reduced[evts_reduced.A .==\"a_big\",:y_mean]\n y_small = evts_reduced[evts_reduced.A .==\"a_small\",:y_mean]\n\n # calculate a one-sided t-test\n pvals[seed] = pvalue(OneSampleTTest(y_big,y_small))\nend","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"let's calculate the power","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"power = mean(pvals .<0.05)*100","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"EditURL = \"../../../literate/reference/overview.jl\"","category":"page"},{"location":"generated/reference/overview/#Overview-of-functionality","page":"Toolbox Overview","title":"Overview of functionality","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"UnfoldSim has many modules, here we try to collect them to provide you with an overview","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"using UnfoldSim\nusing InteractiveUtils","category":"page"},{"location":"generated/reference/overview/#Design","page":"Toolbox Overview","title":"Design","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Designs define the experimental design. They can be nested, e.g. RepeatDesign(SingleSubjectDesign,10) would repeat the generated design-dataframe 10x.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractDesign)","category":"page"},{"location":"generated/reference/overview/#Component","page":"Toolbox Overview","title":"Component","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"components define a signal. Some components can be nested, e.g. LinearModelComponent|>MultichannelComponent, see the multi-channel tutorial for more information","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractComponent)","category":"page"},{"location":"generated/reference/overview/#Onsets","page":"Toolbox Overview","title":"Onsets","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Onsets define the distance between events in the continuous signal","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractOnset)","category":"page"},{"location":"generated/reference/overview/#Noise","page":"Toolbox Overview","title":"Noise","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Choose the noise you need!","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractNoise)","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"EditURL = \"../../../literate/tutorials/quickstart.jl\"","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"using UnfoldSim\nusing Random\nusing CairoMakie","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"tipp: Tipp\nUse subtypes(AbstractNoise) (or subtypes(AbstractComponent) etc.) to find already implemented building blocks","category":"page"},{"location":"generated/tutorials/quickstart/#\"Experimental\"-Design","page":"Quickstart","title":"\"Experimental\" Design","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Define a 1 x 2 design with 20 trials. That is, one condition (condaA) with two levels.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"design = SingleSubjectDesign(;\n conditions=Dict(:condA=>[\"levelA\",\"levelB\"])\n ) |> x->RepeatDesign(x,10);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Component-/-Signal","page":"Quickstart","title":"Component / Signal","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Define a simple component and ground truth simulation formula. Akin to ERP components, we call one simulation signal a component.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"highlight: Highlight\nYou could easily specify multiple components by providing a vector of components, which are automatically added at the same onsets. This procedure simplifies to generate some response that is independent of simulated condition, whereas other depends on it.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"signal = LinearModelComponent(;\n basis=[0,0,0,0.5,1,1,0.5,0,0],\n formula = @formula(0~1+condA),\n β = [1,0.5]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Onsets-and-Noise","page":"Quickstart","title":"Onsets and Noise","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"We will start with a uniform (but overlapping, offset < length(signal.basis)) onset-distribution","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"onset = UniformOnset(;width=20,offset=4);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"And we will use some noise","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"noise = PinkNoise(;noiselevel=0.2);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Combine-and-Generate","page":"Quickstart","title":"Combine & Generate","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"We will put it all together in one Simulation type","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"simulation = Simulation(design, signal, onset, noise);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"finally, we will simulate some data","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"data,events = simulate(MersenneTwister(1),simulation);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Data is a n-sample Vector (but could be a Matrix for e.g. MultiSubjectDesign).","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"events is a DataFrame that contains a column latency with the onsets of events.","category":"page"},{"location":"generated/tutorials/quickstart/#Plot-them!","page":"Quickstart","title":"Plot them!","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"lines(data;color=\"black\")\nvlines!(events.latency;color=[\"orange\",\"teal\"][1 .+ (events.condA.==\"levelB\")])\ncurrent_figure()","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"EditURL = \"../../../literate/HowTo/repeatTrials.jl\"","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"using UnfoldSim","category":"page"},{"location":"generated/HowTo/repeatTrials/#Repeating-Design-entries","page":"Repeating Trials within a Design","title":"Repeating Design entries","text":"","category":"section"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"Sometimes we want to repeat a design, that is, have multiple trials with identical values, but it is not always straight forward to implement For instance, there is no way to easily modify MultiSubjectDesign to have multiple identical subject/item combinations, without doing awkward repetitions of condition-levels or something.","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"If you struggle with this problem RepeatDesign is an easy tool for you","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"designOnce = MultiSubjectDesign(;\n n_items=2,\n\t\tn_subjects = 2,\n subjects_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n\t\titems_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n );\n\ndesign = RepeatDesign(designOnce,4);\ngenerate(design)","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"As you can see, the design was simply repeated. As always, you can ignore the dv column, it is for internal consistency with MixedModelsSim.jl","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"note: Note\nif 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","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"This page was generated using Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = UnfoldSim","category":"page"},{"location":"#UnfoldSim","page":"Home","title":"UnfoldSim","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for UnfoldSim.","category":"page"},{"location":"#Start-simulating-timeseries","page":"Home","title":"Start simulating timeseries","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We offer some predefined signals, check them out!","category":"page"},{"location":"","page":"Home","title":"Home","text":"For instance an P1/N170/P300 complex.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using UnfoldSim\nusing CairoMakie\ndata,evts = UnfoldSim.predef_eeg(;n_repeats=1,noiselevel=0.8)\n\nlines(data;color=\"black\")\nvlines!(evts.latency;color=[\"orange\",\"teal\"][1 .+ (evts.condition .==\"car\")])\n\ncurrent_figure()","category":"page"},{"location":"#Or-simulate-epoched-data-directly","page":"Home","title":"Or simulate epoched data directly","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"\ndata,evts = UnfoldSim.predef_eeg(;n_repeats=20,noiselevel=0.8,return_epoched=true)\nheatmap(data[:,sortperm(evts,[:condition,:continuous])])\n","category":"page"}] +[{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"EditURL = \"../../../literate/HowTo/newComponent.jl\"","category":"page"},{"location":"generated/HowTo/newComponent/#New-component:-Duration-Shift","page":"New Duration/Shift-dependent Component","title":"New component: Duration + Shift","text":"","category":"section"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"We want a new component that changes its duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"using UnfoldSim\nusing Unfold\nusing Random\nusing DSP\nusing CairoMakie\n\nsfreq = 100;\nnothing #hide","category":"page"},{"location":"generated/HowTo/newComponent/#Design","page":"New Duration/Shift-dependent Component","title":"Design","text":"","category":"section"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"Let's generate a design with two columns, shift + duration","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"design = UnfoldSim.SingleSubjectDesign(;conditions= Dict(\n :shift => rand(100).*sfreq/5,\n :duration=>20 .+rand(100).*sfreq/5))","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"We also need a new AbstractComponent","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"struct TimeVaryingComponent <: AbstractComponent\n basisfunction\n maxlength\nend","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"We have to define the length of a component","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"Base.length(c::TimeVaryingComponent) = length(c.maxlength)","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"While we could have put the TimeVaryingComponent.basisfunction directly into the simulate function, I thought this is a bit more modular","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"function UnfoldSim.simulate(rng,c::TimeVaryingComponent,design::AbstractDesign)\n evts = generate(design)\n return c.basisfunction(evts,c.maxlength)\nend","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"finally, the actual function that does the shifting + duration","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"function basis_shiftduration(evts,maxlength)\n basis = hanning.(Int.(round.(evts.duration))) ## hanning as long as duration\n if \"shift\" ∈ names(evts)\n basis = padarray.(basis,Int.(round.(.-evts.shift)),0) ## shift by adding 0 in front\n end\n # we should make sure that all bases have maxlength by appending / truncating\n difftomax = maxlength .- length.(basis)\n if any(difftomax.<0)\n @warn \"basis longer than max length in at least one case. either increase maxlength or redefine function. Trying to truncate the basis\"\n basis[difftomax .>0] = padarray.(basis[difftomax .> 0],difftomax[difftomax .> 0],0)\n return [b[1:maxlength] for b in basis]\n else\n return padarray.(basis,difftomax,0)\n end\nend\n\n\nerp = UnfoldSim.simulate(MersenneTwister(1),TimeVaryingComponent(basis_shiftduration,50),design)\nheatmap(hcat(erp...))","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"","category":"page"},{"location":"generated/HowTo/newComponent/","page":"New Duration/Shift-dependent Component","title":"New Duration/Shift-dependent Component","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"EditURL = \"../../../literate/tutorials/simulateERP.jl\"","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"using UnfoldSim\nusing CairoMakie\nusing Random\nusing Unfold\nusing UnfoldMakie","category":"page"},{"location":"generated/tutorials/simulateERP/#ERP-Complex","page":"Simulate ERPs","title":"ERP Complex","text":"","category":"section"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Here we will learn how to simulate a typical ERP complex with P100, N170, P300.","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Let's grab a SingleSubjectDesign and add a continuous predictor","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"design = SingleSubjectDesign(;\n conditions=Dict(:condition=>[\"car\",\"face\"],:continuous=>range(-5,5,length=10))\n ) |> x->RepeatDesign(x,100);\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Let's make use of the prespecified basis functions, but use different formulas + parameters for each!","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p100 is unaffected by our design and has amplitude of 5","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p1 = LinearModelComponent(;\n basis = p100(),\n formula = @formula(0~1),\n β = [5]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"n170 has a condition effect, faces are more negative than cars","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"n1 = LinearModelComponent(;\n basis = n170(),\n formula = @formula(0~1+condition),\n β = [5,-3]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p300 has a continuous effect, higher continuous values will result in larger P300's","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"p3 = LinearModelComponent(;\n basis = p300(),\n formula = @formula(0~1+continuous),\n β = [5,1]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Now we can simply combine the components and simulate","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"components = [p1,n1,p3]\ndata,evts = simulate(MersenneTwister(1),design,[p1,n1,p3],UniformOnset(;width=0,offset=1000),PinkNoise());\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/#Analysis","page":"Simulate ERPs","title":"Analysis","text":"","category":"section"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"Let's check that everything worked out well, by using Unfold","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"m = fit(UnfoldModel,Dict(Any=>(@formula(0~1+condition+continuous),firbasis(τ=[-0.1,1],sfreq=100,name=\"basis\"))),evts,data);\nnothing #hide","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"first the \"pure\" beta/linear regression parameters","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"plot_erp(coeftable(m))","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"and now beautifully visualized as marginal betas / predicted ERPs","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"plot_erp(effects(Dict(:condition=>[\"car\",\"face\"],:continuous=>-5:5),m);\n mapping=(:color=>:continuous,linestyle=:condition,group=:continuous),\n extra=(;categoricalColor=false))","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"","category":"page"},{"location":"generated/tutorials/simulateERP/","page":"Simulate ERPs","title":"Simulate ERPs","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"EditURL = \"../../../literate/HowTo/newDesign.jl\"","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"using UnfoldSim\nusing StableRNGs\nusing DataFrames\nusing Parameters","category":"page"},{"location":"generated/HowTo/newDesign/#Define-a-new-Design","page":"New Experimental Design","title":"Define a new Design","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"A design specifies how much data is generated, and how the event-table(s) should be generated. Already implemented examples are MultiSubjectDesign and SingleSubjectDesign","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need 3 things for a new design: a struct<:AbstractDesign, a size and a generate function","category":"page"},{"location":"generated/HowTo/newDesign/#)-type","page":"New Experimental Design","title":"1) type","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need a ImbalanceSubjectDesign struct. You are free to implement it as you wish, as long as the other two functions are implemented","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"@with_kw struct ImbalanceSubjectDesign <: UnfoldSim.AbstractDesign\n nTrials::Int\n balance::Float64 = 0.5 # default balanced\nend;\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/#)-size","page":"New Experimental Design","title":"2) size","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"we need a size(design::ImbalanceSubjectDesign) function to tell how many events we will have. This is used at different places, e.g. in the Default onset implementation","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"# note the trailing , to make it a Tuple\nsize(design::ImbalanceSubjectDesign) = (design.nTrials,);\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/#)-generate","page":"New Experimental Design","title":"3) generate","text":"","category":"section"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"We need a type generate(design::ImbalanceSubjectDesign) function. This function should return the actual table as a DataFrame","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"function generate(design::ImbalanceSubjectDesign)\n nA = Int(round.(design.nTrials .* design.balance))\n nB = Int(round.(design.nTrials .* (1-design.balance)))\n @assert nA + nB ≈ design.nTrials\n levels = vcat(repeat([\"levelA\"],nA),repeat([\"levelB\"],nB))\n return DataFrame(Dict(:condition=>levels))\nend;\nnothing #hide","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"Finally, we can test the function and see whether it returns a Design-DataFrame as we requested","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"design = ImbalanceSubjectDesign(;nTrials=6,balance=0.2)\ngenerate(design)","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"warning: Important\nIt 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.","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"","category":"page"},{"location":"generated/HowTo/newDesign/","page":"New Experimental Design","title":"New Experimental Design","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"EditURL = \"../../../literate/HowTo/multichannel.jl\"","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"using UnfoldSim\nusing UnfoldMakie\nusing CairoMakie\nusing DataFrames\nusing Random","category":"page"},{"location":"generated/HowTo/multichannel/#Specify-design","page":"Multi Channel Data","title":"Specify design","text":"","category":"section"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"We are using a one-level design for testing here.","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"design = SingleSubjectDesign(conditions=Dict(:condA=>[\"levelA\"]))","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Next we generate two simple components at two different times without any formula attached (we have a single condition anyway)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"c = LinearModelComponent(;basis=p100(),formula = @formula(0~1),β = [1]);\nc2 = LinearModelComponent(;basis=p300(),formula = @formula(0~1),β = [1]);\nnothing #hide","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"next similar to the nested design above, we can nest the component in a MultichannelComponent. We could either provide the projection matrix manually, e.g.:","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"mc = UnfoldSim.MultichannelComponent(c, [1,2,-1,3,5,2.3,1])","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"or maybe more convenient: use the pair-syntax: Headmodel=>Label which makes use of a headmodel (HaRTmuT is currently easily available in UnfoldSim)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"hart = headmodel(type=\"hartmut\")\nmc = UnfoldSim.MultichannelComponent(c, hart=>\"Left Postcentral Gyrus\")\nmc2 = UnfoldSim.MultichannelComponent(c2, hart=>\"Right Occipital Pole\")","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"info: Info\nYou could also specify a noise-specific component which is applied prior to projection & summing with other components.","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"finally we need to define the onsets of the signal","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"onset = UniformOnset(;width=20,offset=4);\nnothing #hide","category":"page"},{"location":"generated/HowTo/multichannel/#Simulation-Plotting","page":"Multi Channel Data","title":"Simulation + Plotting","text":"","category":"section"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Now as usual we simulate data. Inspecting data shows our result is now indeed ~230 Electrodes large! Nice!","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"data,events = simulate(MersenneTwister(1),design, [mc,mc2], onset, NoNoise())\nsize(data)","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"Let's plot using Butterfly & Topoplot: first we convert the electrodes to positions usable in TopoPlots.jl","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"pos3d = hart.electrodes[\"pos\"];\n\npos2d = to_positions(pos3d');\npos2d = [Point2f(p[1]+0.5,p[2]+0.5) for p in pos2d];\nnothing #hide","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"now plot!","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"f = Figure()\ndf = DataFrame(:estimate => data[:],:channel => repeat(1:size(data,1),outer=size(data,2)),:time => repeat(1:size(data,2),inner=size(data,1)))\nplot_butterfly!(f[1,1:2],df;positions=pos2d)\nplot_topoplot!(f[2,1],df[df.time .== 28,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))\nplot_topoplot!(f[2,2],df[df.time .== 48,:];positions=pos2d,visual=(;enlarge=0.5,label_scatter=false),axis=(;limits=((0,1),(0,0.9))))\nf","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"","category":"page"},{"location":"generated/HowTo/multichannel/","page":"Multi Channel Data","title":"Multi Channel Data","text":"This page was generated using Literate.jl.","category":"page"},{"location":"api/","page":"DocStrings","title":"DocStrings","text":"Modules = [UnfoldSim]","category":"page"},{"location":"api/#UnfoldSim.LinearModelComponent","page":"DocStrings","title":"UnfoldSim.LinearModelComponent","text":"A multiple regression component for one subject\n\nbasis: 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\nformula: StatsModels Formula-Object @formula 0~1+cond (left side must be 0)\nβ Vector of betas, must fit the formula\ncontrasts: Dict. Default is empty, e.g. Dict(:condA=>EffectsCoding())\n\nAll arguments can be named, in that case contrasts is optional\n\nWorks best with SingleSubjectDesign\n\nLinearModelComponent(;\n basis=hanning(40),\n formula=@formula(0~1+cond),\n β = [1.,2.],\n contrasts=Dict(:cond=>EffectsCoding())\n)\n\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MixedModelComponent","page":"DocStrings","title":"UnfoldSim.MixedModelComponent","text":"A component that adds a hierarchical relation between parameters according to a LMM defined via MixedModels.jl\n\nbasis: 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\nformula: Formula-Object in the style of MixedModels.jl e.g. @formula 0~1+cond + (1|subject) - left-handside is ignored\nβ Vector of betas, must fit the formula\nσ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.\ncontrasts: Dict in the style of MixedModels.jl. Default is empty.\n\nAll arguments can be named, in that case contrasts is optional\n\nWorks best with MultiSubjectDesign\n\nMixedModelComponent(;\n basis=hanning(40),\n formula=@formula(0~1+cond+(1+cond|subject)),\n β = [1.,2.],\n σs= Dict(:subject=>[0.5,0.4]),\n contrasts=Dict(:cond=>EffectsCoding())\n)\n\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MultiSubjectDesign","page":"DocStrings","title":"UnfoldSim.MultiSubjectDesign","text":"n_subjects::Int -> number of subjects\nn_items::Int -> number of items (sometimes ≈trials)\nsubjects_between = nothing -> effects between subjects, e.g. young vs old \nitems_between = nothing -> effects between items, e.g. natural vs artificial images, but shown to all subjects\nboth_within = nothing\t-> effects completly crossed\ntableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!\n\ntipp: check the resulting dataframe using generate(design)\n\n# declaring same condition both sub-between and item-between results in a full between subject/item design\ndesign = MultiSubjectDesignjectDesign(;\n n_items=10,\n\t\tn_subjects = 30,\n subjects_between=Dict(:cond=>[\"levelA\",\"levelB\"]),\n\t\titems_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n );\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.MultichannelComponent","page":"DocStrings","title":"UnfoldSim.MultichannelComponent","text":"Wrapper for an AbstractComponent to project it to multiple target-channels via projection. optional adds noise to the source prior to projection.\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.RepeatDesign","page":"DocStrings","title":"UnfoldSim.RepeatDesign","text":"repeat a design DataFrame multiple times to mimick repeatedly recorded trials\n\ndesignOnce = MultiSubjectDesign(;\n n_items=2,\n\t\tn_subjects = 2,\n subjects_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n\t\titems_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n );\n\ndesign = RepeatDesign(designOnce,4);\n\n\n\n\n\n","category":"type"},{"location":"api/#UnfoldSim.SingleSubjectDesign","page":"DocStrings","title":"UnfoldSim.SingleSubjectDesign","text":"conditions = Dict of conditions, e.g. Dict(:A=>[\"a_small\",\"a_big\"],:B=>[\"b_tiny\",\"b_large\"])\ntableModifyFun = x->x; # can be used to sort, or x->shuffle(MersenneTwister(42),x) - be sure to fix/update the rng accordingly!!\n\nNumber of trials / rows in generate(design) depend on the full factorial of your conditions.\n\nTo increase the number of repetitions simply use RepeatDesign(SingleSubjectDesign(...),5)\n\ntipp: check the resulting dataframe using generate(design)\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.size-Tuple{MultiSubjectDesign}","page":"DocStrings","title":"Base.size","text":"Returns dimension of experiment design\n\n\n\n\n\n","category":"method"},{"location":"api/#DSP.Windows.hanning-Tuple{Any, Any, Any}","page":"DocStrings","title":"DSP.Windows.hanning","text":"generate a hanning window\n\nduration: in s offset: in s, defines hanning peak sfreq: sampling rate in Hz\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.adderp!-Tuple{Any, Vector, Vararg{Any, 4}}","page":"DocStrings","title":"UnfoldSim.adderp!","text":"Helper function to add inplace the erps to the EEG, but for both 2D (1 channel) and 3D (X channel case)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.closest_src-Tuple{AbstractVector{<:AbstractVector}, Any}","page":"DocStrings","title":"UnfoldSim.closest_src","text":"closest_src(coords_list::AbstractVector{<:AbstractVector}, pos)\nclosest_src(coords::Vector{<:Real}, pos)\n\nTakes 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.\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.closest_src-Tuple{Hartmut, String}","page":"DocStrings","title":"UnfoldSim.closest_src","text":"closest_src(head::Hartmut,label::String)\n\nReturns src-ix of the Headmodel Hartmut which is closest to the average of the label.\n\nimportant: Important\nWe 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.)\n\nhartmut = headmodel()\npos = closest_src(hartmut=>\"Left Middle Temporal Gyrus, posterior division\")\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.convert-NTuple{4, Any}","page":"DocStrings","title":"UnfoldSim.convert","text":"Function to convert output similar to unfold (data, evts)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, UnfoldSim.RealisticNoise, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::RealisticNoise, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, Union{PinkNoise, RedNoise}, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::Union{PinkNoise, RedNoise}, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.gen_noise-Tuple{Any, WhiteNoise, Int64}","page":"DocStrings","title":"UnfoldSim.gen_noise","text":"gen_noise(t::WhiteNoise, n::Int)\n\nGenerate noise of a given type t and length n\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.generate-Tuple{MultiSubjectDesign}","page":"DocStrings","title":"UnfoldSim.generate","text":"Generates full factorial Dataframe according to MixedModelsSim.jl 's simdatcrossed function Note: nitems = you can think of it as trials or better, as stimuli\n\nNote: No condition can be named dv which is used internally in MixedModelsSim / MixedModels as a dummy left-side\n\nAfterwards applies expdesign.tableModifyFun. Could be used to duplicate trials, sort, subselect etc.\n\nFinally it sorts by :subject\n\njulia> d = MultiSubjectDesign(;nsubjects = 10,nitems=20,both_within= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate(d)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.generate-Tuple{SingleSubjectDesign}","page":"DocStrings","title":"UnfoldSim.generate","text":"Generates full-factorial DataFrame of expdesign.conditions\n\nAfterwards applies expdesign.tableModifyFun.\n\njulia> d = SingleSubjectDesign(;conditions= Dict(:A=>nlevels(5),:B=>nlevels(2))) julia> generate(d)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.hartmut_citation-Tuple{}","page":"DocStrings","title":"UnfoldSim.hartmut_citation","text":"Returns citation-string for HArtMuT\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.headmodel-Tuple{}","page":"DocStrings","title":"UnfoldSim.headmodel","text":"Load a headmodel, using Artifacts.jl automatically downloads the required files\n\nCurrently only type=\"hartmut\" is implemented\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.hrf-Tuple{}","page":"DocStrings","title":"UnfoldSim.hrf","text":"Generate a HRF kernel. \n\nTR = 1/sfreq default parameters taken from SPM\n\nCode adapted from Unfold.jl\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.leadfield-Tuple{Hartmut}","page":"DocStrings","title":"UnfoldSim.leadfield","text":"Returns the leadfield\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.magnitude-Tuple{AbstractHeadmodel}","page":"DocStrings","title":"UnfoldSim.magnitude","text":"Extracts magnitude of the orientation-including leadfield.\n\nBy default uses the orientation specified in the headmodel\n\nFallback: along the third dimension using norm - the maximal projection\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.magnitude-Tuple{Hartmut}","page":"DocStrings","title":"UnfoldSim.magnitude","text":"Extract magnitude of 3-orientation-leadfield, type (default: \"perpendicular\") => uses the provided source-point orientations - otherwise falls back to norm\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.n_channels-Tuple{AbstractComponent}","page":"DocStrings","title":"UnfoldSim.n_channels","text":"Returns the number of channels. By default = 1\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.n_channels-Tuple{MultichannelComponent}","page":"DocStrings","title":"UnfoldSim.n_channels","text":"for MultichannelComponent returns the length of the projection vector\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.padarray-Tuple{Vector, Tuple, Any}","page":"DocStrings","title":"UnfoldSim.padarray","text":"Pads array with specified value, length padarray(arr, len, val)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_2x2-Tuple{Random.AbstractRNG}","page":"DocStrings","title":"UnfoldSim.predef_2x2","text":"todo\n\nCareful 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)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_eeg-Tuple{Any}","page":"DocStrings","title":"UnfoldSim.predef_eeg","text":"Generates a P1/N1/P3 complex. predefeeg(;kwargs...) predefeeg(rng;kwargs...) predefeeg(rng,nsubjects;kwargs...)\n\nIn case of n_subjects, MixedModelComponents are generated\n\nDefault params: n_repeats=100 tableModifyFun = x->shuffle(deepcopy(rng),x # random trial order conditions = Dict(...),\n\ncomponent / signal\n\nsfreq = 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\n\nnoise\n\nnoiselevel = 0.2, noise = PinkNoise(;noiselevel=noiselevel),\n\nonset\n\noverlap = (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]), \n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.predef_eeg-Tuple{Random.AbstractRNG, Any}","page":"DocStrings","title":"UnfoldSim.predef_eeg","text":"predefeeg(rng,nsubjects;kwargs...) Runs predefeeg(rng;kwargs...) nsubject times and concatenates the results.\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, AbstractComponent, Simulation}","page":"DocStrings","title":"UnfoldSim.simulate","text":"by default call simulate with ::Abstractcomponent,::AbstractDesign`, but allow for custom types\n\nmaking use of other information in simulation\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, LinearModelComponent, AbstractDesign}","page":"DocStrings","title":"UnfoldSim.simulate","text":"simulate a linearModel\n\njulia> c = UnfoldSim.LinearModelComponent([0,1,1,0],@formula(0~1+cond),[1,2],Dict()) julia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_between=(;:cond=>[\"A\",\"B\"])) julia> simulate(StableRNG(1),c,design)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, MixedModelComponent, AbstractDesign}","page":"DocStrings","title":"UnfoldSim.simulate","text":"simulate MixedModelComponent\n\njulia> design = MultiSubjectDesign(;nsubjects=2,nitems=50,item_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)\n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.simulate-Tuple{Any, Vector{<:AbstractComponent}, Simulation}","page":"DocStrings","title":"UnfoldSim.simulate","text":"Simulates erp data given the specified parameters \n\n\n\n\n\n","category":"method"},{"location":"api/#UnfoldSim.weight_σs-Tuple{Dict, Float64, Float64}","page":"DocStrings","title":"UnfoldSim.weight_σs","text":"Weights a σs Dict for MixedModels.jl by a Float64\n\nFinally sales it by σ_lmm, as a trick to simulate noise-free LMMs\n\nI anticipate a function function weight_σs(σs::Dict,b_σs::Dict,σ_lmm::Float64) where each σs entry can be weighted individually\n\n\n\n\n\n","category":"method"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"EditURL = \"../../../literate/reference/noisetypes.jl\"","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"using UnfoldSim\nusing CairoMakie\nusing DSP\nusing StableRNGs\nimport StatsBase.autocor","category":"page"},{"location":"generated/reference/noisetypes/#What's-the-noise?","page":"NoiseTypes","title":"What's the noise?","text":"","category":"section"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"There are several noise-types directly implemented. Here is a comparison:","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"f = Figure()\nax_sig = f[1,1:2] = Axis(f;title=\"1.000 samples of noise\")\nax_spec = f[2,1] = Axis(f;title=\"Welch Periodigram\")\nax_auto = f[2,2] = Axis(f;title=\"Autocorrelogram (every 10th lag)\")\nfor n = [PinkNoise RedNoise WhiteNoise NoNoise ExponentialNoise]\n\n # generate\n noisevec = gen_noise(StableRNG(1),n(),10000)\n\n # plot 1000 samples\n lines!(ax_sig,noisevec[1:1000];label=string(n))\n\n # calc spectrum\n perio = welch_pgram(noisevec)\n\n # plot spectrum\n lines!(ax_spec,freq(perio),log10.(power(perio)))\n\n lags = 0:10:500\n autocor_vec = autocor(noisevec,lags)\n lines!(ax_auto,lags,autocor_vec)\n\nend\nf[1:2,3] = Legend(f,ax_sig,\"NoiseType\")\nf","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"hint: Hint\nWe recommend 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.","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"","category":"page"},{"location":"generated/reference/noisetypes/","page":"NoiseTypes","title":"NoiseTypes","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"EditURL = \"../../../literate/reference/basistypes.jl\"","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"using UnfoldSim\nusing CairoMakie\nusing DSP\nusing StableRNGs","category":"page"},{"location":"generated/reference/basistypes/#Basistypes","page":"ComponentBasisTypes","title":"Basistypes","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"There are several basis types directly implemented. They can be easily used for the components.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"note: Note\nYou can use any arbitrary shape defined by yourself! We often make use of hanning(50) from the DSP.jl package.","category":"page"},{"location":"generated/reference/basistypes/#EEG","page":"ComponentBasisTypes","title":"EEG","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"By default, the EEG bases assume a sampling rate of 100, which can easily be changed by e.g. p100(;sfreq=300)","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"f = Figure()\nax = f[1,1] = Axis(f)\nfor b in [p100,n170,p300,n400]\n lines!(ax,b(),label=string(b))\n scatter!(ax,b(),label=string(b))\nend\naxislegend(ax,merge=true)\nf","category":"page"},{"location":"generated/reference/basistypes/#fMRI","page":"ComponentBasisTypes","title":"fMRI","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"default hrf TR is 1. Get to know all your favourite shapes!","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"##--\nf = Figure()\nplotConfig = (:peak=>1:3:10,\n :psUnder=>10:5:30,\n :amplitude=>2:5,\n :shift=>0:3:10,\n :peak_width => 0.1:0.5:1.5,\n :psUnder_width => 0.1:0.5:1.5,\n )\n\nfor (ix,pl) = enumerate(plotConfig)\n col = (ix-1)%3 +1\n row = Int(ceil(ix/3))\n\n ax = f[row,col] = Axis(f)\n cfg = collect(pl)\n for k = cfg[2]\n lines!(ax,UnfoldSim.hrf(;TR=0.1,(cfg[1]=>k,)...),label=string(k))\n end\n\n axislegend(string(cfg[1]);merge=true,)\nend\nf","category":"page"},{"location":"generated/reference/basistypes/#Pupil","page":"ComponentBasisTypes","title":"Pupil","text":"","category":"section"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"We use the simplified PuRF from Hoeks & Levelt, 1993. Note that https://www.science.org/doi/10.1126/sciadv.abi9979 show some evidence in their supplementary material, that the convolution model is not fully applicable.","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"f = Figure()\nplotConfig = (:n=>5:3:15,\n :tmax=>0.5:0.2:1.1,\n )\n\nfor (ix,pl) = enumerate(plotConfig)\n ax = f[1,ix] = Axis(f)\n cfg = collect(pl)\n for k = cfg[2]\n lines!(ax,UnfoldSim.PuRF(;(cfg[1]=>k,)...),label=string(k))\n end\n\n axislegend(string(cfg[1]);merge=true,)\nend\nf","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"","category":"page"},{"location":"generated/reference/basistypes/","page":"ComponentBasisTypes","title":"ComponentBasisTypes","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"EditURL = \"../../../literate/tutorials/poweranalysis.jl\"","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"using UnfoldSim\nusing Unfold\nusing Statistics\nusing HypothesisTests\nusing DataFrames\nusing Random","category":"page"},{"location":"generated/tutorials/poweranalysis/#Simple-Poweranalysis-Script","page":"Poweranalysis","title":"Simple Poweranalysis Script","text":"","category":"section"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"For a power analysis, we will repeatedly simulate data, and check whether we can find a significant effect.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"We perform the power analysis on epoched data.","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"pvals = fill(NaN,100)\n@time for seed = eachindex(pvals)\n # Simulate data of 30 subjects\n data,evts = UnfoldSim.predef_2x2(MersenneTwister(seed);\n n_subjects=20, ## 30 subjects\n overlap=(1,0), ## deactivate overlap\n noiselevel=10, ## add more noise to make it more challenging\n return_epoched=true, ## saves us the epoching step\n )\n\n\n # take the mean over a pre-specified timewindow\n evts.y = dropdims(mean(data[40:60,:],dims=1),dims=(1))\n\n # extract the two levels of condition A\n evts_reduced = combine(groupby(evts,[:subject,:A]),:y=>mean)\n y_big = evts_reduced[evts_reduced.A .==\"a_big\",:y_mean]\n y_small = evts_reduced[evts_reduced.A .==\"a_small\",:y_mean]\n\n # calculate a one-sided t-test\n pvals[seed] = pvalue(OneSampleTTest(y_big,y_small))\nend","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"let's calculate the power","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"power = mean(pvals .<0.05)*100","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"","category":"page"},{"location":"generated/tutorials/poweranalysis/","page":"Poweranalysis","title":"Poweranalysis","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"EditURL = \"../../../literate/reference/overview.jl\"","category":"page"},{"location":"generated/reference/overview/#Overview-of-functionality","page":"Toolbox Overview","title":"Overview of functionality","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"UnfoldSim has many modules, here we try to collect them to provide you with an overview.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"using UnfoldSim\nusing InteractiveUtils","category":"page"},{"location":"generated/reference/overview/#Design","page":"Toolbox Overview","title":"Design","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Designs define the experimental design. They can be nested, e.g. RepeatDesign(SingleSubjectDesign,10) would repeat the generated design-dataframe 10x.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractDesign)","category":"page"},{"location":"generated/reference/overview/#Component","page":"Toolbox Overview","title":"Component","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Components define a signal. Some components can be nested, e.g. LinearModelComponent|>MultichannelComponent, see the multi-channel tutorial for more information.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractComponent)","category":"page"},{"location":"generated/reference/overview/#Onsets","page":"Toolbox Overview","title":"Onsets","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Onsets define the distance between events in the continuous signal.","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractOnset)","category":"page"},{"location":"generated/reference/overview/#Noise","page":"Toolbox Overview","title":"Noise","text":"","category":"section"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"Choose the noise you need!","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"subtypes(AbstractNoise)","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"","category":"page"},{"location":"generated/reference/overview/","page":"Toolbox Overview","title":"Toolbox Overview","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"EditURL = \"../../../literate/tutorials/quickstart.jl\"","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"using UnfoldSim\nusing Random\nusing CairoMakie","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"tip: Tip\nUse subtypes(AbstractNoise) (or subtypes(AbstractComponent) etc.) to find already implemented building blocks.","category":"page"},{"location":"generated/tutorials/quickstart/#\"Experimental\"-Design","page":"Quickstart","title":"\"Experimental\" Design","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Define a 1 x 2 design with 20 trials. That is, one condition (condaA) with two levels.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"design = SingleSubjectDesign(;\n conditions=Dict(:condA=>[\"levelA\",\"levelB\"])\n ) |> x->RepeatDesign(x,10);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Component-/-Signal","page":"Quickstart","title":"Component / Signal","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Define a simple component and ground truth simulation formula. Akin to ERP components, we call one simulation signal a component.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"note: Note\nYou could easily specify multiple components by providing a vector of components, which are automatically added at the same onsets. This procedure simplifies to generate some response that is independent of simulated condition, whereas other depends on it.","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"signal = LinearModelComponent(;\n basis=[0,0,0,0.5,1,1,0.5,0,0],\n formula = @formula(0~1+condA),\n β = [1,0.5]\n );\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Onsets-and-Noise","page":"Quickstart","title":"Onsets and Noise","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"We will start with a uniform (but overlapping, offset < length(signal.basis)) onset-distribution","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"onset = UniformOnset(;width=20,offset=4);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"And we will use some noise","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"noise = PinkNoise(;noiselevel=0.2);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/#Combine-and-Generate","page":"Quickstart","title":"Combine & Generate","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"We will put it all together in one Simulation type","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"simulation = Simulation(design, signal, onset, noise);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"finally, we will simulate some data","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"data,events = simulate(MersenneTwister(1),simulation);\nnothing #hide","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"Data is a n-sample Vector (but could be a Matrix for e.g. MultiSubjectDesign).","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"events is a DataFrame that contains a column latency with the onsets of events.","category":"page"},{"location":"generated/tutorials/quickstart/#Plot-them!","page":"Quickstart","title":"Plot them!","text":"","category":"section"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"lines(data;color=\"black\")\nvlines!(events.latency;color=[\"orange\",\"teal\"][1 .+ (events.condA.==\"levelB\")])\ncurrent_figure()","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"","category":"page"},{"location":"generated/tutorials/quickstart/","page":"Quickstart","title":"Quickstart","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"EditURL = \"../../../literate/HowTo/repeatTrials.jl\"","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"using UnfoldSim","category":"page"},{"location":"generated/HowTo/repeatTrials/#Repeating-Design-entries","page":"Repeating Trials within a Design","title":"Repeating Design entries","text":"","category":"section"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"Sometimes we want to repeat a design, that is, have multiple trials with identical values, but it is not always straight forward to implement. For instance, there is no way to easily modify MultiSubjectDesign to have multiple identical subject/item combinations, without doing awkward repetitions of condition-levels or something.","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"If you struggle with this problem RepeatDesign is an easy tool for you:","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"designOnce = MultiSubjectDesign(;\n n_items=2,\n n_subjects = 2,\n subjects_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n items_between =Dict(:cond=>[\"levelA\",\"levelB\"]),\n);\n\ndesign = RepeatDesign(designOnce,4);\ngenerate(design)","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"As you can see, the design was simply repeated. As always, you can ignore the dv column, it is for internal consistency with MixedModelsSim.jl","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"note: Note\nIf 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","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"","category":"page"},{"location":"generated/HowTo/repeatTrials/","page":"Repeating Trials within a Design","title":"Repeating Trials within a Design","text":"This page was generated using Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = UnfoldSim","category":"page"},{"location":"#UnfoldSim","page":"Home","title":"UnfoldSim","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for UnfoldSim.","category":"page"},{"location":"#Start-simulating-timeseries","page":"Home","title":"Start simulating timeseries","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We offer some predefined signals, check them out!","category":"page"},{"location":"","page":"Home","title":"Home","text":"For instance an P1/N170/P300 complex.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using UnfoldSim\nusing CairoMakie\ndata,evts = UnfoldSim.predef_eeg(;n_repeats=1,noiselevel=0.8)\n\nlines(data;color=\"black\")\nvlines!(evts.latency;color=[\"orange\",\"teal\"][1 .+ (evts.condition .==\"car\")])\n\ncurrent_figure()","category":"page"},{"location":"#Or-simulate-epoched-data-directly","page":"Home","title":"Or simulate epoched data directly","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"\ndata,evts = UnfoldSim.predef_eeg(;n_repeats=20,noiselevel=0.8,return_epoched=true)\nheatmap(data[:,sortperm(evts,[:condition,:continuous])])\n","category":"page"}] }