Sequence of events (e.g. SCR)

In this HowTo you will learn to simulate a "SR"-Sequence, a stimulus response, followed by a button press response.

Setup

Click to expand
# Load required packages
using UnfoldSim
using CairoMakie
using StableRNGs

Create sequence design

First we generate the minimal design of the experiment by specifying our conditions (a one-condition-two-levels design in our case)

design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))
generate_events(design)
2×1 DataFrame
Rowcondition
String
1one
2two

Next we use the SequenceDesign and nest our initial design in it. "SR_" is code for an "S" (stimulus) event and an "R" (response) event - only single letter events are supported! The "_" is a signal for the onset generator to generate a bigger pause - no overlap between adjacent "SR" pairs.

design = SequenceDesign(design, "SR_")
generate_events(StableRNG(1), design)
4×2 DataFrame
Rowconditionevent
StringChar
1oneS
2oneR
3twoS
4twoR

The main thing that happened is that the design was repeated for every event (each 'letter') of the sequence, and an event column was added.

Hint

More advanced sequences are possible as well, like "SR{1,3}", or "A[BC]". Infinite sequences are not possible like "AB*".

Finally, let's repeat the current design 4 times

design = RepeatDesign(design, 4)
generate_events(StableRNG(1), design)
16×2 DataFrame
Rowconditionevent
StringChar
1oneS
2oneR
3twoS
4twoR
5oneS
6oneR
7twoS
8twoR
9oneS
10oneR
11twoS
12twoR
13oneS
14oneR
15twoS
16twoR

This results in 16 trials that nicely follow our sequence.

Hint

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

Specify components for sequence events

Next we have to specify for both events S and R what the responses should look like.

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

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

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

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

We combine them into a dictionary with a sequence-Char as key

components = Dict('S' => [p1, n1, p3], 'R' => [resp])
Dict{Char, Vector{LinearModelComponent}} with 2 entries:
  'R' => [LinearModelComponent([0.0, 0.00410499, 0.0163526, 0.0365416, 0.064340…
  'S' => [LinearModelComponent([0.0, 0.0, 0.0, 0.0, 0.0, 0.116978, 0.413176, 0.…

Simulate data

Given the design and the components. we specify onset and noise and simulate data

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

Finally we can plot the results

f, ax, h = lines(data)
vlines!(ax, evts.latency[evts.event .== 'S'], color = (:darkblue, 0.5))
vlines!(ax, evts.latency[evts.event .== 'R'], color = (:darkred, 0.5))
ax.xlabel = "Time [samples]"
ax.ylabel = "EEG [a.u]"
xlims!(ax, 0, 500)
f
Example block output

As visible, the R response always follows the S response. Due to the "_" we have large breaks between the individual sequences.


This page was generated using Literate.jl.