Skip to content

Analysis Functions

All public analysis and processing functions in EegFun.jl.

Functions

Base.copy Method
julia
Base.copy(dat::ContinuousData) -> ContinuousData
Base.copy(dat::EpochData) -> EpochData
Base.copy(dat::ErpData) -> ErpData
Base.copy(info::AnalysisInfo) -> AnalysisInfo
Base.copy(layout::Layout) -> Layout
Base.copy(tf_data::TimeFreqData) -> TimeFreqData
Base.copy(spectrum_data::SpectrumData) -> SpectrumData
Base.copy(ica::InfoIca) -> InfoIca

Create a shallow copy of an EegFun data object. DataFrames are copied with copycols=true for independence; small immutable fields are shared.

source
Base.show Method

Pretty-print artifact info summary (repairs, rejections, ICA components).

source
Base.show Method

Pretty-print combined channel repair info (continuous + epoch).

source
Base.show Method

Pretty-print continuous-level channel repair info.

source
Base.show Method

Pretty-print an EpochRejectionInfo summary with criterion, counts, and breakdowns.

source
Base.show Method

Pretty-print epoch-level channel repair info.

source
Base.show Method
julia
Base.show(io::IO, result::ErpMeasurementsResult)
Base.show(io::IO, cond::EpochCondition)
Base.show(io::IO, layout::Layout)
Base.show(io::IO, ::MIME"text/plain", layout::Layout)
Base.show(io::IO, neighbours_dict::OrderedDict{Symbol,Neighbours})
Base.show(io::IO, ::MIME"text/plain", neighbours_dict::OrderedDict{Symbol,Neighbours})
Base.show(io::IO, dat::MultiDataFrameEeg)
Base.show(io::IO, dat::ContinuousData)
Base.show(io::IO, dat::SingleDataFrameEeg)
Base.show(io::IO, dat::AnalysisInfo)
Base.show(io::IO, ica::InfoIca)
Base.show(io::IO, ::MIME"text/plain", ica::InfoIca)
Base.show(io::IO, result::TTestResult)
Base.show(io::IO, data::StatisticalData)
Base.show(io::IO, result::PermutationResult)
Base.show(io::IO, result::AnalyticResult)
Base.show(io::IO, data::TFStatisticalData)
Base.show(io::IO, result::TFClusterPermutationResult)
Base.show(io::IO, result::TFAnalyticResult)
Base.show(io::IO, decoded::DecodedData)
Base.show(io::IO, nc::NoiseCeiling)
Base.show(io::IO, rsa::RsaData)

Custom display methods for EegFun data types. Each type prints a structured summary including key fields, dimensions, and metadata.

source
Base.show Method

Pretty-print a Rejection, showing channel and epoch.

source
Base.show Method

Show each EpochRejectionInfo in a vector.

source
EegFun.add_noise_ceiling! Method
julia
add_noise_ceiling!(rsa_data::RsaData, rsa_data_list::Vector{RsaData}; correlation_method::Symbol = :spearman)

Compute and add noise ceiling to an RsaData object (typically grand average).

This is a convenience function that computes the noise ceiling and adds it to the provided RsaData object.

Arguments

  • rsa_data::RsaData: RsaData object to add noise ceiling to (modified in-place)

  • rsa_data_list::Vector{RsaData}: Vector of individual participant RsaData objects

  • correlation_method::Symbol: Correlation method (:spearman or :pearson)

Returns

  • rsa_data::RsaData: The same RsaData object with noise_ceiling field populated

Examples

julia
# Compute RSA for each participant
all_rsa = [rsa(epochs_p1), rsa(epochs_p2), rsa(epochs_p3)]

# Create grand average
grand_avg = grand_average(all_rsa)

# Add noise ceiling
add_noise_ceiling!(grand_avg, all_rsa)
source
EegFun.add_topo_rois! Method
julia
add_topo_rois!(ax::Axis, layout::DataFrame, rois::Vector{<:Vector{Symbol}}; border_size::Real=10, kwargs...)

Add regions of interest (ROIs) to a topographic plot based on groups of electrodes. Uses Graham's Scan algorithm to create convex hulls around electrode groups.

Arguments

  • ax: The axis to add ROIs to

  • layout: DataFrame containing electrode positions (needs x2, y2)

  • rois: Array of arrays, where each inner array contains electrode labels for a ROI

Keyword Arguments

All keyword arguments below have sensible defaults. You can override any by passing the corresponding keyword argument.

  • roi_linewidth::Int64=2: Line width of ROI outline.

  • roi_fill::Bool=false: Whether to fill the ROI area.

  • roi_fillalpha::Float64=0.2: Transparency of ROI fill.

  • roi_fillcolor::Symbol=gray: Color of ROI fill.

  • roi_linecolor::Symbol=black: Color of ROI outline.

  • roi_border_size::Int64=10: Size of the border around ROI points.

Example

julia
layout = read_layout("./layouts/biosemi64.csv")
polar_to_cartesian_xy!(layout)
fig, ax = plot_layout_2d(layout)
# Add simple ROI
add_topo_rois!(ax, layout, [[:PO7, :PO3, :P1]], roi_border_size=10)
# Add filled ROI
add_topo_rois!(ax, layout, [[:Fp1]], roi_border_size=5, fill=true, roi_fillcolor=:red, roi_fillalpha=0.2)
source
EegFun.add_zscore_columns Function
julia
add_zscore_columns(df::DataFrame, exclude_columns::Vector{Symbol} = [:row])

Non-mutating version of add_zscore_columns!.

Arguments

  • df::DataFrame: The correlation matrix DataFrame

  • exclude_columns::Vector{Symbol}: Columns to exclude from z-score calculation (default: [:row])

Returns

  • DataFrame: A new DataFrame with z-score columns added

Examples

julia
# Create new DataFrame with z-score columns
cm_with_z = add_zscore_columns(correlation_matrix_dual_selection(dat, 
    channel_selection1 = channels(),
    channel_selection2 = channels([:vEOG, :hEOG])
))
source
EegFun.add_zscore_columns! Function
julia
add_zscore_columns!(df::DataFrame, exclude_columns::Vector{Symbol} = [:row])

Add z-score columns to a correlation matrix DataFrame.

This function adds z-score columns for each numeric column in the DataFrame, excluding specified columns (default: :row).

Arguments

  • df::DataFrame: The correlation matrix DataFrame to modify

  • exclude_columns::Vector{Symbol}: Columns to exclude from z-score calculation (default: [:row])

Returns

  • DataFrame: The modified DataFrame with z-score columns added

Examples

julia
# Add z-score columns to correlation matrix
cm = correlation_matrix_dual_selection(dat, 
    channel_selection1 = channels(),
    channel_selection2 = channels([:vEOG, :hEOG])
)
add_zscore_columns!(cm)  # Adds z_vEOG, z_hEOG columns

# Exclude additional columns from z-score calculation
add_zscore_columns!(cm, [:row, :channel_name])
source
EegFun.all_data Method

Flatten all rejection data (abs + z-score) into a single DataFrame with reason tags.

source
EegFun.all_data Method
julia
all_data(dat::EegData) -> DataFrame

Get the complete DataFrame with all columns.

Arguments

  • dat::EegData: The EEG data object

Returns

  • DataFrame: Complete DataFrame with all columns
source
EegFun.all_labels Method
julia
all_labels(dat::EegData) -> Vector{Symbol}

Get all column names from the complete DataFrame.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Vector{Symbol}: All column names
source
EegFun.analytic_test Method
julia
analytic_test(prepared::StatisticalData;
              alpha::Float64 = 0.05,
              tail::Symbol = :both,
              correction_method::Symbol = :no)

Perform analytic (parametric) t-test without permutation

Arguments

  • prepared::StatisticalData: Prepared data from prepare_stats

  • alpha::Float64: Significance threshold (default: 0.05)

  • tail::Symbol: Test tail - :both (default), :left, or :right

  • correction_method::Symbol: Multiple comparison correction - :no (default) or :bonferroni

Returns

  • AnalyticResult: Results structure with t-statistics, p-values, and significant masks

Examples

julia
result = analytic_test(prepared, alpha=0.05, correction_method=:no)
result = analytic_test(prepared, alpha=0.05, correction_method=:bonferroni)
source
EegFun.apply_analysis_settings Function
julia
apply_analysis_settings(dat::EegFun.EegData, ica::EegFun.InfoIca, settings::Observables.Observable{EegFun.AnalysisSettings}; kwargs...)
apply_analysis_settings(dat::EegFun.EegData, ica::EegFun.InfoIca, settings::EegFun.AnalysisSettings; kwargs...)
apply_analysis_settings(dat::EegFun.EegData, settings::Observables.Observable{EegFun.AnalysisSettings}; kwargs...)
apply_analysis_settings(dat::EegFun.EegData, settings::EegFun.AnalysisSettings; kwargs...)

Non-mutating version of apply_analysis_settings!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.apply_analysis_settings! Method
julia
apply_analysis_settings!(data::EegData, settings::AnalysisSettings)

Apply analysis settings to data in-place.

source
EegFun.average_epochs Method
julia
average_epochs(dat::EpochData)

Average epochs to create an ERP. This function:

  1. Concatenates all epochs

  2. Groups by time point and condition

  3. Averages the EEG channels at each time point

  4. Adds a count of how many epochs went into each average

Arguments

  • dat::EpochData: The epoched data to average

Returns

  • ErpData: The averaged ERP data with epoch counts
source
EegFun.average_number_of_neighbours Method
julia
average_number_of_neighbours(neighbours_dict::OrderedDict{Symbol, Neighbours})

Calculate the average number of neighbours per electrode from a neighbours dictionary.

Arguments

  • neighbours_dict::OrderedDict{Symbol, Neighbours}: Dictionary containing neighbour information for each electrode

Returns

  • Float64: The average number of neighbours per electrode

Example

julia
layout = read_layout("./layouts/biosemi64.csv")
get_neighbours_xy!(layout, 0.4)
avg_neighbours = average_number_of_neighbours(layout.neighbours)
source
EegFun.baseline Function
julia
baseline(dat::Vector{EegFun.ErpData}; kwargs...)
baseline(dat::Vector{EegFun.EpochData}; kwargs...)
baseline(dat::Vector{EegFun.ErpData}, baseline_interval::Union{Nothing, Function, Tuple{Real, Real}}; kwargs...)
baseline(dat::Vector{EegFun.EpochData}, baseline_interval::Union{Nothing, Function, Tuple{Real, Real}}; kwargs...)
baseline(dat::EegFun.EegData; kwargs...)
baseline(dat::EegFun.EegData, baseline_interval::Union{Nothing, Function, Tuple{Real, Real}}; kwargs...)

Non-mutating version of baseline!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.baseline! Method
julia
baseline!(dat::EegData, baseline_interval::Interval; channel_selection = channels()) -> Nothing
baseline!(dat::EegData; channel_selection = channels()) -> Nothing
baseline!(dat::Vector{EpochData}, baseline_interval::Interval; channel_selection = channels()) -> Nothing
baseline!(dat::Vector{EpochData}; channel_selection = channels()) -> Nothing
baseline!(dat::Vector{ErpData}, baseline_interval::Interval; channel_selection = channels()) -> Nothing
baseline!(dat::Vector{ErpData}; channel_selection = channels()) -> Nothing

Apply baseline correction in-place. Subtracts the mean amplitude over baseline_interval from each selected channel. Omitting baseline_interval uses the full time range.

Arguments

  • dat: EegData (any subtype), Vector{EpochData}, or Vector{ErpData}

  • baseline_interval: time range as a tuple (-0.2, 0.0) or a range -0.2:0.0

  • channel_selection: channel predicate (default: all channels)

Examples

julia
# Correct all channels over -200 to 0 ms
baseline!(erp, (-0.2, 0.0))

# Correct a channel subset
baseline!(epochs, (-0.2, 0.0), channel_selection = channels([:Fz, :Cz, :Pz]))

# Correct using entire time range
baseline!(erp)
source
EegFun.baseline Method
julia
baseline(file_pattern::String, baseline_interval; 
         input_dir::String = pwd(), 
         participant_selection::Function = participants(),
         condition_selection::Function = conditions(),
         output_dir::Union{String, Nothing} = nothing)

Apply baseline correction to EEG/ERP data from JLD2 files and save to a new directory.

Arguments

  • file_pattern::String: Pattern to match files ("epochs", "erps", "cleaned", "original", or custom)

  • baseline_interval: Baseline interval as:

    • Tuple: (-0.2, 0.0) - time range in seconds

    • Range: -0.2:0 - time range using range syntax

  • input_dir::String: Input directory containing JLD2 files (default: current directory)

  • participant_selection::Function: Participant selection predicate (default: participants() for all)

  • condition_selection::Function: Condition selection predicate (default: conditions() for all)

  • output_dir::Union{String, Nothing}: Output directory (default: creates subdirectory based on baseline settings)

Notes

  • For in-memory data with function-based baseline selection, use baseline!(data, samples((-0.2, 0.0))) instead

Example

julia
# Baseline correct all epochs from -0.2 to 0.0 seconds
baseline("epochs", (-0.2, 0.0))

# Baseline correct specific participant
baseline("epochs", (-0.2, 0.0), participant_selection=participants(3))

# Baseline correct specific participants and conditions
baseline("epochs", (-0.2, 0.0), participant_selection=participants([3, 4]), condition_selection=conditions([1, 2]))

# Use tuple directly
source
EegFun.basename_without_ext Method
julia
basename_without_ext(path::String)

Extract the base filename without extension from a file path.

Arguments

  • path::String: File path to process

Returns

  • String: Base filename without extension

Example

julia
filename = basename_without_ext("data/file.bdf")
source
EegFun.bool_param Function

Create a Bool parameter.

source
EegFun.calculate_eog_channels! Method
julia
calculate_eog_channels!(dat::EegData, eog_cfg::EogConfig)
calculate_eog_channels!(dat::EegData, eog_cfg::Dict)

Calculate EOG channels based on configuration. Modifies the data in place.

Arguments

  • dat::EegData: EegData object to modify

  • eog_cfg::EogConfig: EOG configuration object (primary method)

  • eog_cfg::Dict: Configuration dictionary containing EOG channel settings (convenience method, converted to EogConfig)

Configuration Structure

The eog_cfg (Dict) should contain:

  • "vEOG_channels": Vector of 3 elements: [channels1, channels2, output_name]

  • "hEOG_channels": Vector of 3 elements: [channels1, channels2, output_name]

Examples

julia
# Calculate EOG channels using EogConfig
eog_cfg = EogConfig(
    vEOG_criterion = 50.0,
    hEOG_criterion = 30.0,
    vEOG_channels = [["Fp1", "Fp2"], ["IO1", "IO2"], ["vEOG"]],
    hEOG_channels = [["F9"], ["F10"], ["hEOG"]]
)
calculate_eog_channels!(dat, eog_cfg)

# Or using Dict (convenience method)
eog_cfg = Dict(
    "vEOG_channels" => [["Fp1", "Fp2"], ["IO1", "IO2"], ["vEOG"]],
    "hEOG_channels" => [["F9"], ["F10"], ["hEOG"]]
)
calculate_eog_channels!(dat, eog_cfg)
source
EegFun.channel_average Function
julia
channel_average(dat::EegFun.EegData; kwargs...)

Non-mutating version of channel_average!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.channel_average! Method
julia
channel_average!(dat::EegData; channel_selections::AbstractVector{<:Function} = [channels()],
                 output_labels = nothing,
                 include_extra::Bool = false,
                 reduce::Bool = false) -> Nothing

Create averaged channels for each provided channel-selection predicate and add them to dat.

  • channel_selections is a vector of channel predicates (see channels(...)) that defaults to [channels()] (all channels).

  • For every predicate, the selected channels are averaged to produce a new column.

  • Output labels default to the underscore-joined contributing channel labels (e.g., :Fp1_Fp2).

  • Can be used for single or multiple channel selections by passing a vector with one or more functions.

Layout handling

  • reduce = false (default): append averaged columns to data and averaged channels to layout.

  • reduce = true: replace data with metadata + averaged columns and create new layout with only averaged channels.

Examples

julia
# Average all channels (default)
channel_average!(dat)

# Single channel selection
channel_average!(dat, channel_selections = [channels([:Fp1, :Fp2])])

# Multiple channel selections (append columns and layout)
channel_average!(dat, channel_selections = [channels([:Fp1, :Fp2]), channels([:O1, :O2])])

# Average by patterns with custom labels
channel_average!(dat,
    channel_selections = [x -> startswith.(string.(x), "F"), x -> startswith.(string.(x), "P")];
    output_labels = [:F_avg, :P_avg],
)

# Produce a reduced dataset (meta + averages only) with new layout
channel_average!(dat, channel_selections = [channels([:Fp1, :Fp2])]; reduce = true)
source
EegFun.channel_average Method
julia
channel_average(file_pattern::String, channel_selections::Vector{Function}; 
                output_labels::Union{Vector{Symbol}, Nothing} = nothing,
                input_dir::String = pwd(), 
                participant_selection::Function = participants(),
                condition_selection::Function = conditions(),
                output_dir::Union{String, Nothing} = nothing,
                reduce::Bool = false)

Batch process EEG data files to average specified channels using predicates.

This function loads JLD2 files containing EEG data, averages specified channel groups, and saves the resulting data with new averaged channels to a new directory.

Arguments

  • file_pattern::String: Pattern to match JLD2 files (e.g., "erps_cleaned", "epochs_cleaned")

  • channel_selections::Vector{Function}: Channel selection predicates (e.g., [channels([:Fp1, :Fp2]), channels([:PO7, :PO8])])

  • output_labels::Union{Vector{Symbol}, Nothing}: Labels for averaged channels (default: auto-generated from selection)

  • input_dir::String: Input directory containing JLD2 files (default: current directory)

  • participant_selection::Function: Participant selection predicate (default: participants() for all)

  • condition_selection::Function: Condition selection predicate (default: conditions() for all)

  • output_dir::Union{String, Nothing}: Output directory (default: auto-generated)

  • reduce::Bool: Whether to keep only averaged channels (true) or append to existing (false, default)

Examples

julia
# Average frontal and parietal channels using predicates
channel_average("erps_cleaned", [channels([:Fp1, :Fp2]), channels([:PO7, :PO8])])

# With custom output labels
channel_average("erps_cleaned", 
                [channels([:Fp1, :Fp2]), channels([:PO7, :PO8])],
                output_labels = [:frontal, :parietal])

# Process specific participants and conditions
channel_average("erps_cleaned", [channels([:Fp1, :Fp2])], 
                input_dir = "/path/to/data", 
                participants = [1, 2, 3], 
                conditions = [1, 2])

# Create reduced dataset with only averaged channels
channel_average("erps_cleaned", 
                [channels([:Fp1, :Fp2]), channels([:PO7, :PO8])], 
                reduce = true)
source
EegFun.channel_data Method
julia
channel_data(eeg_data::EegData) -> DataFrame

Get EEG channel data columns from the EEG data.

source
EegFun.channel_delete! Method
julia
channel_delete!(dat::EegData, channels_to_delete::Union{Symbol, Vector{Symbol}})

Delete channels from EEG data and layout. Modifies the data in place by removing both the data columns and the layout rows.

Arguments

  • dat::EegData: The EEG data object to modify

  • channels_to_delete::Union{Symbol, Vector{Symbol}}: Channel(s) to delete

Returns

  • nothing (modifies the data in place)

Examples

julia
channel_delete!(dat, :Fp1)                 # Delete a single channel
channel_delete!(dat, [:Fp1, :Fp2, :Diode]) # Delete multiple channels

Notes

  • Only channels that exist in the data will be deleted

  • Updates both the data columns and the layout rows

  • Clears any cached neighbour information in the layout since channels have changed

source
EegFun.channel_delete Method
julia
channel_delete(dat::EegData, channels_to_delete::Union{Symbol, Vector{Symbol}})

Create a copy of EEG data with specified channels deleted.

Arguments

  • dat::EegData: The EEG data object to copy and modify

  • channels_to_delete::Union{Symbol, Vector{Symbol}}: Channel(s) to delete

Returns

  • EegData: A new EEG data object with channels deleted

Examples

julia
new_dat = channel_delete(dat, :Fp1)                 # Delete a single channel
new_dat = channel_delete(dat, [:Fp1, :Fp2, :Diode]) # Delete multiple channels

Notes

  • Only channels that exist in the data will be deleted

  • Updates both the data columns and the layout rows

  • Clears any cached neighbour information in the layout since channels have changed

  • The original data is not modified

source
EegFun.channel_difference Function
julia
channel_difference(dat::EegFun.EegData; kwargs...)

Non-mutating version of channel_difference!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.channel_difference! Method
julia
channel_difference!(dat::EegData; channel_selection1::Function = channels(), channel_selection2::Function = channels(), channel_out::Symbol = :diff)

Calculate and add channel difference to EEG data in-place using predicates.

Arguments

  • dat::EegData: EEG data object to modify (ContinuousData, ErpData, or EpochData)

  • channel_selection1::Function: Channel selection predicate for first set (default: channels() - all channels)

  • channel_selection2::Function: Channel selection predicate for second set (default: channels() - all channels)

  • channel_out::Symbol: Column name for the difference (default: :diff)

Examples

julia
# Calculate difference between frontal and parietal channels
channel_difference!(dat, 
    channel_selection1 = channels(x -> startswith.(string.(x), "F")),  # Frontal channels
    channel_selection2 = channels(x -> startswith.(string.(x), "P"))   # Parietal channels
)

# Calculate difference between specific channels
channel_difference!(dat, 
    channel_selection1 = channels([:Fp1, :Fp2]), 
    channel_selection2 = channels([:O1, :O2]), 
    channel_out = :front_back_diff
)

# Calculate difference between all channels vs all channels (will be zero)
channel_difference!(dat)
source
EegFun.channel_groups_param Method

Create a channel-groups parameter (Vector{Vector{String}}).

source
EegFun.channel_joint_probability Method
julia
channel_joint_probability(dat::SingleDataFrameEeg; channel_selection::Function = channels(), threshold::Real = 5.0, normalize::Int = 2, discret::Int = 1000)

Calculate joint probability of extreme values across EEG channels.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • channel_selection::Function: Function that returns boolean vector for channel filtering (default: channels() - all channels)

  • threshold::Real: Threshold for extreme value detection (default: 5.0)

  • normalize::Int: Normalization method (default: 2)

  • discret::Int: Number of discretization bins (default: 1000)

Returns

  • DataFrame: Joint probability data with channel names and probability values

Examples

julia
# Calculate joint probability for all channels
jp = channel_joint_probability(dat)

# Calculate joint probability with custom threshold
jp = channel_joint_probability(dat, threshold = 0.3)
source
EegFun.channel_labels Method
julia
channel_labels(dat::EegData) -> Vector{Symbol}

Get EEG channel column names from the EEG data.

source
EegFun.channel_labels Method
julia
channel_labels(layout::Layout) -> Vector{Symbol}

Get electrode label column names from the layout.

Arguments

  • layout::Layout: The layout object

Returns

  • Vector{Symbol}: Electrode label column names
source
EegFun.channel_repairable Function
julia
channel_repairable(repair_info::EegFun.ContinuousRepairInfo, bad_channels::Vector{Symbol}, layout::EegFun.Layout; kwargs...)
channel_repairable(artifacts::Vector{EegFun.EpochRejectionInfo}, layout::EegFun.Layout; kwargs...)
channel_repairable(artifacts::EegFun.EpochRejectionInfo, layout::EegFun.Layout; kwargs...)

Non-mutating version of channel_repairable!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.channel_repairable! Method
julia
channel_repairable!(repair_info::ContinuousRepairInfo, bad_channels::Vector{Symbol}, layout::Layout)

Analyze which channels can be repaired and which cannot, based on neighbor availability. Populates repair_info.repaired and repair_info.skipped with the analysis.

Arguments

  • repair_info::ContinuousRepairInfo: Continuous repair tracking struct (mutated to add repair analysis)

  • bad_channels::Vector{Symbol}: Channels identified as bad that need repair consideration

  • layout::Layout: Layout object containing neighbor information

Returns

  • ContinuousRepairInfo: The same repair_info object (modified in-place)

Notes

This function only analyzes repairability - it does not perform any repairs. Use repair_channels! to actually perform the repairs after this analysis.

source
EegFun.channel_repairable! Method
julia
channel_repairable!(artifacts::EpochRejectionInfo, layout::Layout)

Analyze which channels can be repaired and which cannot, based on neighbor availability. Populates artifacts.repaired and artifacts.skipped with the analysis.

Arguments

  • artifacts::EpochRejectionInfo: Artifact information from detect_artifacts (mutated to add repair analysis)

  • layout::Layout: Layout object containing neighbor information

Returns

  • EpochRejectionInfo: The same artifacts object (modified in-place)

Notes

This function only analyzes repairability - it does not perform any repairs. Use repair_artifacts_neighbor! to actually perform the repairs after this analysis.

source
EegFun.channel_summary Method
julia
channel_summary(dat::SingleDataFrameEeg; sample_selection::Function = samples(),
interval_selection::Interval = times(), channel_selection::Function = channels(), include_extra::Bool = false)::DataFrame
channel_summary(dat::MultiDataFrameEeg; sample_selection::Function = samples(),
interval_selection::Interval = times(), channel_selection::Function = channels(), include_meta::Bool = false, include_extra::Bool = false)::DataFrame
channel_summary(file_pattern::String; input_dir::String = pwd(),
participant_selection::Function = participants(), condition_selection::Function = conditions(),
sample_selection::Function = samples(), channel_selection::Function = channels(),
include_extra::Bool = false, output_dir = nothing, output_file::String = "channel_summary")

Compute summary statistics (min, max, std, range, var, zvar) for EEG channels.

The SingleDataFrameEeg method returns one row per channel; the MultiDataFrameEeg method (e.g. EpochData) returns one row per channel per epoch with an :epoch column. The file_pattern method batch-processes JLD2 files and writes results to CSV.

Examples

julia
# Basic usage
summary = channel_summary(dat)

# Specific channel selection
summary = channel_summary(dat, channel_selection = channels([:Fp1, :Fp2]))

# Exclude bad samples
summary = channel_summary(dat, sample_selection = samples_not(:is_extreme_value_100))

# Epoch-wise summary
summary = channel_summary(epoch_data, channel_selection = channels([:Fp1, :Fp2]))

# Batch processing
channel_summary("epochs", channel_selection = channels([:Fp1, :Fp2]))
source
EegFun.channels Method

Predicate generators for channel selection. channels() selects all; variants filter by name, number, or range.

source
EegFun.channels_not Method

Predicate generators that exclude the specified channels.

source
EegFun.check_channel_neighbors Method
julia
check_channel_neighbors(bad_channels::Vector{Symbol}, layout::Layout)::Vector{Symbol}

Check which bad channels can be repaired using neighbor interpolation. A channel is repairable only if:

  • ALL of its neighbors are good (not in the bad_channels list)

  • There are at least 2 good neighbors (required for meaningful interpolation)

This prevents bad channels from being used to repair other bad channels.

Arguments

  • bad_channels::Vector{Symbol}: Vector of bad channel names

  • layout::Layout: Layout object containing neighbor information

Returns

  • Vector{Symbol}: Bad channels that have ALL good neighbors (can be repaired)

Examples

julia
# Check which bad channels can be repaired
repairable_channels = check_channel_neighbors(bad_channels, layout)
source
EegFun.check_files_exist Method

Return true if all paths in files exist, warning for each missing file.

source
EegFun.clear_neighbours! Method
julia
clear_neighbours!(layout::Layout)

Clear all neighbor information from the layout.

This function removes all calculated neighbor data and resets the distance criterion. This is useful when layout coordinates change and neighbor calculations need to be redone.

Arguments

  • layout::Layout: The layout object to clear neighbors from

Modifies

  • layout: Removes neighbor data and criterion

Examples

julia
clear_neighbours!(layout)
source
EegFun.close_logging Method
julia
close_logging(; is_global::Bool = false)

Close the currently active log file and restore previous logger.

source
EegFun.combine_artifact_components Method
julia
combine_artifact_components(eog_comps, ecg_comps, line_noise_comps, channel_noise_comps)

Combine all identified artifact components into a single ArtifactComponents structure.

Arguments

  • eog_comps::Dict{Symbol, Vector{Int}}: EOG components dictionary

  • ecg_comps::Vector{Int}: ECG components vector

  • line_noise_comps::Vector{Int}: Line noise components vector

  • channel_noise_comps::Vector{Int}: Channel noise components vector

Returns

  • ArtifactComponents: Combined structure containing all artifact components
source
EegFun.common_channels Method
julia
common_channels(dat1::EegData, dat2::EegData) -> Vector{Symbol}

Find common channels between two EEG data objects.

Arguments

  • dat1::EegData: First EEG data object

  • dat2::EegData: Second EEG data object

Returns

  • Vector{Symbol}: Vector of common channel symbols
source
EegFun.compare_models Method
julia
compare_models(
    rsa_data::RsaData,
    model_rdms::Union{Vector{Matrix{Float64}}, Vector{Array{Float64, 3}}, Vector{Union{Matrix{Float64}, Array{Float64, 3}}}};
    model_names::Union{Vector{String}, Nothing} = nothing,
    correlation_type::Symbol = :spearman,
    n_permutations::Int = 1000,
    
)

Compare neural RDMs to model RDMs (static or temporal).

Computes correlations between neural RDMs and model RDMs at each time point, with optional permutation-based significance testing.

Input Format

Model RDMs must be in one of these formats:

  1. Static RDM: Matrix{Float64} [n_conditions × n_conditions]
  • Single RDM used at all time points

  • Example: Reaction time RDM, similarity ratings RDM

  1. Temporal RDM: Array{Float64, 3} [n_timepoints × n_conditions × n_conditions]
  • RDM at each time point

  • Must have same number of timepoints as rsa_data.times

  • Example: Eye tracking RDM, EDA RDM

Note: Convert your raw data to RDMs first using helper functions:

  • Static: create_rdm_from_reaction_times(), create_rdm_from_vectors(), etc.

  • Temporal: create_rdm_from_timeseries() (handles temporal alignment automatically)

Arguments

  • rsa_data::RsaData: RSA results from neural data (temporal)

  • model_rdms: Vector of model RDMs

    • Static: Matrix{Float64} [condition × condition]

    • Temporal: Array{Float64, 3} [time × condition × condition]

    • Mixed: Can contain both static and temporal models

  • model_names::Union{Vector{String}, Nothing}: Names for models (default: Model1, Model2, ...)

  • correlation_type::Symbol: Type of correlation (:spearman, :pearson)

  • n_permutations::Int: Number of permutations for significance testing (0 = no testing)

Returns

  • RsaData: Updated RsaData with model correlations and p-values

Examples

Static Model (Reaction Times)

julia
# Step 1: Convert your data to RDM
rts = [0.3, 0.5, 0.4]  # Your reaction time data
rt_rdm = create_rdm_from_reaction_times(rts)

# Step 2: Compare
neural_rsa = rsa(epochs)
rsa_with_model = compare_models(neural_rsa, [rt_rdm], model_names=["RTs"])

Temporal Model (Eye Tracking)

julia
# Step 1: Convert your data to temporal RDM (with automatic alignment)
eye_data = Array{Float64, 3}  # [conditions × features × time] - your format
eye_times = Vector{Float64}     # Your time vector
eye_rdms = create_rdm_from_timeseries(eye_data, eye_times; align_to=neural_rsa)

# Step 2: Compare
neural_rsa = rsa(epochs)
rsa_with_model = compare_models(neural_rsa, [eye_rdms], model_names=["Eye Tracking"])

Multiple Models (Mixed Static and Temporal)

julia
# Convert all your model data to RDMs
rt_rdm = create_rdm_from_reaction_times(rts)
eye_rdms = create_rdm_from_timeseries(eye_data, eye_times; align_to=neural_rsa)
eda_rdms = create_rdm_from_timeseries(eda_data, eda_times; align_to=neural_rsa)

# Compare all at once
rsa_with_models = compare_models(
    neural_rsa,
    [rt_rdm, eye_rdms, eda_rdms],
    model_names=["RTs", "Eye Tracking", "EDA"]
)

Pre-computed RDMs

If you already have RDMs computed (from other analyses):

julia
# Your pre-computed RDM
my_rdm = Matrix{Float64}  # [conditions × conditions]

# Use directly - no conversion needed
rsa_with_model = compare_models(neural_rsa, [my_rdm], model_names=["My Model"])
source
EegFun.compare_rejections Method
julia
compare_rejections(rejection_step1::Vector{EpochRejectionInfo}, rejection_step2::Vector{EpochRejectionInfo})

Compare rejection results before and after repair to show repair effectiveness.

Arguments

  • rejection_step1::Vector{EpochRejectionInfo}: Rejection info before repair

  • rejection_step2::Vector{EpochRejectionInfo}: Rejection info after repair

Returns

  • DataFrame: Comparison table with detailed statistics per condition showing:
    • Original number of epochs

    • Rejected before repair (step1)

    • Rejected after repair (step2)

    • Successfully repaired epochs

    • Percentage of step1 rejections that were repaired

    • Final percentage of epochs kept

Examples

julia
rejection_step1 = detect_bad_epochs_automatic(epochs, name="step1")
repair_artifacts!(epochs, rejection_step1)
rejection_step2 = detect_bad_epochs_automatic(epochs, name="step2")
comparison = compare_rejections(rejection_step1, rejection_step2)
source
EegFun.components Method

Predicate generators for ICA component selection.

source
EegFun.components_not Method

Predicate generators that exclude the specified components.

source
EegFun.compute_noise_ceiling Method
julia
compute_noise_ceiling(
    rsa_data_list::Vector{RsaData};
    correlation_method::Symbol = :spearman,
)

Compute noise ceiling for RSA analysis using leave-one-out cross-validation.

The noise ceiling quantifies the maximum correlation that an ideal model could achieve with the observed neural data, given the noise and variability in the measurements. It provides a benchmark for evaluating model performance.

Methodology (Nili et al., 2014)

Lower Bound (conservative estimate):

  • For each participant, correlate their RDM with the average RDM of all OTHER participants

  • Average these correlations across participants

  • This underestimates the true ceiling because it doesn't use all available data

Upper Bound (liberal estimate):

  • For each participant, correlate their RDM with the average RDM of ALL participants (including themselves)

  • Average these correlations across participants

  • This overestimates the true ceiling because it includes the participant in their own reference

The true noise ceiling likely lies between these bounds.

Arguments

  • rsa_data_list::Vector{RsaData}: Vector of RsaData objects, one per participant

    • All must have the same number of conditions and time points

    • All must use the same dissimilarity measure

  • correlation_method::Symbol: Method for correlating RDMs (:spearman or :pearson, default: :spearman)

Returns

  • noise_ceiling::NoiseCeiling: Noise ceiling estimates with lower and upper bounds

Examples

julia
# Compute RSA for each participant
all_rsa = [rsa(epochs_p1), rsa(epochs_p2), rsa(epochs_p3)]

# Compute noise ceiling
nc = compute_noise_ceiling(all_rsa)

# Noise ceiling is automatically added to grand average
grand_avg = grand_average(all_rsa)
println(grand_avg.noise_ceiling)

Notes

  • Requires at least 2 participants (preferably 10+)

  • More participants = more reliable noise ceiling estimate

  • The noise ceiling is time-varying (computed at each time point)

  • Use this to evaluate whether your model performance is limited by the model or by data quality

References

Nili, H., Wingfield, C., Walther, A., Su, L., Marslen-Wilson, W., & Kriegeskorte, N. (2014). A toolbox for representational similarity analysis. PLoS computational biology, 10(4), e1003553.

source
EegFun.compute_probability! Method
julia
compute_probability!(probaMap::Vector{Float64}, data::AbstractVector{Float64}, bins::Int)::Vector{Float64}

Compute probability distribution for a data vector.

Arguments

  • probaMap::Vector{Float64}: Pre-allocated vector to store probability values

  • data::AbstractVector{Float64}: Input data vector

  • bins::Int: Number of bins for discretization

Returns

  • Vector{Float64}: Probability distribution (same as probaMap)
source
EegFun.condition_average Method
julia
condition_average(data::Vector{<:ErpData}, condition_groups::Vector{Vector{Int}}) -> Vector{ErpData}
condition_average(data::Vector{TimeFreqData}, condition_groups::Vector{Vector{Int}}) -> Vector{TimeFreqData}
condition_average(file_pattern::String, condition_groups::Vector{Vector{Int}};
input_dir::String = pwd(), participant_selection::Function = participants(),
output_dir = nothing)

Create condition average waves (or time-frequency averages), or batch-process JLD2 files.

For each group of condition indices, averages the EEG channels (or TF power/phase) across those conditions. The file_pattern method loads JLD2 files and saves results to a new directory.

Arguments

  • data: Vector{<:ErpData} or Vector{TimeFreqData}, one element per condition

  • condition_groups: Groups of condition indices to average (e.g., [[1, 2], [3, 4]])

Example

julia
erps = average_epochs(epochs)
avg_waves = condition_average(erps, [[1, 2], [3, 4]])

# Batch
condition_average("erps_cleaned", [[1, 2], [3, 4]])
source
EegFun.condition_combine Method
julia
condition_combine(data::Vector{<:EpochData}, condition_groups::Vector{Vector{Int}})::Vector{EpochData}
condition_combine(file_pattern::String, condition_groups::Vector{Vector{Int}};
input_dir::String = pwd(), participant_selection::Function = participants(),
output_dir = nothing)

Combine epoch conditions by concatenating epochs from specified groups.

For each group in condition_groups, epochs from those conditions are concatenated into a single new EpochData object. The file_pattern method saves results to a new JLD2 directory.

Arguments

  • data::Vector{<:EpochData}: Vector of EpochData, one per condition

  • condition_groups::Vector{Vector{Int}}: Groups of condition indices to combine (e.g., [[1, 2], [3, 4]])

Example

julia
epochs = extract_epochs(dat, epoch_cfg, (-0.2, 1.0))
combined = condition_combine(epochs, [[1, 2], [3, 4]])

# Batch
condition_combine("epochs", [[1, 2], [3, 4]])
source
EegFun.condition_difference Method
julia
condition_difference(data::Vector{<:ErpData}, condition_pairs) -> Vector{ErpData}
condition_difference(data::Vector{TimeFreqData}, condition_pairs) -> Vector{TimeFreqData}
condition_difference(file_pattern::String, condition_pairs;
input_dir::String = pwd(), participant_selection::Function = participants(),
output_dir = nothing)

Create condition difference waves (or time-frequency differences), or batch-process JLD2 files.

For each pair (a, b), subtracts condition b from condition a. condition_pairs can be Vector{Tuple{Int,Int}} or Vector{Vector{Int}}. The file_pattern method loads JLD2 files.

Example

julia
erps = average_epochs(epochs)
diff_waves = condition_difference(erps, [(1, 2)])

# Batch
condition_difference("erps_cleaned", [(1, 2)])
source
EegFun.condition_info Method

Return (condition_number, condition_name) for any EegFunData object.

source
EegFun.condition_name Method
julia
condition_name(dat::EegFunData) -> String

Get the condition name.

source
EegFun.condition_number Method
julia
condition_number(dat::EegFunData) -> Union{Int, String}

Get the condition number or identifier.

source
EegFun.condition_parse_epoch Method
julia
condition_parse_epoch(config::Dict) -> Vector{EpochCondition}

Parse epoch condition configurations from a TOML-based configuration dictionary.

This function is primarily used by the built-in preprocessing pipelines to parse epoch conditions from TOML files. If you're creating a custom pipeline and want to use the same TOML configuration format, you can use this function. Otherwise, you can create EpochCondition objects directly.

TOML Format

See the pipeline documentation for the expected TOML structure.

Example

julia
config = TOML.parsefile("epoch_conditions.toml")
conditions = condition_parse_epoch(config)
source
EegFun.conditions Method

Predicate generators for condition selection by index or name.

source
EegFun.conditions_not Method

Predicate generators that exclude the specified conditions.

source
EegFun.consecutive Method
julia
consecutive(f::Function, A::AbstractVector; step::Int=1) -> Vector

Apply function f to consecutive pairs of elements in vector A.

Arguments

  • f::Function: Function to apply to pairs

  • A::AbstractVector: Input vector

  • step::Int=1: Step size between pairs

Returns

  • Vector: Results of applying f to consecutive pairs
source
EegFun.correlation_matrix Method
julia
correlation_matrix(dat::SingleDataFrameEeg; channel_selection::Function = channels())

Calculate the correlation matrix between EEG channels.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • channel_selection::Function: Function that returns boolean vector for channel filtering (default: channels() - all channels)

Returns

  • DataFrame: Correlation matrix with channel names as both row and column names

Examples

julia
# Calculate correlation matrix for all channels
cm = correlation_matrix(dat)

# Calculate correlation matrix for specific channels
cm = correlation_matrix(dat, channel_selection = channels([:Fp1, :Fp2, :F3, :F4]))
source
EegFun.correlation_matrix_dual_selection Method
julia
correlation_matrix_dual_selection(dat::SingleDataFrameEeg; 
                                 sample_selection::Function = samples(),
interval_selection::Interval = times(),
                                 channel_selection1::Function = channels(),
                                 channel_selection2::Function = channels(),
                                 include_extra_selection1::Bool = false,
                                 include_extra_selection2::Bool = true)

Calculate correlation matrix between two sets of channels using a single sample selection.

This function is particularly useful for calculating correlations between all EEG channels and EOG channels using the same time points.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • sample_selection::Function: Function for sample selection (default: samples())

  • channel_selection1::Function: Function for channel selection for first set (default: channels())

  • channel_selection2::Function: Function for channel selection for second set (default: channels())

  • include_extra_selection1::Bool: Whether to include extra channels for first set (default: false)

  • include_extra_selection2::Bool: Whether to include extra channels for second set (default: true)

Returns

  • DataFrame: Correlation matrix with first channel set as rows and second channel set as columns

Examples

julia
# Calculate correlations between all channels and EOG channels
cm = correlation_matrix_dual_selection(dat, 
    sample_selection = samples(),  # All samples
    channel_selection1 = channels(),  # All EEG channels
    channel_selection2 = channels([:vEOG, :hEOG])  # EOG channels
)

# Calculate correlations between frontal channels and EOG channels
cm = correlation_matrix_dual_selection(dat,
    sample_selection = samples_not(:is_extreme_value_100),
    channel_selection1 = channels([:Fp1, :Fp2, :F3, :F4, :F5, :F6, :F7, :F8]),
    channel_selection2 = channels([:vEOG, :hEOG])
)

# Include extra channels for EOG set but not EEG set
cm = correlation_matrix_dual_selection(dat,
    sample_selection = samples(),
    channel_selection1 = channels(),  # EEG channels only
    channel_selection2 = channels([:vEOG, :hEOG]),  # EOG channels
    include_extra_selection1 = false,  # No extra channels for EEG
    include_extra_selection2 = true    # Include extra channels for EOG
)
source
EegFun.correlation_matrix_eog Method
julia
correlation_matrix_eog(dat::SingleDataFrameEeg, eog_cfg::EogConfig; 
                      sample_selection::Function = samples(),
interval_selection::Interval = times(),
                      channel_selection::Function = channels(),
                      include_extra::Bool = false)

Calculate correlation matrix between EEG channels and EOG channels using EogConfig.

This is a convenience function that extracts EOG channel names from the EogConfig and calls correlation_matrix_dual_selection internally.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • eog_cfg::EogConfig: The EOG configuration object

  • sample_selection::Function: Function for sample selection (default: samples())

  • channel_selection::Function: Function for channel selection for EEG channels (default: channels())

  • include_extra::Bool: Whether to include extra channels (default: false)

Returns

  • DataFrame: Correlation matrix with EEG channels as rows and EOG channels as columns

Examples

julia
# Calculate correlations between all EEG channels and EOG channels
cm = correlation_matrix_eog(dat, eog_cfg)

# Calculate correlations with specific sample and channel selection
cm = correlation_matrix_eog(dat, eog_cfg,
    sample_selection = samples_not(:is_extreme_value_100),
    channel_selection = channels([:Fp1, :Fp2, :F3, :F4])
)

# Add z-score columns
cm = correlation_matrix_eog(dat, eog_cfg)
add_zscore_columns!(cm)
source
EegFun.create_continuous_repair_info Method
julia
create_continuous_repair_info(method::Symbol; name::String="continuous_repair")

Create a new ContinuousRepairInfo tracking struct for continuous data repairs. Initializes with empty repaired and skipped vectors.

Arguments

  • method::Symbol: Repair method to use

  • name::String: Optional name/identifier (default: "continuous_repair")

source
EegFun.create_custom_layout Method
julia
create_custom_layout(positions::Vector{Tuple{Float64, Float64}}, channels::Vector{Symbol})

Create a custom layout with specific positions for each channel.

source
EegFun.create_eegfun_data Method
julia
create_eegfun_data(dat::BrainVisionDataFormat.BrainVisionData, layout::Layout)::ContinuousData

Creates a ContinuousData object from a BrainVisionDataFormat data structure and a layout.

Arguments

  • dat::BrainVisionDataFormat.BrainVisionData: The BrainVisionDataFormat data structure containing EEG data.

  • layout::Layout: The layout object containing electrode information.

Returns

  • ContinuousData: ContinuousData object containing the EEG data and layout information.

Examples

julia
# Create ContinuousData from BrainVisionDataFormat data and layout
eeg_data = create_eegfun_data(brainvision_data, layout)

# Quick visualization without layout (for databrowser only)
eeg_data = create_eegfun_data(brainvision_data)
source
EegFun.create_epoch_repair_info Function
julia
create_epoch_repair_info(dat::EpochData,
                          repaired::Union{OrderedDict{Int, Vector{Symbol}}, Dict{Int, Vector{Symbol}}},
                          skipped::Union{OrderedDict{Int, Vector{Symbol}}, Dict{Int, Vector{Symbol}}},
                          method::Symbol,
                          neighbors_dict::Union{OrderedDict, Nothing} = nothing)

Create EpochRepairInfo from actual repair tracking data. The repaired and skipped should be populated during the repair process. If not already OrderedDict, will convert to OrderedDict sorted by epoch number.

source
EegFun.create_highpass_filter Method
julia
create_highpass_filter(cutoff_freq::Real, sample_rate::Real; 
filter_method::String = "iir", 
order::Integer = 2, 
transition_width::Real = 0.1)::FilterInfo

Create a digital filter object for the specified parameters.

Arguments

  • cutoff_freq: Cutoff frequency in Hz

  • sample_rate: Sampling rate in Hz

  • filter_method: String specifying filter implementation ("iir" or "fir")

  • order: Filter order for IIR filters (default: 2, becomes effective order 4 with filtfilt)

  • transition_width: Relative width of transition band as fraction of cutoff (default: 0.1 for EEG)

Returns

  • FilterInfo: A struct containing all filter information and the actual filter object

Notes

  • NOTE: When using filtfilt (default), effective filter order is approximately doubled

Examples

julia
# Create a highpass filter and plot the response
filter_info = create_highpass_filter(0.1, 256.0)
plot_filter_response(filter_info)
print_filter_characteristics(filter_info)
source
EegFun.create_layout Method
julia
create_layout(layout_spec, channels, eeg_layout; kwargs...)

Create a PlotLayout object based on the layout specification. This is a generic function that can be used by any plot type.

Keyword Arguments

  • kwargs...: Additional keyword arguments passed to the specific layout creation function (e.g., plot_width, plot_height for topo layouts)
source
EegFun.create_lowpass_filter Method
julia
create_lowpass_filter(cutoff_freq::Real, sample_rate::Real; 
filter_method::String = "iir", 
order::Integer = 2, 
transition_width::Real = 0.1)::FilterInfo

Create a digital filter object for the specified parameters.

Arguments

  • cutoff_freq: Cutoff frequency in Hz

  • sample_rate: Sampling rate in Hz

  • filter_method: String specifying filter implementation ("iir" or "fir")

  • order: Filter order for IIR filters (default: 2, becomes effective order 4 with filtfilt)

  • transition_width: Relative width of transition band as fraction of cutoff (default: 0.1 for EEG)

Returns

  • FilterInfo: A struct containing all filter information and the actual filter object

Notes

  • NOTE: When using filtfilt (default), effective filter order is approximately doubled

Examples

julia
# Create a lowpass filter and plot the response
filter_info = create_lowpass_filter(30.0, 256.0)
plot_filter_response(filter_info)
print_filter_characteristics(filter_info)
source
EegFun.create_model_rdms Method
julia
create_model_rdms(model_data::Dict{String, Any})

Create multiple model RDMs from a dictionary of different model types.

This function automatically detects the type of each model and converts it to an RDM.

Arguments

  • model_data::Dict{String, Any}: Dictionary mapping model names to their data
    • Vector{Vector{Float64}}: Feature vectors → RDM via create_rdm_from_vectors

    • Matrix{Float64}: Data matrix → RDM via create_rdm_from_matrix

    • Vector{Float64}: Reaction times (one value per condition)

    • Vector{Int}: Categorical labels

    • Symmetric Matrix{Float64}: Treated as similarity matrix or pre-computed RDM

Returns

  • model_rdms::Vector{Matrix{Float64}}: Vector of model RDMs

  • model_names::Vector{String}: Vector of model names

source
EegFun.create_rdm_from_categorical Method
julia
create_rdm_from_categorical(categories::Vector{Int})

Create an RDM from categorical labels.

Dissimilarity is 0 if conditions are in the same category, 1 otherwise.

Arguments

  • categories::Vector{Int}: Category label for each condition

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

Examples

julia
# Create RDM from category labels
categories = [1, 1, 2, 2, 3]  # 5 conditions in 3 categories
rdm = create_rdm_from_categorical(categories)
source
EegFun.create_rdm_from_distances Method
julia
create_rdm_from_distances(distances::Vector{Float64}, n_conditions::Int)

Create an RDM from a vector of pairwise distances.

The distances vector should contain all unique pairwise distances in the order: (1,2), (1,3), ..., (1,n), (2,3), ..., (2,n), ..., (n-1,n)

Arguments

  • distances::Vector{Float64}: Vector of pairwise distances (upper triangular)

  • n_conditions::Int: Number of conditions

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

Examples

julia
# Create RDM from reaction times (dissimilarity = difference in RT)
rts = [0.3, 0.5, 0.4]  # RTs for 3 conditions
distances = [abs(rts[1] - rts[2]), abs(rts[1] - rts[3]), abs(rts[2] - rts[3])]
rdm = create_rdm_from_distances(distances, 3)
source
EegFun.create_rdm_from_matrix Method
julia
create_rdm_from_matrix(
    data_matrix::Matrix{Float64};
    dissimilarity_measure::Symbol = :correlation,
)

Create an RDM from a data matrix where each row is a condition.

Input Format

Expected: Matrix{Float64} [n_conditions × n_features]

  • Each row: one condition

  • Each column: one feature

  • Example: Image features where rows are images, columns are feature dimensions

Arguments

  • data_matrix::Matrix{Float64}: Data matrix [conditions × features]

  • dissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

Examples

julia
# Image features: 3 conditions (images), 4 features per image
image_features = [
    0.1  0.2  0.3  0.4;  # Image 1 (condition 1)
    0.2  0.3  0.4  0.5;  # Image 2 (condition 2)
    0.5  0.6  0.7  0.8   # Image 3 (condition 3)
]
rdm = create_rdm_from_matrix(image_features, dissimilarity_measure=:correlation)
source
EegFun.create_rdm_from_reaction_times Method
julia
create_rdm_from_reaction_times(rts::Vector{Float64})

Create an RDM from reaction times (or any single-value-per-condition data).

Input Format

Expected: Vector{Float64} [n_conditions]

  • One value per condition

  • Order must match condition order in neural data

Arguments

  • rts::Vector{Float64}: Reaction times (or other single values) for each condition

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]
    • Dissimilarity = absolute difference between values

    • Diagonal = 0.0 (condition vs itself)

Examples

julia
# Reaction times: 3 conditions
rts = [0.3, 0.5, 0.4]  # Face=0.3s, Object=0.5s, Scene=0.4s
rdm = create_rdm_from_reaction_times(rts)

# Resulting RDM:
#            Face  Object  Scene
# Face       0.0   0.2    0.1
# Object     0.2   0.0    0.1
# Scene      0.1   0.1    0.0
source
EegFun.create_rdm_from_similarity_ratings Method
julia
create_rdm_from_similarity_ratings(
    similarity_matrix::Matrix{Float64};
    convert_to_dissimilarity::Bool = true,
)

Create an RDM from a similarity matrix (e.g., from behavioral ratings).

Arguments

  • similarity_matrix::Matrix{Float64}: Similarity matrix [condition × condition]. Higher values mean more similar.

  • convert_to_dissimilarity::Bool: Whether to convert similarity to dissimilarity (default: true)

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

Examples

julia
# Create RDM from similarity ratings (1 = very similar, 7 = very different)
similarity = [
    1.0 2.0 5.0;
    2.0 1.0 4.0;
    5.0 4.0 1.0
]
rdm = create_rdm_from_similarity_ratings(similarity, convert_to_dissimilarity=true)
source
EegFun.create_rdm_from_timeseries Method
julia
create_rdm_from_timeseries(
    temporal_data::Array{Float64, 3},
    times::Vector{Float64};
    dissimilarity_measure::Symbol = :correlation,
    align_to::Union{Vector{Float64}, RsaData, Nothing} = nothing,
    interpolation_method::Symbol = :linear,
)

Create temporal RDMs from temporal model data (e.g., eye tracking, EDA, other physiological signals).

Automatic temporal alignment: If your model data has a different sampling rate than neural data, use align_to to automatically resample to match neural timepoints.

Input Format

Expected:

  • temporal_data::Array{Float64, 3}: [n_conditions × n_features × n_timepoints]

    • Dimension 1: Conditions (e.g., Face, Object, Scene)

    • Dimension 2: Features (e.g., x_position, y_position, pupil_size for eye tracking)

    • Dimension 3: Time points

  • times::Vector{Float64}: Time vector in seconds [n_timepoints]

    • Must match length of dimension 3 in temporal_data

Arguments

  • temporal_data::Array{Float64, 3}: Temporal data [conditions × features × time]

  • times::Vector{Float64}: Time points in seconds for temporal_data

  • dissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)

  • align_to::Union{Vector{Float64}, RsaData, Nothing}: Target timepoints to align to

    • Vector{Float64}: Time vector (e.g., neural_rsa.times)

    • RsaData: Automatically uses neural_rsa.times

    • nothing: Use model's own timepoints (no alignment)

  • interpolation_method::Symbol: Interpolation method for resampling (:linear, :nearest, :cubic)

Returns

  • rdms::Array{Float64, 3}: Temporal RDMs [time × condition × condition]
    • If align_to provided: matches length of align_to timepoints

    • If align_to is nothing: matches length of input times

source
EegFun.create_rdm_from_vectors Method
julia
create_rdm_from_vectors(
    vectors::Vector{Vector{Float64}};
    dissimilarity_measure::Symbol = :correlation,
)

Create an RDM from feature vectors (e.g., word embeddings, image features).

Input Format

Expected: Vector{Vector{Float64}}

  • Outer vector: one element per condition

  • Inner vector: feature values for that condition

  • All inner vectors must have the same length (same number of features)

Arguments

  • vectors::Vector{Vector{Float64}}: Vector of feature vectors, one per condition

  • dissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)

Returns

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

Examples

julia
# Word embeddings: 3 conditions, each with 100-dimensional embedding
word_embeddings = [
    [0.1, 0.2, 0.3, ..., 0.9],  # Condition 1: 100 features
    [0.2, 0.3, 0.4, ..., 1.0],  # Condition 2: 100 features
    [0.5, 0.6, 0.7, ..., 1.2],  # Condition 3: 100 features
]
rdm = create_rdm_from_vectors(word_embeddings, dissimilarity_measure=:euclidean)
source
EegFun.create_synthetic_epochs Method
julia
create_synthetic_epochs(participant_id, condition_id, condition_name, n_epochs;
                        n_timepoints=500, channels=[:Fz, :Cz, :Pz],
                        signal_strength=1.0, noise_level=0.3) -> EpochData

Create synthetic EpochData with Gaussian-shaped condition-specific signals for MVPA/decoding testing.

Condition 1 gets a positive Gaussian bump at ~200 ms, condition 2 gets a negative bump, making the two conditions separable by a classifier.

Arguments

  • participant_id: Participant identifier (used in filename)

  • condition_id: Condition number (1 or 2)

  • condition_name: Condition label string

  • n_epochs: Number of epochs to generate

Keyword Arguments

  • n_timepoints::Int=500: Samples per epoch

  • channels::Vector{Symbol}=[:Fz, :Cz, :Pz]: Channel labels

  • signal_strength::Float64=1.0: Amplitude of the Gaussian signal

  • noise_level::Float64=0.3: Amplitude of additive Gaussian noise

  • seed::Union{Int,Nothing}=nothing: Random seed for reproducibility

Example

julia
epochs_c1 = create_synthetic_epochs(1, 1, "Cond1", 100; signal_strength=1.0, noise_level=0.3)
epochs_c2 = create_synthetic_epochs(1, 2, "Cond2", 100; signal_strength=1.0, noise_level=0.3)
source
EegFun.create_temporal_model_rdms Method
julia
create_temporal_model_rdms(
    temporal_model_data::Dict{String, Any},
    times::Vector{Float64};
    dissimilarity_measure::Symbol = :correlation,
    align_to::Union{Vector{Float64}, RsaData, Nothing} = nothing,
    interpolation_method::Symbol = :linear,
)

Create temporal model RDMs from a dictionary of temporal data.

Automatically detects the format and converts to temporal RDMs. If align_to is provided, model data will be resampled to match those timepoints (handles different sampling rates).

Arguments

  • temporal_model_data::Dict{String, Any}: Dictionary mapping model names to temporal data

    • Tuple{Array{Float64, 3}, Vector{Float64}}: (data, times) - data with its own timepoints

    • Array{Float64, 3}: Direct temporal data (uses times parameter)

    • Vector{Vector{Vector{Float64}}}: Temporal vectors (see create_rdm_from_timeseries)

  • times::Vector{Float64}: Time points in seconds (used if model data doesn't provide its own)

  • dissimilarity_measure::Symbol: Measure to use

  • align_to::Union{Vector{Float64}, RsaData, Nothing}: Target timepoints to align to

    • Vector{Float64}: Time vector (e.g., neural_rsa.times)

    • RsaData: Automatically uses neural_rsa.times

    • nothing: Use model's own timepoints (no alignment)

  • interpolation_method::Symbol: Interpolation method for resampling (:linear, :nearest, :cubic)

Returns

  • model_rdms::Vector{Array{Float64, 3}}: Vector of temporal RDMs [time × condition × condition]

  • model_names::Vector{String}: Vector of model names

source
EegFun.create_test_batch_erp_data Method
julia
create_test_batch_erp_data(; n_conditions = 2, fs = 1000, n_channels = 3, seed = nothing)

Create a vector of ErpData objects for testing batch operations.

source
EegFun.create_test_continuous_data Method
julia
create_test_continuous_data(; n::Int = 2000, fs::Int = 1000, n_channels::Int = 3, seed = nothing)

Create synthetic ContinuousData for testing.

source
EegFun.create_test_continuous_data_empty_triggers Method
julia
create_test_continuous_data_empty_triggers(; n_samples = 1000, fs = 1000, seed = nothing)

Create ContinuousData with zero triggers.

source
EegFun.create_test_continuous_data_with_artifacts Method
julia
create_test_continuous_data_with_artifacts(; n=1000, fs=1000)

Create ContinuousData with two channels: one clean and one containing large-amplitude artifacts.

source
EegFun.create_test_continuous_data_with_triggers Method
julia
create_test_continuous_data_with_triggers(; n = 1000, fs = 1000, seed = nothing)

Create synthetic ContinuousData with trigger sequences.

source
EegFun.create_test_epoch_data Method
julia
create_test_epoch_data(; n=2000, fs=1000, condition=1, n_epochs=10, n_channels=3, seed=nothing)

Create synthetic EpochData for a single condition. Returns EpochData.

source
EegFun.create_test_epoch_data_vector Method
julia
create_test_epoch_data_vector(; conditions=1:2, n_epochs=10, n_channels=3, kwargs...)

Create a Vector{EpochData} across multiple conditions.

source
EegFun.create_test_epoch_data_with_artifacts Method
julia
create_test_epoch_data_with_artifacts(; participant=1, condition=1, n_epochs=10, n_timepoints=100, n_channels=3, n_bad_epochs=1, seed=nothing)

Create EpochData with large amplitude noise in some epochs for testing artifact detection.

source
EegFun.create_test_epoch_data_with_rt Method
julia
create_test_epoch_data_with_rt(; participant=1, condition=1, n_epochs=5, n_timepoints=100, n_channels=3, kwargs...)

Create synthetic EpochData with reaction time (RT) metadata.

source
EegFun.create_test_erp_data Method
julia
create_test_erp_data(; participant = 1, condition = 1, fs = 1000, n_channels = 3, seed = nothing)

Create synthetic ErpData for testing.

source
EegFun.create_test_layout Method
julia
create_test_layout(; n_channels=4, layout_type=:grid)

Create a test Layout. Supports :grid (auto-positioned) and :topo (fixed 6-channel topographic) types.

source
EegFun.create_test_lrp_data Method
julia
create_test_lrp_data(; participant = 1, condition = 1, n_timepoints = 100, signal_scale = 1.0, fs = 250, seed = nothing)

Create ErpData with lateralized channel pairs.

source
EegFun.create_test_summary_data Method
julia
create_test_summary_data()

Create a DataFrame of per-channel summary statistics for testing artifact detection displays.

source
EegFun.create_test_summary_data_with_epochs Method
julia
create_test_summary_data_with_epochs()

Create a DataFrame of per-channel, per-epoch summary statistics for testing epoch-wise artifact displays.

source
EegFun.create_test_tf_data Method
julia
create_test_tf_data(; file, participant, condition, condition_name,
                     n_channels, frequencies, time_points, power_offset, noise, fs)

Create synthetic TimeFreqData for testing statistics.

Keyword Arguments

  • participant::Int: Participant number (default: 1), used to construct file field

  • file::String: Filename (default: "participantparticipant")

  • condition::Int: Condition number (default: 1)

  • condition_name::String: Condition label (default: "condition_1")

  • n_channels::Int: Number of electrodes (default: 3)

  • frequencies::Vector{Float64}: Frequency values in Hz (default: [4.0, 8.0, 12.0, 16.0])

  • time_points::Vector{Float64}: Time points in seconds (default: [0.0, 0.1, 0.2, 0.3, 0.4])

  • power_offset::Float64: Constant added to power (useful for creating condition differences, default: 0.0)

  • noise::Float64: Noise amplitude (default: 0.1)

  • fs::Int: Sample rate (default: 256)

Returns

  • TimeFreqData: Synthetic TF data
source
EegFun.create_test_tf_epoch_data Method
julia
create_test_tf_epoch_data(; file, participant, condition, condition_name,
                           n_channels, n_epochs, frequencies, time_points, power_offset, noise, fs)

Create synthetic TimeFreqEpochData for testing TF decoding.

Keyword Arguments

  • participant::Int: Participant number (default: 1), used to construct file field

  • file::String: Filename (default: "participantparticipant")

  • condition::Int: Condition number (default: 1)

  • condition_name::String: Condition label (default: "condition_1")

  • n_channels::Int: Number of electrodes (default: 3)

  • n_epochs::Int: Number of trials (default: 10)

  • frequencies::Vector{Float64}: Frequency values in Hz (default: [4.0, 8.0, 12.0, 16.0])

  • time_points::Vector{Float64}: Time points in seconds (default: [0.0, 0.1, 0.2, 0.3, 0.4])

  • power_offset::Float64: Constant added to power (useful for creating condition differences, default: 0.0)

  • noise::Float64: Noise amplitude (default: 0.1)

  • fs::Int: Sample rate (default: 256)

Returns

  • TimeFreqEpochData: Synthetic TF epoch data with individual trials
source
EegFun.create_work_arrays Method

Allocate and initialise WorkArrays for n_components and a given block size.

source
EegFun.decode_libsvm Method
julia
decode_libsvm(epochs::Vector{EpochData}; channel_selection = channels(),
interval_selection = times(), n_iterations::Int = 100, n_folds::Int = 3,
equalize_trials::Bool = true, cost::Float64 = 1.0, show_progress::Bool = true)
decode_libsvm(epochs::Vector{TimeFreqEpochData}; channel_selection = channels(),
interval_selection = times(), n_iterations::Int = 100, n_folds::Int = 3,
equalize_trials::Bool = true, cost::Float64 = 1.0, show_progress::Bool = true)
decode_libsvm(participant_epochs::Vector{Vector{T}}; kwargs...) where {T<:MultiDataFrameEeg}

Perform multivariate pattern classification (decoding) using LIBSVM.

Time-point-by-time-point SVM classification using k-fold cross-validation. Works with EpochData (channel features) and TimeFreqEpochData (channel × frequency features). The Vector{Vector{T}} form batch-processes all participants at once.

Keyword Arguments

  • channel_selection: Channel selection predicate (default: all channels)

  • interval_selection: Time interval selection (default: all time points)

  • n_iterations::Int: Shuffle iterations (default: 100)

  • n_folds::Int: Cross-validation folds (default: 3)

  • equalize_trials::Bool: Equalize trial counts (default: true)

  • cost::Float64: SVM regularization (default: 1.0)

  • show_progress::Bool: Show progress bar (default: true)

Returns

  • DecodedData (or Vector{DecodedData} for batch)

Example

julia
decoded = decode_libsvm(epochs; channel_selection = channels(:Cz))
all_decoded = decode_libsvm(participant_epochs; n_iterations = 100)
source
EegFun.detect_bad_epochs_automatic Method
julia
detect_bad_epochs_automatic(dat::EpochData; z_criterion::Real = 3,
                 abs_criterion::Real = 100,
                 channel_selection::Function = channels(),
                 z_measures::Vector{Symbol} = [:variance, :max, :min, :abs, :range, :kurtosis])::EpochRejectionInfo

Detect bad epochs using statistical criteria and/or absolute voltage thresholds.

This function can use two types of criteria for epoch rejection:

  1. Z-score criteria: Calculates six statistical measures for each epoch (variance, max, min, absolute max, range, kurtosis) across selected channels. For each measure, the maximum across channels is taken for each epoch, then z-scored across epochs. Epochs exceeding the z-criterion for any selected measure are rejected. You can choose which measures to apply using measures (default: [:variance, :max, :min, :abs, :range, :kurtosis]).

  2. Absolute voltage criteria: Epochs with any channel exceeding the absolute voltage threshold (in μV) are rejected.

Arguments

  • dat::EpochData: Epoched EEG data to process

  • z_criterion::Real: Z-score threshold for rejection (default: 3.0). Set to 0 to disable z-score based rejection.

  • abs_criterion::Real: Absolute voltage threshold in μV for rejection (default: 100.0). Set to 0 to disable absolute threshold rejection.

  • channel_selection::Function: Channel predicate for selecting channels to analyze (default: all channels)

  • z_measures::Vector{Symbol}: Which z-score measures to apply (default: all: [:variance, :max, :min, :abs, :range, :kurtosis])

Returns

  • EpochRejectionInfo: Information about which epochs were rejected and why

Requirements

  • At least one of z_criterion or abs_criterion must be greater than 0

Examples

julia
using EegFun, JLD2

# Load epoched data
epochs = load("participant_1_epochs.jld2")

# Use default criteria (z_criterion=3.0, abs_criterion=100.0)
rejection_info = detect_bad_epochs_automatic(epochs)

# Customize z-score criterion only
rejection_info = detect_bad_epochs_automatic(epochs, z_criterion = 2.0)

# Customize absolute voltage threshold only
rejection_info = detect_bad_epochs_automatic(epochs, abs_criterion = 80.0)  # 80 μV

# Use both criteria with custom values
rejection_info = detect_bad_epochs_automatic(epochs, z_criterion = 2.5, abs_criterion = 80.0)

# Disable z-score rejection (use only absolute threshold)
rejection_info = detect_bad_epochs_automatic(epochs, z_criterion = 0, abs_criterion = 100.0)

# Disable absolute threshold rejection (use only z-score)
rejection_info = detect_bad_epochs_automatic(epochs, z_criterion = 3.0, abs_criterion = 0)

# Check results
println("Original epochs: $(rejection_info.n_epochs)")
println("Rejected epochs: $(rejection_info.n_artifacts)")
println("Rejected epochs: $(rejection_info.rejected)")

Notes

  • Default z-criterion: 3.0

  • Default abs-criterion: 100 μV

  • Common z-criteria: 2.0 (more aggressive), 2.5, 3.0 (more conservative)

  • Common absolute criteria: 50-100 μV

  • Set either criterion to 0 to disable that type of rejection

  • Rejection is based on ANY metric exceeding the criterion

  • All metrics are calculated independently and combined with OR logic

source
EegFun.detect_bad_epochs_interactive Method
julia
detect_bad_epochs_interactive(dat::EpochData;
                         channel_selection::Function = channels(),
                         epochs_per_page::Int = 12,
                         grid_size::Tuple{Int,Int} = (3, 4))::EpochRejectionState

Create an interactive interface for visually rejecting epochs.

This function creates a Makie window displaying epochs in a grid layout with checkboxes for marking each epoch as good (✓) or bad (✗). Navigation buttons allow scrolling through pages of epochs.

Arguments

  • dat::EpochData: Epoched EEG data to review

  • channel_selection::Function: Channels to display (default: all channels)

  • artifact_info::Union{Nothing,EpochRejectionInfo}: Optional artifact detection info for bad channel filtering

  • kwargs: Additional keyword arguments

Keyword Arguments

All keyword arguments below have sensible defaults. You can override any by passing the corresponding keyword argument.

  • display_plot::Bool=true: Whether to display the plot

  • ygrid::Bool=false: Whether to show y-axis grid

  • dims::Tuple{Int64, Int64}=(4, 6): Grid dimensions as (rows, cols) for epoch display

  • colormap::Symbol=jet: Colormap for channel traces

  • yminorgrid::Bool=false: Whether to show y-axis minor grid

  • good_epoch_color::Symbol=green: Color for good epoch spines

  • ylim::Nothing=nothing: Y-axis limits as (min, max) tuple, or nothing for automatic

  • xlim::Nothing=nothing: X-axis limits as (min, max) tuple, or nothing for automatic

  • xminorgrid::Bool=false: Whether to show x-axis minor grid

  • spine_width::Int64=2: Width of axis spines

  • global_ylim::Bool=false: Whether to use global Y-axis limits across all epochs

  • xgrid::Bool=false: Whether to show x-axis grid

  • add_xy_origin::Bool=true: Whether to add origin lines at x=0 and y=0

  • theme_fontsize::Int64=20: Font size for theme

  • linewidth::Int64=1: Line width for epoch traces

  • bad_epoch_color::Symbol=red: Color for bad epoch spines

Returns

  • EpochRejectionState: State object containing rejection decisions

Usage

After reviewing all epochs and marking bad ones, extract the results:

julia
# Create interactive interface
state = detect_bad_epochs_interactive(epochs)

# After closing the window or finishing review:
rejected_indices = findall(state.rejected)
clean_epochs = epochs.data[.!state.rejected]

Examples

julia
using EegFun, JLD2

# Load epochs
epochs = load("participant_1_epochs.jld2", "epochs")

# Launch interactive rejection interface
state = detect_bad_epochs_interactive(epochs)

# Review epochs by:
# 1. Checking/unchecking boxes for each epoch
# 2. Using Previous/Next buttons to navigate
# 3. Using First/Last buttons to jump

# After review, get results
rejected_indices = findall(state.rejected)
println("Rejected epochs: $rejected_indices")

# Create cleaned dataset
clean_data = EpochData(
    epochs.data[.!state.rejected],
    epochs.layout,
    epochs.sample_rate,
    epochs.analysis_info
)

# Save
jldsave("participant_1_epochs_cleaned.jld2"; data = clean_data)

Interactive Controls

  • Checkboxes: Toggle to mark epoch as bad (checked) or good (unchecked)

  • Previous/Next: Navigate one page at a time

  • First/Last: Jump to first or last page

  • Page indicator: Shows current page number

  • Show bad channels only: Filter to display only channels that are marked as bad in any epoch (only available when artifact_info is provided)

Notes

  • Epochs are displayed with all selected channels overlaid (or only bad channels if filter is enabled)

  • Checked boxes indicate epochs to REJECT

  • Unchecked boxes indicate epochs to KEEP

  • Changes are saved in the state object

  • Close the window when done reviewing

  • Bad channels filter only appears when artifact_info is provided and contains rejected channels

source
EegFun.detect_eog_signals! Method
julia
detect_eog_signals!(dat::EegData, eog_cfg::Dict)

Detect EOG onsets for both vertical and horizontal EOG channels based on configuration.

Arguments

  • dat::EegData: The EEG data object

  • eog_cfg::Dict: EOG configuration dictionary containing vEOG and hEOG settings

Example

julia
eog_cfg = Dict(
    "vEOG_criterion" => 50.0,
    "hEOG_criterion" => 50.0,
    "vEOG_channels" => [["Fp1"], ["IO1"], ["vEOG"]],
    "hEOG_channels" => [["F9"], ["F10"], ["hEOG"]]
)
detect_eog_signals!(dat, eog_cfg)
source
EegFun.detect_eog_signals! Method
julia
detect_eog_signals!(dat::EegData, eog_cfg::EogConfig)

Detect EOG onsets for both vertical and horizontal EOG channels based on configuration.

Arguments

  • dat::EegData: The EEG data object

  • eog_cfg::EogConfig: EOG configuration object containing vEOG and hEOG settings

Example

julia
eog_cfg = EogConfig(
    vEOG_criterion = 50.0,
    hEOG_criterion = 50.0,
    vEOG_channels = [["Fp1"], ["Fp2"], ["vEOG"]],
    hEOG_channels = [["F9"], ["F10"], ["hEOG"]]
)
detect_eog_signals!(dat, eog_cfg)
source
EegFun.detrend Method
julia
detrend(x::AbstractVector, y::AbstractVector) -> Vector{Float64}

Remove linear trend from data using closed-form least squares regression. More efficient than matrix-based regression (X \ y).

Arguments

  • x::AbstractVector: Independent variable (e.g., time points)

  • y::AbstractVector: Dependent variable to detrend

Returns

  • Vector{Float64}: Detrended data with linear trend removed
source
EegFun.duration Method
julia
duration(dat::EegData) -> Float64

Get the duration of the EEG data in seconds.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Float64: Duration in seconds
source
EegFun.epoch_to_continuous Method
julia
epoch_to_continuous(dat::MultiDataFrameEeg, epoch_idx::Int) -> ContinuousData

Extract a single epoch from multi-epoch EEG data and return it as ContinuousData.

Arguments

  • dat::MultiDataFrameEeg: The multi-DataFrame EEG data

  • epoch_idx::Int: Index of the epoch to extract (1-based)

Returns

  • ContinuousData: Single DataFrame containing only the specified epoch

Examples

julia
# Extract epoch 3 as continuous data
single_dat = epoch_to_continuous(dat, 3)

# Now you can use single DataFrame functions
summary = channel_summary(single_dat)
source
EegFun.epochs Method

Predicate generators for epoch selection.

source
EegFun.epochs_not Method

Predicate generators that exclude the specified epochs.

source
EegFun.epochs_table Method
julia
epochs_table(epochs::Vector{EpochData})

Display a pretty table showing epoch information to console and return the DataFrame.

source
EegFun.erp_measurements! Method
julia
erp_measurements!(dat::ErpData, analysis_type::String; analysis_interval = times(), baseline_interval = times(), channel_selection = channels(), participant = 0, kwargs...)
erp_measurements!(dat::EpochData, analysis_type::String; kwargs...)
erp_measurements!(data::Vector{<:Union{ErpData,EpochData}}, analysis_type::String; kwargs...)
erp_measurements(file_pattern::String, analysis_type::String; analysis_interval = times(), baseline_interval = times(),
participant_selection = participants(), condition_selection = conditions(), channel_selection = channels(),
input_dir = pwd(), output_dir = nothing, output_file = "erp_measurements", kwargs...)

Extract ERP measurements from EEG data. The mutating (!) forms accumulate results in Julia; the file_pattern form batch-processes JLD2 files and saves a CSV.

analysis_type Options

Essential:

  • "mean_amplitude" — average amplitude in the analysis interval

  • "max_peak_amplitude" / "min_peak_amplitude" — robust peak detection (fallback to simple max/min)

  • "max_peak_latency" / "min_peak_latency" — timing of peak (in seconds)

Specialized:

  • "peak_to_peak_amplitude" / "peak_to_peak_latency"

  • "integral" / "rectified_area" / "positive_area" / "negative_area" — area in µV·s

  • "fractional_area_latency" / "fractional_peak_latency" — onset/offset-style latencies

Keyword Arguments

All keyword arguments below have sensible defaults. You can override any by passing the corresponding keyword argument.

  • fractional_area_fraction::Float64=0.5: Fraction for fractional area latency (0.0-1.0). Finds latency where this fraction of area is to the left.

  • fractional_peak_fraction::Float64=0.5: Fraction for fractional peak latency (0.0-1.0). Finds latency where amplitude is this fraction of peak.

  • fractional_peak_direction::Symbol=onset: Direction for fractional peak latency: :onset (before peak) or :offset (after peak)

  • local_interval::Int64=3: Number of samples on each side of peak (total interval = 2*local_interval + 1). Peak must be larger than neighbors and local averages within this window.

Examples

julia
# In-memory, single ERP
erp_measurements!(dat, "mean_amplitude", analysis_interval = (0.1, 0.2), baseline_interval = (-0.2, 0.0))

# Batch across participants
erp_measurements("erps", "max_peak_amplitude",
    analysis_interval = (0.3, 0.5),
    baseline_interval = (-0.2, 0.0),
    participant_selection = participants(1:20),
    condition_selection = conditions([1, 2]))
source
EegFun.example_path Method
julia
example_path(relative_path::String) -> String

Return the absolute path to a bundled example resource file.

This resolves paths relative to the package's resources/ directory using pkgdir, so it works regardless of the current working directory.

Arguments

  • relative_path::String: Path relative to the resources/ directory

Returns

  • String: Absolute path to the resource file

Examples

julia
# Load example BDF data
dat = read_raw_data(EegFun.example_path("data/bdf/example1.bdf"))

# Load a layout
layout = read_layout(EegFun.example_path("layouts/biosemi/biosemi72.csv"))
source
EegFun.export_bids Method
julia
export_bids(; task, raw_dir, raw_pattern, derivatives_dir, output_dir,
              layout_file, participant_map, dataset_name, dataset_authors,
              power_line_frequency, overwrite, ...)

Export an EegFun project (raw data + preprocessed outputs) to a BIDS-compliant directory structure.

The generated dataset can be validated using the BIDS Validator.

Keyword Arguments

  • task::String: Required. BIDS task label (e.g., "posner").

  • raw_dir::String: Directory containing raw data files (default: ".").

  • raw_pattern::String: Regex pattern for raw files (default: "\\.bdf").

  • derivatives_dir::String: Pipeline output directory containing JLD2 files (default: ""). If empty, only raw data is exported.

  • output_dir::String: Where to create the BIDS dataset (default: "./bids_dataset").

  • layout_file::String: Electrode layout CSV for electrode positions (default: ""). If empty, electrode/coordsystem files are skipped.

  • participant_map::Union{Nothing,Dict{String,String}}: Optional mapping from raw filenames (without extension) to BIDS subject IDs (e.g., "sub-01"). If nothing, IDs are auto-extracted from filenames.

  • dataset_name::String: Name for dataset_description.json (default: "").

  • dataset_authors::Vector{String}: Authors for dataset_description.json (default: String[]).

  • dataset_license::String: License for dataset (default: "CC0").

  • power_line_frequency::Int: Power line frequency in Hz, 50 (EU) or 60 (US) (default: 50).

  • overwrite::Bool: Overwrite existing output directory (default: false).

Recommended metadata (suppresses BIDS validator warnings)

  • task_description::String: Longer description of the task (default: "").

  • instructions::String: Instructions given to participants (default: "").

  • manufacturer::String: EEG equipment manufacturer, e.g., "BioSemi" (default: "").

  • manufacturer_model::String: Equipment model name, e.g., "ActiveTwo" (default: "").

  • cap_manufacturer::String: EEG cap manufacturer, e.g., "BioSemi" (default: "").

  • cap_model::String: Cap model name, e.g., "headcap with 72 active electrodes" (default: "").

  • institution_name::String: Name of the recording institution (default: "").

  • institution_address::String: Address of the institution (default: "").

  • institution_department::String: Department name (default: "").

  • eeg_ground::String: Description of ground electrode location (default: "").

  • eeg_placement_scheme::String: Electrode placement scheme, e.g., "10-20" (default: "").

Example

julia
EegFun.export_bids(
    task = "posner",
    raw_dir = ".",
    derivatives_dir = "./preprocessed_files",
    output_dir = "./bids_dataset",
    layout_file = "biosemi72.csv",
    dataset_name = "Visual Attention (Posner Cueing)",
    dataset_authors = ["Author A", "Author B"],
    manufacturer = "BioSemi",
    manufacturer_model = "ActiveTwo",
    cap_manufacturer = "BioSemi",
    task_description = "Visual attention task using Posner cueing paradigm",
    power_line_frequency = 50,
)
source
EegFun.extra_data Method
julia
extra_data(eeg_data::EegData) -> DataFrame

Get extra/derived columns (EOG, flags, etc.) from the EEG data.

source
EegFun.extra_labels Method
julia
extra_labels(dat::EegData) -> Vector{Symbol}

Get extra/derived column names (EOG, flags, etc.) from the EEG data.

source
EegFun.extract_epochs Method
julia
extract_epochs(dat::ContinuousData, condition::Int, epoch_condition::EpochCondition, epoch_interval::Tuple{Real,Real})

Extract epochs based on a single EpochCondition object, including timing validation, after/before filtering, trigger ranges, wildcard sequences, and multiple sequences.

Arguments

  • dat::ContinuousData: The continuous EEG data

  • condition::Int: Condition number to assign to epochs

  • epoch_condition::EpochCondition: EpochCondition object defining trigger sequence and timing constraints

  • epoch_interval::Tuple{Real,Real}: Time window relative to reference point (start_time, end_time) in seconds

Returns

  • EpochData: The extracted epochs

Examples

julia
# Single sequence
condition = EpochCondition(name="single", trigger_sequences=[[1, 2, 3]], reference_index=2)
epochs = extract_epochs(dat, 1, condition, (-0.2, 1.0))  # -200ms to 1000ms

# Multiple sequences (OR logic)
condition = EpochCondition(
    name="multiple", 
    trigger_sequences=[[1, 2, 1], [1, 3, 1]],  # Match either [1,2,1] OR [1,3,1]
    reference_index=2
)
epochs = extract_epochs(dat, 1, condition, (-0.5, 2.0))

# Wildcard sequence
condition = EpochCondition(
    name="wildcard", 
    trigger_sequences=[[1, :any, 3]],  # :any matches any trigger value
    reference_index=2
)
epochs = extract_epochs(dat, 1, condition, (-0.2, 1.0))

# Trigger ranges
condition = EpochCondition(
    name="ranges", 
    trigger_sequences=[[1:5, 10]],  # First element matches triggers 1-5, then 10
    reference_index=1
)
epochs = extract_epochs(dat, 1, condition, (-0.2, 1.0))
source
EegFun.file_name Method
julia
file_name(dat::EegFunData) -> String

Get the source filename.

source
EegFun.filter_info Method
julia
filter_info(dat::AnalysisInfo) -> Vector

Get filter information from the analysis info.

Arguments

  • dat::AnalysisInfo: The analysis info object

Returns

  • Vector: Filter information [hp_filter, lp_filter]
source
EegFun.find_file Method
julia
find_file(filename::String, search_dir::String; 
          recursive::Bool = true, extensions::Vector{String} = []) -> Union{String, Nothing}

Find a file by searching through a directory, optionally recursively.

Arguments

  • filename::String: Name of the file to find (e.g., "biosemi64.csv", "config.toml")

  • search_dir::String: Directory to search in

  • recursive::Bool: Whether to search subdirectories recursively (default: true)

  • extensions::Vector{String}: Optional file extensions to match (e.g., [".csv", ".toml"])

Returns

  • String or nothing: Full path to the file if found, nothing otherwise

Examples

julia
# Find a layout file in the layouts directory
layout_file = find_file("biosemi64.csv", "data/layouts")

# Find any CSV file with that name
csv_file = find_file("biosemi64", "data/layouts", extensions = [".csv"])

# Non-recursive search (only direct children)
direct_file = find_file("README.txt", "data/layouts", recursive = false)
source
EegFun.find_times Method
julia
find_times(time::AbstractVector, requested_times::AbstractVector) -> (indices::Vector{Int}, times::Vector{Float64})

Find nearest time points in a sorted time vector for each requested time.

For each requested time, finds the nearest matching time point in the sorted time vector and returns both the indices and the actual time values.

Arguments

  • time::AbstractVector: Sorted time vector (assumed to be in ascending order)

  • requested_times::AbstractVector: Vector of requested time points

Returns

  • indices::Vector{Int}: Indices of nearest matching time points

  • times::Vector{Float64}: Actual time values at those indices

Example

julia
time_vec = [0.0, 0.01, 0.02, 0.03, 0.04]
requested = [0.005, 0.015, 0.025]
indices, times = find_times(time_vec, requested)
# indices = [1, 2, 3]
# times = [0.0, 0.01, 0.02]
source
EegFun.freq_spectrum Method
julia
freq_spectrum(dat::EegData;
              channel_selection::Function=channels(),
              window_size::Int=256,
              overlap::Real=0.5,
              window_function::Function=DSP.hanning,
              max_freq::Union{Nothing,Real}=nothing)

Compute power spectrum using Welch's method for EEG data.

This function computes the frequency-domain power spectrum (no time dimension) using Welch's method with overlapping windows. For EpochData, power is averaged across epochs. For ErpData and ContinuousData (SingleDataFrameEeg), power is computed directly from the single DataFrame.

Arguments

  • dat::EegData: EEG data (EpochData, ErpData, or ContinuousData)

Keyword Arguments

  • channel_selection::Function=channels(): Channel selection predicate. See channels() for options.

    • Example: channel_selection=channels(:Cz) for single channel

    • Example: channel_selection=channels([:Cz, :Pz]) for multiple channels

  • window_size::Int=1024: Size of the FFT window for spectral estimation (in samples)

  • overlap::Real=0.5: Overlap fraction between windows (0.0 to 1.0). Default is 0.5 (50% overlap)

  • window_function::Function=DSP.hanning: Window function for spectral estimation

    • Options: DSP.hanning, DSP.hamming, DSP.blackman, etc.
  • max_freq::Union{Nothing,Real}=nothing: Maximum frequency to return in Hz.

    • If nothing, returns all frequencies up to Nyquist

    • If specified, filters results to frequencies <= max_freq

Returns

  • SpectrumData: Power spectrum data structure with:
    • data::DataFrame: DataFrame with columns: freq, [electrode channels...] containing power spectral density (μV²/Hz)

    • Other metadata: file, condition, condition_name, layout, sample_rate, method, analysis_info

Example

julia
# Compute power spectrum for all channels
spectrum = freq_spectrum(epochs)

# Single channel with custom parameters
spectrum = freq_spectrum(epochs; channel_selection=channels(:Cz), window_size=2048, max_freq=100.0)
source
EegFun.generate_config_template Method
julia
generate_config_template(; filename::String="config_template.toml")

Generate and save a template TOML configuration file with all available parameters and their default values.

Arguments

  • filename::String: Name of the template file to create (keyword argument, default: "config_template.toml")
source
EegFun.generate_pipeline_template Function
julia
generate_pipeline_template(
    output_file::String = "custom_pipeline.jl",
    function_name::String = "custom_preprocess";
    options::PipelineTemplateOptions = PipelineTemplateOptions(),
)

Generate a template file for users to create their own preprocessing pipelines. The template includes standard setup (logging, config loading, error handling) and provides a structured framework with sections and subsections for custom processing.

Why use this?

This function generates a complete preprocessing pipeline template that handles all the boilerplate code required for a robust EEG preprocessing workflow. Instead of manually writing repetitive setup code (logging initialization, configuration loading, file validation, error handling, etc.), you can generate a ready-to-use template that:

  • Sets up global and per-file logging with proper error handling

  • Loads and validates configuration files (user config + default config merging)

  • Checks for required input files and creates output directories

  • Initializes layout files and neighbor calculations

  • Provides structured sections for your custom processing steps

  • Handles errors gracefully with try-catch blocks and proper cleanup

  • Generates processing summaries and reports

You can then focus on writing your specific preprocessing logic in the designated sections, rather than spending time on infrastructure code. The generated template follows best practices and ensures consistency across different custom pipelines.

Arguments

  • output_file::String: Path where the template file should be saved

  • function_name::String: Name for the main preprocessing function

  • options::PipelineTemplateOptions: Configuration options for template generation

    • num_steps::Int: Number of processing steps to generate (default: 5)

    • subsections_per_step::Int: Number of subsections per step (default: 2)

    • include_usage_example::Bool: Whether to include usage example comments (default: true)

    • include_setup::Bool: Whether to include setup section (default: true)

    • include_summary::Bool: Whether to include summary section (default: true)

Examples

julia
# Generate a basic template
generate_pipeline_template()

# Generate with custom options
generate_pipeline_template(
    "my_pipeline.jl",
    "my_preprocess";
    options = PipelineTemplateOptions(num_steps = 3, subsections_per_step = 3)
)
source
EegFun.generate_signal Method
julia
generate_signal(n_trials, time_window, sample_rate, signal_freqs, signal_amps, signal_times, noise)

Generate synthetic signals with known frequency components for testing TF analysis.

Arguments

  • n_trials: Number of trials to generate

  • time_window: [start_time, end_time] in seconds

  • sample_rate: Sampling rate in Hz

  • signal_freqs: Vector of frequencies for each component

  • signal_amps: Vector of amplitudes for each component

  • signal_times: Vector of [start_time, end_time] for each component

  • noise: Amplitude of random noise

Returns

  • times: Time vector

  • signal: Matrix (samples × trials)

source
EegFun.get_all_ica_components Method
julia
get_all_components(artifacts::ArtifactComponents)

Get all unique artifact component indices as a single vector.

Arguments

  • artifacts::ArtifactComponents: The artifact components structure

Returns

  • Vector{Int}: Sorted vector of all unique artifact component indices
source
EegFun.get_eog_channels Method
julia
get_eog_channels(eog_cfg::EogConfig)

Extract EOG channel symbols from EogConfig.

Arguments

  • eog_cfg::EogConfig: The EOG configuration object

Returns

  • Vector{Symbol}: Vector of EOG channel symbols (e.g., [:vEOG, :hEOG])

Examples

julia
eog_cfg = EogConfig(50.0, 30.0, [["Fp1", "Fp2"], ["IO1", "IO2"], ["vEOG"]], [["F9"], ["F10"], ["hEOG"]])
eog_channels = get_eog_channels(eog_cfg)
# Returns [:vEOG, :hEOG]
source
EegFun.get_file_extension Method
julia
get_file_extension(filename::String) -> String

Get the lowercase file extension including the dot (e.g., ".bdf", ".vhdr").

source
EegFun.get_filter_characteristics Method
julia
get_filter_characteristics(filter, sample_rate::Real, transition_width::Real; npoints::Int=1000)

Calculate and return key characteristics of a digital filter.

Arguments

  • filter: A digital filter object (FIR coefficients or DSP.jl filter)

  • sample_rate::Real: Sampling rate in Hz

  • transition_width::Real: Width of transition band in Hz

  • npoints::Int: Number of frequency points for analysis (default: 1000)

Returns

A NamedTuple containing:

  • cutoff_freq_3db: Frequency at -3dB point(s) in Hz

  • cutoff_freq_6db: Frequency at -6dB point(s) in Hz

  • passband_ripple: Maximum ripple in passband in dB

  • stopband_atten: Mean attenuation in stopband in dB

  • phase_delay: Mean phase delay in passband (samples)

  • group_delay: Mean group delay in passband (samples)

  • transition_band: Width of transition band in Hz

  • filter_type: Detected filter type ("hp" or "lp")

source
EegFun.get_neighbours_xy! Method
julia
get_neighbours_xy!(layout::Layout, distance_criterion::Real)

Get neighbors with automatic computation if needed (mutating version).

This function automatically calculates 2D neighbors if they don't exist or if the distance criterion has changed. It caches the results for efficiency.

Arguments

  • layout::Layout: The layout object

  • distance_criterion::Real: The distance criterion for neighbors in normalized coordinate units (e.g., 0.3–0.5; where 1.0 = the scalp equator at 90° incidence). Think of this as a fraction of the head radius: a value of 0.4 means “neighbours within 40% of the head radius.”

Modifies

  • layout: Updates neighbor information and criterion

Examples

julia
get_neighbours_xy!(layout, 0.4)
source
EegFun.get_neighbours_xyz! Method
julia
get_neighbours_xyz!(layout::Layout, distance_criterion::Real)

Get neighbors with automatic computation if needed (mutating version).

This function automatically calculates 3D neighbors if they don't exist or if the distance criterion has changed. It caches the results for efficiency.

Arguments

  • layout::Layout: The layout object

  • distance_criterion::Real: The distance criterion for neighbors in normalized coordinate units (e.g., 0.3–0.5; where 1.0 = the scalp equator at 90° incidence). Think of this as a fraction of the head radius: a value of 0.4 means “neighbours within 40% of the head radius.”

source
EegFun.get_package_version Method
julia
get_package_version(; package_name::String = "EegFun")

Get the package version from Project.toml.

It locates the package using Base.find_package() and reads Project.toml from the package root.

Returns

  • String: The version string from Project.toml, or "unknown" if not available/problem
source
EegFun.get_rejected Method
julia
get_rejected(info::EpochRejectionInfo)::Vector{Rejection}

Get the list of rejected channel-epoch pairs from the rejection info.

Examples

julia
info = detect_bad_epochs_automatic(epochs)
rejected = get_rejected(info)
source
EegFun.get_rejected_epochs Method
julia
get_rejected_epochs(state::EpochRejectionState)::Vector{Int}

Get indices of rejected epochs from the rejection state.

Examples

julia
state = detect_bad_epochs_interactive(epochs)
# ... after review ...
rejected_indices = get_rejected_epochs(state)
source
EegFun.get_selected_channels Method

Apply a channel predicate to data and return the matching column names (with optional metadata/extra).

source
EegFun.get_selected_channels Method
julia
get_selected_channels(layout::Layout, channel_selection::Function) -> Vector{Symbol}

Get channel labels that match the selection criteria.

This helper function applies a channel selection predicate to the layout's electrode labels and returns the matching channels.

Arguments

  • layout::Layout: The layout object

  • channel_selection::Function: Channel selection predicate function

Returns

  • Vector{Symbol}: Vector of selected channel labels

Examples

julia
selected = get_selected_channels(layout, channels([:Fp1, :Fp2]))
source
EegFun.get_selected_components Method

Apply a component predicate and return matching indices.

source
EegFun.get_selected_conditions Method

Apply a condition predicate and return matching dataset indices.

source
EegFun.get_selected_epochs Method
julia
get_selected_epochs(dat::EpochData, epoch_selection::Function) -> Vector{Int}

Get the indices of epochs that match the epoch selection predicate.

Arguments

  • dat::EpochData: The EpochData object containing the epochs

  • epoch_selection::Function: Function that returns boolean vector for epoch filtering

Returns

  • Vector{Int}: Indices of selected epochs

Examples

julia
# Get all epochs
selected = get_selected_epochs(dat, epochs())

# Get specific epochs
selected = get_selected_epochs(dat, epochs(1:10))

# Get epochs matching a condition
selected = get_selected_epochs(dat, epochs([1, 3, 5]))
source
EegFun.get_selected_epochs Method

Apply an epoch predicate and return matching epoch indices.

source
EegFun.get_selected_samples Method

Apply a sample predicate and return matching row indices.

source
EegFun.gfp Method
julia
gfp(dat::ErpData; 
    channel_selection::Function = channels(),
    normalize::Bool = false)::DataFrame

Calculate Global Field Power (GFP) for ERP data.

Global Field Power is computed as the standard deviation across channels at each time point, providing a reference-independent measure of global response strength.

Arguments

  • dat::ErpData: ERP data structure

  • channel_selection::Function: Channel predicate for selecting channels to include (default: all channels)

  • normalize::Bool: If true, normalize GFP to 0-100% range (default: false)

Returns

  • DataFrame: Contains columns :time, :gfp, and metadata columns (:condition, :condition_name, etc.)

Examples

julia
using EegFun

# Load ERP data
erp_data = EegFun.read_data("participant_1_erps.jld2")

# Calculate GFP using all channels
gfp_result = gfp(erp_data)

# Calculate GFP using specific channels
gfp_result = gfp(erp_data, channel_selection = channels([:C3, :C4, :Cz, :CP3, :CP4, :CPz]))

# Calculate normalized GFP (0-100%)
gfp_result = gfp(erp_data, normalize = true)

# Access the values
time_vector = gfp_result.time
gfp_values = gfp_result.gfp

Notes

  • GFP is reference-independent and reflects the overall strength of the electric field

  • High GFP values indicate strong, synchronized activity across channels

  • Low GFP values indicate weak or desynchronized activity

  • Normalization to 0-100% is useful for comparing across different datasets or conditions

source
EegFun.gfp_and_dissimilarity Method
julia
gfp_and_dissimilarity(dat::ErpData;
                      channel_selection::Function = channels(),
                      normalize::Bool = false)::DataFrame

Calculate both Global Field Power and Global Dissimilarity in one call.

This is a convenience function that computes both metrics efficiently.

Arguments

  • dat::ErpData: ERP data structure

  • channel_selection::Function: Channel predicate for selecting channels (default: all channels)

  • normalize::Bool: If true, normalize both metrics to 0-100% range (default: false)

Returns

  • DataFrame: Contains columns :time, :gfp, :dissimilarity, and metadata columns

Examples

julia
# Calculate both metrics
result = gfp_and_dissimilarity(erp_data)

# Access the values
time_vector = result.time
gfp_values = result.gfp
dissimilarity_values = result.dissimilarity

# With normalization
result = gfp_and_dissimilarity(erp_data, normalize = true)
source
EegFun.global_dissimilarity Method
julia
global_dissimilarity(dat::ErpData;
                     channel_selection::Function = channels(),
                     normalize::Bool = false)::DataFrame

Calculate Global Dissimilarity (GD) for ERP data.

Global Dissimilarity measures the rate of topographic change by quantifying how much the normalized potential distribution changes from one time point to the next.

Arguments

  • dat::ErpData: ERP data structure

  • channel_selection::Function: Channel predicate for selecting channels (default: all channels)

  • normalize::Bool: If true, normalize GD to 0-100% range (default: false)

Returns

  • DataFrame: Contains columns :time, :dissimilarity, and metadata columns

Examples

julia
# Calculate global dissimilarity
gd_result = global_dissimilarity(erp_data)

# With specific channels and normalization
gd_result = global_dissimilarity(erp_data, 
                                 channel_selection = channels([:C3, :C4, :Cz]),
                                 normalize = true)

Notes

  • Global dissimilarity peaks indicate moments of rapid topographic change

  • These peaks may indicate transitions between different brain states or ERP components

  • Normalization to 0-100% facilitates comparison across datasets

source
EegFun.grand_average Method
julia
grand_average(erps::Vector{ErpData}; condition_selection = conditions()) -> Vector{ErpData}
grand_average(tfs::Vector{TimeFreqData}; condition_selection = conditions()) -> Vector{TimeFreqData}
grand_average(rsa_data_list::Vector{RsaData}; compute_noise_ceiling::Bool = true) -> RsaData
grand_average(decoded_list::Vector{DecodedData}) -> DecodedData
grand_average(file_pattern::String; input_dir::String = pwd(),
participant_selection::Function = participants(), condition_selection::Function = conditions(),
output_dir = nothing)

Create grand averages by averaging across participants.

For ErpData / TimeFreqData: groups by condition and averages across participants. For RsaData: averages RDMs at each time point with optional noise ceiling. For DecodedData: averages classification accuracy across participants. The file_pattern method batch-processes JLD2 ERP files.

Examples

julia
grand_avgs = grand_average([erp1, erp2, erp3])
grand_avgs = grand_average(erps, condition_selection = conditions([1, 3]))
grand_avg_rsa = grand_average([rsa_p1, rsa_p2, rsa_p3])
grand_avg_decoding = grand_average([decoded_p1, decoded_p2])
grand_average("erps_cleaned")
source
EegFun.group_by_condition Method
julia
group_by_condition(items::Vector{T}) where {T}

Group items by their .condition field.

Arguments

  • items::Vector{T}: Items to group (must have a .condition field, e.g. ErpData, EpochData)

Returns

  • OrderedDict{Int, Vector{T}}: Items grouped by condition number (sorted)
source
EegFun.has_2d_coords Method
julia
has_2d_coords(layout::Layout) -> Bool

Check if the layout has 2D Cartesian coordinates.

Arguments

  • layout::Layout: The layout object

Returns

  • Bool: True if x2 and y2 columns exist
source
EegFun.has_3d_coords Method
julia
has_3d_coords(layout::Layout) -> Bool

Check if the layout has 3D Cartesian coordinates.

Arguments

  • layout::Layout: The layout object

Returns

  • Bool: True if x3, y3, and z3 columns exist
source
EegFun.has_channels Method
julia
has_channels(dat::EegData, chans::Vector{Symbol}) -> Bool

Check if the EEG data contains all specified channels.

Arguments

  • dat::EegData: The EEG data object

  • chans::Vector{Symbol}: Vector of channel symbols to check

Returns

  • Bool: True if all channels are present
source
EegFun.has_neighbours Method
julia
has_neighbours(layout::Layout) -> Bool

Check if the layout has calculated neighbor information.

Arguments

  • layout::Layout: The layout object

Returns

  • Bool: True if neighbors have been calculated and stored

Examples

julia
if has_neighbours(layout)
    # Use neighbor information
end
source
EegFun.has_valid_coordinates Method
julia
has_valid_coordinates(layout::Layout) -> Bool

Check if the layout has non-zero coordinates suitable for topographic plotting. Auto-generated layouts have all zeros which cannot be used for spatial visualization.

Arguments

  • layout::Layout: The layout object

Returns

  • Bool: True if layout has non-zero inc/azi coordinates

Examples

julia
if !has_valid_coordinates(layout)
    error("Cannot create topographic plot without a proper electrode layout")
end
source
EegFun.head Method
julia
head(dat::EegData; n=5)

Display and return the first n rows of the EEG data.

Examples

julia
head(erps[1])        # First 5 rows
head(erps[1], n=10)  # First 10 rows
source
EegFun.highpass_filter Function
julia
highpass_filter(dat::EegFun.EegData, cutoff_freq::Real; kwargs...)

Non-mutating version of highpass_filter!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.highpass_filter! Method

Apply highpass filter using settings from a FilterConfig section.

source
EegFun.highpass_filter! Method
julia
highpass_filter!(dat::EegData, cutoff_freq::Real; kwargs...)

Apply a digital highpass filter to EEG data. Modifies the data in place.

Arguments

  • dat::EegData: EegData object (ContinuousData, ErpData, or EpochData)

  • cutoff_freq::Real: Cutoff frequency in Hz

Keyword Arguments

  • order::Integer: Filter order (default: 1)

  • transition_width::Real: Relative width of transition band (default: 0.25)

  • filter_method::String: Filter implementation ("iir" or "fir")

  • channel_selection::Function: Channel selection predicate (default: channels() - all channels)

  • filter_func::String: Filtering function ("filtfilt" or "filt")

source
EegFun.highpass_filter Method
julia
highpass_filter(file_pattern::String, cutoff_freq::Real; kwargs...)

Batch highpass filter EEG/ERP data from JLD2 files.

source
EegFun.identify_bad_channels Method
julia
identify_bad_channels(summary_df::DataFrame, joint_prob_df::DataFrame; 
                     zvar_criterion::Real = 3.0)::Vector{Symbol}

Identify bad channels based on channel summary z-variance and joint probability criteria.

Arguments

  • summary_df::DataFrame: Channel summary DataFrame (output from channel_summary)

  • joint_prob_df::DataFrame: Joint probability DataFrame (output from channel_joint_probability)

  • zvar_criterion::Real: Z-variance threshold for bad channel identification (default: 3.0)

Returns

  • Vector{Symbol}: Vector of channel names identified as bad channels

Examples

julia
# Get channel summary and joint probability
summary_df = channel_summary(dat)
joint_prob_df = channel_joint_probability(dat)

# Identify bad channels with default criteria
bad_channels = identify_bad_channels(summary_df, joint_prob_df)

# Use custom z-variance criterion
bad_channels = identify_bad_channels(summary_df, joint_prob_df, zvar_criterion = 2.5)
source
EegFun.identify_components Method
julia
identify_components(dat::ContinuousData, ica::InfoIca; sample_selection::Function = samples(),
interval_selection::Interval = times(), kwargs...)

Identify all types of artifact components in one unified call.

Arguments

  • dat::ContinuousData: The continuous data

  • ica::InfoIca: The ICA result object

  • sample_selection::Function: Sample selection function (default: samples())

  • kwargs...: Additional keyword arguments passed to individual identification functions

Returns

  • ArtifactComponents: Combined structure containing all identified artifact components

  • Dict{Symbol, DataFrame}: Dictionary containing metrics DataFrames for each component type:

    • :eog_metrics: EOG component metrics

    • :ecg_metrics: ECG component metrics

    • :line_noise_metrics: Line noise component metrics

    • :channel_noise_metrics: Channel noise component metrics

Examples

julia
# Basic usage
artifacts, metrics = identify_components(dat, ica_result)

# With custom sample selection
artifacts, metrics = identify_components(dat, ica_result, 
    sample_selection = samples_not(:is_extreme_value_100))

# Access specific metrics
eog_metrics = metrics[:eog_metrics]
ecg_metrics = metrics[:ecg_metrics]
source
EegFun.identify_ecg_components Method
julia
identify_ecg_components(dat::ContinuousData, ica::InfoIca;
                          min_bpm::Real=50, max_bpm::Real=110,
                          min_prominence_std::Real=4,
                          min_peaks::Int=100,
                          max_ibi_std_s::Real=0.2,
                          min_peak_ratio::Real=0.5,
                          sample_selection::Function = samples(),
interval_selection::Interval = times(),

)

Identify ICA components potentially related to ECG artifacts based on peak detection and interval regularity, using only samples consistent with ICA calculation.

Arguments

  • dat::ContinuousData: The continuous data (needed for sampling rate and sample selection columns).

  • ica::InfoIca: The ICA result object.

Keyword Arguments

  • min_bpm::Real: Minimum plausible heart rate in beats per minute (default: 40).

  • max_bpm::Real: Maximum plausible heart rate in beats per minute (default: 120).

  • min_prominence_std::Real: Minimum peak prominence in standard deviations above mean for z-scored time series (default: 4).

  • min_peaks::Int: Minimum number of valid inter-beat intervals required (default: 100). Note: Since num_valid_ibis is the number of valid intervals between peaks (which is num_peaks - 1 if all are valid), the check uses num_valid_ibis >= (min_peaks - 1) to account for this relationship.

  • max_ibi_std_s::Real: Maximum standard deviation of the inter-beat intervals (in seconds) for component to be flagged (default: 0.2).

  • min_peak_ratio::Real: Minimum ratio of valid inter-beat intervals to total inter-peak intervals (default: 0.5). This ensures that a sufficient proportion of detected peaks fall within the plausible heart rate range.

  • sample_selection::Function: Function to select samples from dat.data. Defaults to samples(). Only selected samples are used for component calculation and peak detection.

Returns

  • Vector{Int}: Sorted vector of indices identified as potential ECG components.

  • DataFrame: DataFrame containing metrics for each component (calculated on the selected samples):

    • :Component: Component index (1 to n).

    • :num_peaks: Number of detected prominent peaks.

    • :num_valid_ibis: Number of inter-beat intervals within the plausible BPM range.

    • :mean_ibi_s: Mean inter-beat interval in seconds (if num_valid_ibis > 0).

    • :std_ibi_s: Standard deviation of inter-beat intervals in seconds (if num_valid_ibis > 1).

    • :peak_ratio: Ratio of valid inter-beat intervals to total inter-peak intervals.

    • :heart_rate_bpm: Estimated heart rate in beats per minute (if mean_ibi_s is valid).

source
EegFun.identify_eog_components Method
julia
identify_eog_components(dat::ContinuousData, ica::InfoIca;
                      vEOG_channel::Symbol=:vEOG,
                      hEOG_channel::Symbol=:hEOG,
                      z_threshold::Float64=3.0,
                      two_step::Bool=true,
                      sample_selection::Function = samples())

Identify ICA components potentially related to eye movements based on z-scored correlation with EOG channels.

Uses a two-step approach by default:

  1. Step 1: Calculate correlations between all ICA components and EOG channels, compute standard z-scores, and identify primary EOG components (those exceeding both the z-score and correlation thresholds).

  2. Step 2: Calculate spatial correlations (topographies) between remaining components and primary EOG components. Identify secondary EOG components based on spatial correlation threshold. This approach validates that secondary components share similar spatial patterns with primary components, which is more aligned with ICA principles.

This two-step approach helps detect secondary EOG components that share spatial topographies with primary components, even if their temporal correlations with EOG channels are not as strong.

Arguments

  • dat::ContinuousData: The continuous data containing EOG channels.

  • ica::InfoIca: The ICA result object.

Keyword Arguments

  • vEOG_channel::Symbol: Name of the vertical EOG channel (default: :vEOG).

  • hEOG_channel::Symbol: Name of the horizontal EOG channel (default: :hEOG).

  • z_threshold::Float64: Absolute Z-score threshold for step1 identification (default: 3.0). Primary components must exceed this z-score threshold to be identified in step1.

  • min_correlation::Float64: For step1, minimum absolute correlation threshold with EOG channels (default: 0.6). Primary components must exceed this correlation threshold in addition to the z-score threshold. For step2, this is the minimum spatial correlation threshold with primary component topographies required to identify secondary components.

  • two_step::Bool: If true (default), use two-step identification: step1 identifies primary components based on z-score and EOG correlation, step2 identifies secondary components based on spatial correlation with primary components. If false, use single-step identification with standard z-scores and correlation thresholds on all components simultaneously.

  • sample_selection::Function: Function to select samples from dat.data. Defaults to samples(). Only selected samples are used for correlation calculation.

Returns

  • Dict{Symbol, Vector{Int}}: Dictionary containing:

    • :vEOG: Vector of component indices identified for vertical eye movements.

    • :hEOG: Vector of component indices identified for horizontal eye movements.

  • DataFrame: DataFrame containing detailed metrics per component:

    • :Component: Component index (1 to n_components)

    • :vEOG_corr: Absolute correlation with vertical EOG channel

    • :vEOG_zscore or :vEOG_zscore_step1: Z-score of correlation with vertical EOG channel. When two_step=true, only step1 z-scores are provided.

    • :vEOG_spatial_corr: (Only when two_step=true) Maximum spatial correlation (topography) with any primary vEOG component. NaN for primary vEOG components identified in step1.

    • :vEOG_spatial_corr_z: Z-score of :vEOG_spatial_corr.

    • :vEOG_temporal_corr: (Only when two_step=true) Maximum lagged temporal correlation with any primary vEOG component. NaN for primary vEOG components identified in step1.

    • :vEOG_temporal_corr_z: Z-score of :vEOG_temporal_corr.

    • :hEOG_corr: Absolute correlation with horizontal EOG channel

    • :hEOG_zscore or :hEOG_zscore_step1: Z-score of correlation with horizontal EOG channel. When two_step=true, only step1 z-scores are provided.

    • :hEOG_spatial_corr: (Only when two_step=true) Maximum spatial correlation (topography) with any primary hEOG component. NaN for primary hEOG components identified in step1.

    • :hEOG_spatial_corr_z: Z-score of :hEOG_spatial_corr.

    • :hEOG_temporal_corr: (Only when two_step=true) Maximum lagged temporal correlation with any primary hEOG component. NaN for primary hEOG components identified in step1.

    • :hEOG_temporal_corr_z: Z-score of :hEOG_temporal_corr.

Examples

julia
# Basic usage with default two-step approach
eog_comps, metrics = identify_eog_components(dat, ica_result)

# Use single-step identification
eog_comps, metrics = identify_eog_components(dat, ica_result, two_step=false)

# Custom threshold and sample selection
eog_comps, metrics = identify_eog_components(dat, ica_result, 
    z_threshold=2.5,
    sample_selection=samples_not(:is_extreme_value_100))
source
EegFun.identify_line_noise_components Method
julia
identify_line_noise_components(dat::ContinuousData, ica::InfoIca;
                             line_freq::Real=50.0,
                             freq_bandwidth::Real=1.0,
                             z_threshold::Float64=3.0,
                             min_harmonic_power::Real=0.5)

Identify ICA components with strong line noise characteristics using spectral peakiness.

Spectral peakiness compares power at the line frequency to nearby flanking frequencies (±5 Hz excluding the line band), rather than to the entire broadband spectrum. This avoids false positives from components with generally elevated power that happens to include the line frequency range.

Arguments

  • dat::ContinuousData: The continuous data.

  • ica::InfoIca: The ICA result object.

Keyword Arguments

  • sample_selection::Function: Function to select samples (default: samples()).

  • line_freq::Real: Line frequency in Hz (default: 50.0 for European power).

  • freq_bandwidth::Real: Bandwidth around line frequency to consider (default: 1.0 Hz).

  • z_threshold::Float64: Z-score threshold for identifying line noise components (default: 3.0).

  • min_harmonic_power::Real: Minimum peakiness ratio of 2nd harmonic (100 Hz vs. its flanking frequencies) (default: 1.5).

Returns

  • Vector{Int}: Indices of components with strong line noise characteristics.

  • DataFrame: DataFrame containing spectral metrics for all components:

    • :Component: Component index

    • :line_power: Mean power at the line frequency band

    • :flanking_power: Mean power at flanking frequencies (used as baseline for peakiness)

    • :power_ratio: Spectral peakiness ratio (line_power / flanking_power)

    • :harmonic_ratio: Spectral peakiness ratio at the 2nd harmonic

    • :power_ratio_zscore: Z-score of power_ratio across components

source
EegFun.identify_spatial_kurtosis_components Method
julia
identify_spatial_kurtosis_components(ica::InfoIca; z_threshold::Float64 = 3.0)

Identify ICA components with high spatial kurtosis (localized, spot-like activity).

Spatial kurtosis measures how localized the component's topography is. High spatial kurtosis indicates that the component's activity is concentrated in a small number of channels (spot-like), which is characteristic of channel noise or artifacts.

Arguments

  • ica::InfoIca: The ICA result object.

Keyword Arguments

  • z_threshold::Float64: Z-score threshold for identifying high spatial kurtosis components (default: 3.0).

Returns

  • Vector{Int}: Indices of components with high spatial kurtosis.

  • DataFrame: DataFrame containing spatial kurtosis values and z-scores for all components.

source
EegFun.infomax_extended_ica Method
julia
infomax_extended_ica(dat_ica::Matrix{Float64}, layout::Layout; n_components::Int, params::IcaPrms = IcaPrms())

Extended Infomax algorithm implementation that can separate both sub-Gaussian and super-Gaussian sources.

Extended Infomax is an enhancement of the standard Infomax algorithm that adapts to the statistical properties of source signals. It uses kurtosis-based switching to handle both sub-Gaussian (kurtosis < 0) and super-Gaussian (kurtosis > 0) sources, making it more versatile than standard Infomax.

Arguments

  • dat_ica::Matrix{Float64}: Data matrix (channels × samples), should be preprocessed

  • layout::Layout: Layout information for channels

  • n_components::Int: Number of ICA components to extract

  • params::IcaPrms: ICA parameters (uses max_iter, w_change from params)

Returns

InfoIca with unmixing, mixing, sphere, variance, and metadata.

References

Lee, T. W., Girolami, M., & Sejnowski, T. J. (1999). Independent component analysis using an extended infomax algorithm for mixed subgaussian and supergaussian sources. Neural computation, 11(2), 417-441.

source
EegFun.infomax_ica Method

Standard Infomax ICA implementation (super-Gaussian sources only).

source
EegFun.is_equal_rejection Method

Check whether two Rejection objects refer to the same channel and epoch.

source
EegFun.is_extreme_value! Method
julia
is_extreme_value!(dat::SingleDataFrameEeg, threshold::Real; channel_selection = channels(),
sample_selection = samples(), interval_selection = times(), mode::Symbol = :combined, channel_out = nothing)
is_extreme_value!(dat::MultiDataFrameEeg, threshold::Real; channel_selection = channels(),
sample_selection = samples(), interval_selection = times(), epoch_selection = epochs(), mode::Symbol = :combined, channel_out = nothing)
is_extreme_value!(epochs_list::Vector{EpochData}, threshold::Real; kwargs...)

Detect extreme values and flag them in-place. Adds a boolean column (:is_extreme_value_<threshold> by default) to the data. The Vector{EpochData} form broadcasts across conditions.

Keyword Arguments

  • channel_selection: Channels to check (default: all layout channels)

  • mode::Symbol: :combined (single OR'd column) or :separate (one column per channel)

  • channel_out: Output column name (default: auto-generated)

Examples

julia
is_extreme_value!(dat, 100)             # → :is_extreme_value_100
is_extreme_value!(dat, 100, mode = :separate)
is_extreme_value!(epoch_data, 100, epoch_selection = epochs([1, 3]))
is_extreme_value!(epochs_cleaned, 100)  # batch over conditions
source
EegFun.is_extreme_value Method
julia
is_extreme_value(dat::SingleDataFrameEeg, threshold::Real; channel_selection = channels(),
sample_selection = samples(), interval_selection = times(), mode::Symbol = :combined)

Detect extreme values without modifying data. Returns Vector{Bool} (:combined mode) or a DataFrame (:separate mode).

Examples

julia
extreme_mask = is_extreme_value(dat, 100)
results = is_extreme_value(dat, 100, mode = :separate)
source
EegFun.is_step_value! Method
julia
is_step_value!(dat::SingleDataFrameEeg, threshold::Real; channel_selection = channels(),
sample_selection = samples(), interval_selection = times(), mode::Symbol = :combined, channel_out = nothing)
is_step_value!(dat::MultiDataFrameEeg, threshold::Real; channel_selection = channels(),
sample_selection = samples(), interval_selection = times(), epoch_selection = epochs(), mode::Symbol = :combined, channel_out = nothing)
is_step_value!(epochs_list::Vector{EpochData}, threshold::Real; kwargs...)

Detect step artifacts (sudden voltage jumps) and flag them in-place. Adds a boolean column (:is_step_value_<threshold> by default). The Vector{EpochData} form broadcasts across conditions.

Examples

julia
is_step_value!(dat, 50.0)             # → :is_step_value_50.0
is_step_value!(dat, 50.0, mode = :separate)
is_step_value!(epoch_data, 50.0, epoch_selection = epochs([1, 3]))
is_step_value!(all_epochs, 50.0)      # batch over conditions
source
EegFun.is_step_value Method
julia
is_step_value(dat::SingleDataFrameEeg, threshold::Real; 
              channel_selection::Function = channels(), 
              sample_selection::Function = samples(),
              interval_selection::Interval = times(),
              mode::Symbol = :combined)

Detect step values (sudden voltage jumps) across selected channels and return results without modifying the input data.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • threshold::Real: Threshold for step detection (in μV)

  • channel_selection::Function: Channel predicate for selecting channels (default: all layout channels)

  • sample_selection::Function: Sample predicate for selecting samples (default: all samples)

  • interval_selection::Interval: Time interval for selection (default: all times)

  • mode::Symbol: Mode of operation - :combined (boolean vector, default) or :separate (DataFrame with separate columns per channel)

Returns

  • Vector{Bool} (combined mode) or DataFrame (separate mode)

Examples

julia
# Detect step values and return combined boolean vector (default)
step_mask = is_step_value(dat, 50.0)

# Detect step values and return DataFrame with separate columns
results = is_step_value(dat, 50.0, mode = :separate)

# Detect step values for specific channels
step_mask = is_step_value(dat, 50.0, channel_selection = channels([:Fp1, :Fp2]))
source
EegFun.jackknife_average Method
julia
jackknife_average(erps::Vector{ErpData})::Vector{ErpData}

Create jackknife averages from a vector of ERP/LRP data.

For each participant i, creates an average of all other participants (excluding i). This leave-one-out approach is commonly used with LRP data to reduce variance for statistical testing.

Arguments

  • erps::Vector{ErpData}: Vector of ERP/LRP data, one per participant

Returns

  • Vector{ErpData}: Vector of jackknifed averages, where element i is the average of all participants except participant i

Notes

  • Requires at least 2 participants

  • All ERP/LRP data must have matching structure (same channels, time points, sample rate)

  • The resulting data has the same format as the input (ErpData objects)

  • Common workflow: Calculate LRP → Jackknife average → Statistical testing

source
EegFun.libsvm_classifier Method
julia
libsvm_classifier(
    X_train::AbstractMatrix{Float64},
    y_train::Vector{Int},
    X_test::AbstractMatrix{Float64};
    cost::Float64 = 1.0,
) -> Vector{Int}

LIBSVM classifier for decoding.

This function uses LIBSVM.jl directly (no MLJ wrapper) for SVM classification.

Arguments

  • X_train::AbstractMatrix{Float64}: Training data [n_samples × n_features]

  • y_train::Vector{Int}: Training labels [n_samples]

  • X_test::AbstractMatrix{Float64}: Test data [n_samples × n_features]

Keyword Arguments

  • cost::Float64: Regularization parameter (default: 1.0). Larger values = less regularization.

Returns

  • Vector{Int}: Predicted class labels

Examples

julia
y_pred = libsvm_classifier(X_train, y_train, X_test, cost=1.0)
source
EegFun.log_epochs_table Method
julia
log_epochs_table(message::String, epochs...; kwargs...)

Log an epochs table with message and return the DataFrame. Combines logging and table creation in one clean call.

source
EegFun.log_pretty_table Method
julia
log_pretty_table(df::DataFrame; log_level::Symbol = :info, kwargs...)

Log a pretty table with specified log level. For general DataFrame logging. Sets show_row_number=false and show_subheader=false by default for cleaner logs.

Arguments

  • df::DataFrame: The DataFrame to log

  • log_level::Symbol: Log level (:debug, :info, :warn, :error) (default: :info)

  • kwargs...: Additional arguments passed to pretty_table

Examples

julia
# Log with default info level
log_pretty_table(df; title = "My Table")

# Log with debug level
log_pretty_table(df; log_level = :debug, title = "Debug Table")

# Log with warn level
log_pretty_table(df; log_level = :warn, title = "Warning Table")
source
EegFun.lowpass_filter Function
julia
lowpass_filter(dat::EegFun.EegData, cutoff_freq::Real; kwargs...)

Non-mutating version of lowpass_filter!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.lowpass_filter! Method

Apply lowpass filter using settings from a FilterConfig section.

source
EegFun.lowpass_filter! Method
julia
lowpass_filter!(dat::EegData, cutoff_freq::Real; kwargs...)

Apply a digital lowpass filter to EEG data. Modifies the data in place.

Arguments

  • dat::EegData: EegData object (ContinuousData, ErpData, or EpochData)

  • cutoff_freq::Real: Cutoff frequency in Hz

Keyword Arguments

  • order::Integer: Filter order (default: 3)

  • transition_width::Real: Relative width of transition band (default: 0.1)

  • filter_method::String: Filter implementation ("iir" or "fir")

  • channel_selection::Function: Channel selection predicate (default: channels() - all channels)

  • filter_func::String: Filtering function ("filtfilt" or "filt")

source
EegFun.lowpass_filter Method
julia
lowpass_filter(file_pattern::String, cutoff_freq::Real; kwargs...)

Batch lowpass filter EEG/ERP data from JLD2 files.

Arguments

  • file_pattern::String: Pattern to match files ("epochs", "erps", etc.)

  • cutoff_freq::Real: Cutoff frequency in Hz

Keyword Arguments

  • input_dir::String: Input directory (default: pwd())

  • participant_selection::Function: Participant selection (default: participants())

  • condition_selection::Function: Condition selection (default: conditions())

  • output_dir::Union{String, Nothing}: Output directory

source
EegFun.lrp Method
julia
lrp(erp_left::ErpData, erp_right::ErpData; channel_selection = channels())
lrp(erps::Vector{ErpData}, condition_pairs::Vector{Tuple{Int,Int}}; channel_selection = channels())
lrp(file_pattern::String, condition_pairs::Vector{Tuple{Int,Int}}; input_dir = pwd(),
channel_selection = channels(), participant_selection = participants(), output_dir = nothing)

Calculate the lateralized readiness potential (LRP) from ERP data.

  • Two-argument form: takes paired left/right ERP datasets, returns one ErpData with LRP values.

  • Vector{ErpData} form: computes LRP for multiple condition pairs at once.

  • file_pattern form: batch-processes JLD2 files across participants.

The LRP is a measure of lateralized motor preparation. For each channel pair (e.g., C3/C4):

  • LRP_C3 = 0.5 × ((C3_right - C4_right) + (C4_left - C3_left))

  • LRP_C4 = 0.5 × ((C4_right - C3_right) + (C3_left - C4_left))

This formula isolates lateralized activity by averaging the difference between contralateral and ipsilateral activation across both hemispheres.

References

  • Coles, M. G. H. (1989). Modern mind-brain reading. Psychophysiology, 26(3), 251-269.

  • de Jong, R. et al. (1988). Use of partial stimulus information. JEP:HPP, 14(4), 682-692.

  • Oostenveld, R. et al. (2003). Brain symmetry and topographic analysis. Clin. Neurophysiology, 114(7), 1194-1202.

Arguments (two-argument form)

  • erp_left::ErpData: ERP data for left-hand responses (ipsilateral activation)

  • erp_right::ErpData: ERP data for right-hand responses (contralateral activation)

  • channel_selection: Channel predicate selecting left/odd hemisphere channels. Default channels() auto-detects all odd/even pairs (C3/C4, C1/C2, Fp1/Fp2, …)

Examples

julia
# Auto-detect all lateral pairs (C3/C4, C1/C2, etc.)
lrp_data = lrp(erps[1], erps[2])

# Only C3/C4 and CP3/CP4
lrp_data = lrp(erps[1], erps[2], channel_selection = channels([:C3, :CP3]))

# Multiple condition pairs from one participant file
pairs = [(i, i+1) for i in 1:2:15]
lrp_results = lrp(erps, pairs)

# Batch across all participants
lrp("erps_cleaned", [(1, 2), (3, 4)], input_dir = "/data/study1")
source
EegFun.mark_epoch_intervals! Method
julia
mark_epoch_intervals!(dat::ContinuousData, triggers_of_interest::Vector{Int}, time_window::Vector{<:Real}; channel_out::Symbol = :epoch_interval)
mark_epoch_intervals!(dat::ContinuousData, channel_in::Symbol, time_window::Vector{<:Real}; channel_out = Symbol(string(channel_in)*"_window"))
mark_epoch_intervals!(dat::ContinuousData, channels_in::Vector{Symbol}, time_window::Vector{<:Real}; channel_out::Symbol = :event_window)
mark_epoch_intervals!(dat::ContinuousData, epoch_conditions::Vector{EpochCondition}, time_window::Vector{<:Real}; channel_out::Symbol = :epoch_interval)

Mark samples within a time window in-place by adding a boolean column to the data.

  • Trigger form: marks windows around samples matching any trigger in triggers_of_interest

  • Column form: marks windows around each true sample in a boolean column (e.g., :is_vEOG)

  • Multi-column form: unions true samples across multiple boolean columns and marks windows around all of them

  • EpochCondition form: marks windows around trigger sequences defined by epoch_conditions

Examples

julia
mark_epoch_intervals!(dat, [1, 3, 5], [-1.0, 2.0])
mark_epoch_intervals!(dat, :is_vEOG, [-0.1, 0.3])
mark_epoch_intervals!(dat, [:is_vEOG, :is_hEOG], [-0.05, 0.4]; channel_out = :eog_window)
mark_epoch_intervals!(dat, [condition1, condition2], [-1.0, 2.0])
source
EegFun.meta_data Method
julia
meta_data(eeg_data::EegData) -> DataFrame

Get meta data columns from the EEG data.

source
EegFun.meta_labels Method
julia
meta_labels(dat::EegData) -> Vector{Symbol}

Get metadata column names from the EEG data.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Vector{Symbol}: Vector of metadata column names
source
EegFun.mirror Function
julia
mirror(dat::EpochData, side::Symbol = :both) -> EpochData
mirror(dat::ErpData, side::Symbol = :both) -> ErpData
mirror(data_vec::Vector{EpochData}, side::Symbol = :both) -> Vector{EpochData}
mirror(data_vec::Vector{ErpData}, side::Symbol = :both) -> Vector{ErpData}

Non-mutating version of mirror!. Returns a new object with mirrored data, leaving the original unchanged.

Arguments

  • dat / data_vec: EpochData, ErpData, or a Vector of either

  • side: :pre, :post, or :both (default: :both)

source
EegFun.mirror! Function
julia
mirror!(dat::EpochData, side::Symbol = :both) -> Nothing
mirror!(dat::ErpData, side::Symbol = :both) -> Nothing
mirror!(data_vec::Vector{EpochData}, side::Symbol = :both) -> Nothing
mirror!(data_vec::Vector{ErpData}, side::Symbol = :both) -> Nothing

Mirror data in-place by appending time-reversed copies before and/or after the data. Reduces edge artifacts when filtering by creating smooth continuity at boundaries. Always call unmirror! after processing to restore the original length.

Arguments

  • dat / data_vec: EpochData, ErpData, or a Vector of either

  • side: :pre, :post, or :both (default: :both)

Notes

  • Mirrored data will be approximately 3× longer with :both

  • The side argument to unmirror! must match

Examples

julia
mirror!(epochs, :both)
filter!(epochs, 1.0, filter_type = "hp")
unmirror!(epochs, :both)
source
EegFun.n_channels Method
julia
n_channels(dat::EegData) -> Int

Get the number of channels in the EEG data.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Int: Number of channels
source
EegFun.n_epochs Method
julia
n_epochs(dat::EegData) -> Int

Get the number of epochs in the EEG data.

source
EegFun.n_extreme_value Method
julia
n_extreme_value(dat::SingleDataFrameEeg, threshold::Real; 
               channel_selection::Function = channels(), 
               sample_selection::Function = samples(),
interval_selection::Interval = times(),
               mode::Symbol = :combined)

Count the number of extreme values across selected channels.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • threshold::Real: Threshold for extreme value detection

  • channel_selection::Function: Channel predicate for selecting channels (default: all layout channels)

  • sample_selection::Function: Sample predicate for selecting samples (default: all samples)

  • mode::Symbol: Mode for extreme value detection (:separate or :combined, default: :combined)

Returns

  • DataFrame: DataFrame with extreme value counts for each channel (separate mode) or total count (combined mode)

Examples

julia
# Count total extreme values across all channels (combined mode, default)
total_count = n_extreme_value(dat, 100)

# Count extreme values in specific channels (combined mode)
total_count = n_extreme_value(dat, 100, channel_selection = channels([:Fp1, :Fp2]))

# Count extreme values only for selected samples (combined mode)
total_count = n_extreme_value(dat, 100, sample_selection = sample_mask)

# Count extreme values in all channels (separate mode)
count_df = n_extreme_value(dat, 100, mode = :separate)
source
EegFun.n_layout Method
julia
n_layout(layout::Layout) -> Int

Get the number of channels in the layout.

Arguments

  • layout::Layout: The layout object

Returns

  • Int: Number of channels in the layout
source
EegFun.n_samples Method
julia
n_samples(dat::EegData) -> Int

Get the number of samples in the EEG data.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Int: Number of samples
source
EegFun.n_step_value Method
julia
n_step_value(dat::SingleDataFrameEeg, threshold::Real; 
             channel_selection::Function = channels(), 
             sample_selection::Function = samples(),
             interval_selection::Interval = times(),
             mode::Symbol = :combined)

Count the number of step values (sudden voltage jumps) across selected channels.

Arguments

  • dat::SingleDataFrameEeg: The EEG data object

  • threshold::Real: Threshold for step value detection

  • channel_selection::Function: Channel predicate for selecting channels (default: all layout channels)

  • sample_selection::Function: Sample predicate for selecting samples (default: all samples)

  • mode::Symbol: Mode for step value detection (:separate or :combined, default: :combined)

Returns

  • Int (combined mode): Total number of samples with step values across all selected channels

  • DataFrame (separate mode): DataFrame with step value counts for each channel

Examples

julia
# Count total step values across all channels (combined mode, default)
total_count = n_step_value(dat, 50)

# Count step values per channel (separate mode)
count_df = n_step_value(dat, 50, mode = :separate)

# Count step values in specific channels
total_count = n_step_value(dat, 50, channel_selection = channels([:Fp1, :Fp2]))
source
EegFun.n_values Method
julia
n_values(df::DataFrame, column::Symbol)
n_values(dat::SingleDataFrameEeg, column::Symbol)
n_values(dat::MultiDataFrameEeg, column::Symbol)
n_values(dat::Vector{<:EegData}, column::Symbol)

Count the number of true values in a boolean column. Returns 0 if the column is not found. For MultiDataFrameEeg and Vector inputs, counts are summed across all epochs/items.

source
EegFun.neighbour_criterion Method
julia
neighbour_criterion(layout::Layout) -> Union{Nothing, Float64}

Get the distance criterion used for neighbor calculations.

Arguments

  • layout::Layout: The layout object

Returns

  • Union{Nothing, Float64}: The distance criterion in normalized coordinate units, or nothing if not calculated

Examples

julia
criterion = neighbour_criterion(layout)
source
EegFun.neighbours Method
julia
neighbours(layout::Layout) -> Union{Nothing, OrderedDict{Symbol, Neighbours}}

Get the neighbor information for all electrodes.

Arguments

  • layout::Layout: The layout object

Returns

  • Union{Nothing, OrderedDict{Symbol, Neighbours}}: Dictionary of neighbors for each electrode, or nothing if not calculated

Examples

julia
neighbor_dict = neighbours(layout)
source
EegFun.normalize_rdm Method
julia
normalize_rdm(rdm::Matrix{Float64}; method::Symbol = :none)

Normalize a Representational Dissimilarity Matrix (RDM).

Different normalization schemes can affect correlation results and interpretability. Choose based on your analysis goals and the properties of your dissimilarity measure.

Arguments

  • rdm::Matrix{Float64}: RDM matrix [condition × condition]

  • method::Symbol: Normalization method

    • :none - No normalization (default)

    • :zscore - Z-score normalization (mean=0, std=1)

    • :rank - Rank transformation (converts to ordinal ranks)

    • :minmax - Min-max normalization (scales to [0, 1])

Returns

  • normalized_rdm::Matrix{Float64}: Normalized RDM matrix

Examples

julia
# Z-score normalization (useful for comparing RDMs with different scales)
rdm_z = normalize_rdm(rdm, method=:zscore)

# Rank normalization (robust to outliers, used for Spearman-like comparisons)
rdm_rank = normalize_rdm(rdm, method=:rank)

# Min-max normalization (scales to [0, 1])
rdm_minmax = normalize_rdm(rdm, method=:minmax)

Notes

  • Normalization is applied to the upper triangle (excluding diagonal)

  • Diagonal remains zero after normalization

  • For :rank, ties are handled using average ranks

source
EegFun.number_param Function

Create a numeric parameter with optional min/max bounds.

source
EegFun.participants Method

Predicate generators for participant ID selection.

source
EegFun.participants_not Method

Predicate generators that exclude the specified participant IDs.

source
EegFun.partition_channels_by_eog_correlation Method
julia
partition_channels_by_eog_correlation(bad_channels::Vector{Symbol}, eog_correlation_df::DataFrame;
                                      eog_channels::Vector{Symbol} = [:hEOG, :vEOG],
                                      threshold::Real = 0.3,
                                      use_z::Bool = false)::Tuple{Vector{Symbol},Vector{Symbol}}

Partition bad channels into two groups based on correlation with EOG columns:

  • First element: bad channels NOT highly correlated with EOG (retain for non-EOG handling)

  • Second element: bad channels correlated with EOG (prefer ICA handling)

Arguments

  • bad_channels::Vector{Symbol}: Vector of bad channel names

  • eog_correlation_df::DataFrame: EOG correlation matrix DataFrame

  • eog_channels::Vector{Symbol}: EOG columns to use (default: [:hEOG, :vEOG])

  • threshold::Real: Correlation threshold (default: 0.3)

  • use_z::Bool: If true, use z-scored equivalents (e.g., :z_hEOG, :z_vEOG)

source
EegFun.permutation_test Method
julia
permutation_test(prepared::StatisticalData; kwargs...)

Perform cluster-based permutation test on prepared ERP data.

Arguments

  • prepared::StatisticalData: Prepared data from prepare_stats

  • n_permutations::Int: Number of permutations (default: 1000)

  • threshold::Float64: P-value threshold (default: 0.05)

  • threshold_method::Symbol: Threshold method - :parametric (default), :nonparametric_individual, or :nonparametric_common

  • cluster_type::Symbol: Type of clustering - :spatial, :temporal, or :spatiotemporal (default)

  • min_num_neighbors::Int: Minimum number of neighbouring significant channels required to keep a point (default: 2).

  • tail::Symbol: Test tail - :both (default), :left, or :right

  • show_progress::Bool: Whether to show progress bar (default: true)

Returns

  • PermutationResult: Complete results structure

Notes

  • Cluster statistics are computed using the maxsum method (sum of t-values within cluster), which is the most sensitive and standard approach in EEG research.

  • For reproducible results, call Random.seed!(xxx) in your Julia session before running the test.

source
EegFun.polar_to_cartesian_xy! Method
julia
polar_to_cartesian_xy!(layout::Layout; normalization_radius::Float64=1.0, preserve_radial_distance::Bool=true)

Converts polar coordinates (incidence and azimuth angles) from a layout into Cartesian coordinates (x, y).

Arguments

  • layout::Layout: A Layout containing the layout information with columns for incidence angles (:inc) and azimuth angles (:azi).

  • normalization_radius::Real: Maximum radius for electrode positions (default: 1.0). Only used when preserve_radial_distance=false.

  • preserve_radial_distance::Bool: If true, preserves true radial distances from polar coordinates, allowing electrodes with inc>90° to appear outside the unit circle (default: true). When false, normalizes all electrodes to fit within normalization_radius.

Throws

  • ArgumentError: If required :inc and :azi columns are missing or contain non-numeric values.

Modifies

  • The input layout is modified in place to include new columns x2 and y2, which represent the Cartesian coordinates calculated from the polar coordinates.

  • Clears any existing neighbour information since coordinates have changed.

Returns

  • Nothing. The function modifies the layout directly.

Notes

  • When preserve_radial_distance=true, electrodes imported from EEGLAB with inc>90° (e.g., eye electrodes beyond the scalp) will appear outside the standard head circle, matching EEGLAB's topoplot rendering.
source
EegFun.polar_to_cartesian_xyz! Method
julia
polar_to_cartesian_xyz!(layout::Layout)

Converts polar coordinates (incidence and azimuth angles) from a layout into Cartesian coordinates (x, y, z).

Arguments

  • layout::Layout: A Layout containing the layout information with columns for incidence angles (:inc) and azimuth angles (:azi).

Throws

  • ArgumentError: If required :inc and :azi columns are missing or contain non-numeric values.

Modifies

  • The input layout is modified in place to include new columns x3, y3, and z3, which represent the Cartesian coordinates calculated from the polar coordinates.

  • Clears any existing neighbour information since coordinates have changed.

Returns

  • Nothing. The function modifies the layout directly.
source
EegFun.positions_2D Method
julia
positions_2D(layout::Layout) -> DataFrame

Get 2D Cartesian coordinate data from the layout.

Arguments

  • layout::Layout: The layout object

Returns

  • DataFrame: DataFrame containing 2D Cartesian coordinate columns (x2, y2) if available
source
EegFun.positions_3D Method
julia
positions_3D(layout::Layout) -> DataFrame

Get 3D Cartesian coordinate data from the layout.

Arguments

  • layout::Layout: The layout object

Returns

  • DataFrame: DataFrame containing 3D Cartesian coordinate columns (x3, y3, z3) if available
source
EegFun.positions_polar Method
julia
positions_polar(layout::Layout) -> DataFrame

Get polar coordinate data from the layout.

Arguments

  • layout::Layout: The layout object

Returns

  • DataFrame: DataFrame containing polar coordinate columns (inc, azi)
source
EegFun.prepare_decoding Method
julia
prepare_decoding(epochs::Vector{T}; condition_selection, channel_selection, interval_selection) where {T<:MultiDataFrameEeg}

Prepare epoch data for multivariate pattern analysis (MVPA/decoding).

Organizes epoch data into a structure suitable for decoding analysis by grouping epochs by participant and selecting specified conditions for classification. Works with both EpochData and TimeFreqEpochData.

Arguments

  • epochs::Vector{T}: Epoch data containing multiple conditions/participants (EpochData or TimeFreqEpochData)

  • condition_selection::Function: Predicate to select conditions for classification (default: conditions() - all conditions)

  • channel_selection::Function: Predicate to filter channels (default: channels() - all channels)

  • interval_selection::Interval: Time window as tuple (e.g., (0.0, 1.0)) or interval object (default: times() - all samples)

Returns

  • Vector{Vector{T}}: Vector of participant data, where each element is a vector of data for that participant's conditions

Examples

julia
# ERP decoding
# all_epochs = read_all_data(EpochData, joinpath(input_dir, "epochs_good"))
participant_epochs = prepare_decoding(
    all_epochs,
    condition_selection = conditions([1, 2]),
    channel_selection = channels(),
    interval_selection = (0.0, 1.0))

# TF decoding
# all_tf_epochs = read_all_data(TimeFreqEpochData, joinpath(input_dir, "tf_epochs"))
participant_tf = prepare_decoding(
    all_tf_epochs,
    condition_selection = conditions([1, 2]),
    interval_selection = times(0.0, 1.0))
source
EegFun.prepare_stats Method

Load ERP/TF files by pattern and call the typed prepare_stats method.

source
EegFun.prepare_stats Method
julia
prepare_stats(erps::Vector{ErpData}; design::Symbol = :paired, condition_selection::Function = conditions([1, 2]), channel_selection::Function = channels(), interval_selection::Interval = times(), baseline_interval::Interval = times(), analysis_interval::Interval = times())

Prepare ErpData for comparing two conditions in statistical tests (permutation and analytic tests).

Organizes ErpData into participant × electrode × time arrays for statistical analysis. Validates the design and ensures data consistency across conditions.

Arguments

  • erps::Vector{ErpData}: ERPs containing data for multiple conditions/participants

  • design::Symbol: Design type - :paired (same participants in both conditions) or :independent (different participants)

  • condition_selection::Function: Predicate to select exactly 2 conditions for comparison (default: conditions([1, 2]))

  • channel_selection::Function: Predicate to filter channels (default: channels() - all channels)

  • interval_selection::Interval: Time interval as tuple (e.g., (0.0, 1.0)) or interval object for initial data selection (default: nothing - all samples)

  • baseline_interval::Interval: Baseline interval as tuple (e.g., (-0.2, 0.0)) or interval object (default: nothing - no baseline)

  • analysis_interval::Interval: Analysis interval as tuple (e.g., (0.3, 0.5)) or interval object for statistical testing (default: nothing - use interval_selection)

Returns

  • StatisticalData: Prepared data structure ready for statistical testing
source
EegFun.prepare_stats Method
julia
prepare_stats(tfs::Vector{TimeFreqData}; design, condition_selection, channel_selection, frequency_selection, interval_selection, baseline_interval, baseline_method)

Prepare TimeFreqData for comparing two conditions in statistical tests.

Organizes TimeFreqData power values into participant × electrode × frequency × time arrays for statistical analysis. Validates the design and ensures data consistency across conditions.

Arguments

  • tfs::Vector{TimeFreqData}: TF data containing data for multiple conditions/participants

  • design::Symbol: Design type - :paired or :independent

  • condition_selection::Function: Predicate to select exactly 2 conditions (default: conditions() - all conditions)

  • channel_selection::Function: Predicate to filter channels (default: channels() - all channels)

  • frequency_selection::Interval: Frequency range as tuple (e.g., (4.0, 30.0)) or nothing for all frequencies

  • interval_selection::Interval: Time interval as tuple (e.g., (0.0, 1.0)) or nothing for all time points

  • baseline_interval::Union{Nothing,Tuple{Real,Real}}: Baseline window in seconds (e.g., (-0.5, -0.1)). Default: nothing (no baseline)

  • baseline_method::Symbol: Baseline method (default: :db). Options: :db, :absolute, :relative, :relchange, :percent, :zscore

Returns

  • TFStatisticalData: Prepared data structure ready for statistical testing

Examples

julia
# Prepare with baseline correction
prepared = prepare_stats(tfs; design=:paired,
    baseline_interval=(-0.5, -0.1), baseline_method=:db,
    frequency_selection=(4.0, 30.0))

# Run permutation test
result = permutation_test(prepared; n_permutations=1000)
source
EegFun.preprocess Method
julia
preprocess(config::String; base_dir::Union{String,Nothing} = nothing, log_level::Symbol = :info)

Preprocess EEG data according to the specified configuration file.

Arguments

  • config::String: Path to the configuration file in TOML format

  • base_dir::Union{String,Nothing}: Base directory for resolving relative paths in TOML file. If nothing (default), uses the directory of the config file. This allows relative paths in your TOML to work relative to where your analysis script is located.

  • log_level::Symbol: Log level for preprocessing (:debug, :info, :warn, :error)

Notes

  • Relative paths in the TOML config file are resolved relative to base_dir

  • If base_dir is not provided, it defaults to the directory containing the config file

  • Absolute paths in the TOML are used as-is

  • This allows you to use relative paths in your TOML files that work regardless of where you run the script from, as long as the config file is in the same directory as your analysis script

source
EegFun.preprocess_v2 Method
julia
preprocess_v2(config::String; base_dir::Union{String,Nothing} = nothing, log_level::Symbol = :info)

Preprocess EEG data according to the specified configuration file.

Arguments

  • config::String: Path to the configuration file in TOML format

  • base_dir::Union{String,Nothing}: Base directory for resolving relative paths in TOML file. If nothing (default), uses the directory of the config file. This allows relative paths in your TOML to work relative to where your analysis script is located.

  • log_level::Symbol: Log level for preprocessing (:debug, :info, :warn, :error)

Notes

  • Relative paths in the TOML config file are resolved relative to base_dir

  • If base_dir is not provided, it defaults to the directory containing the config file

  • Absolute paths in the TOML are used as-is

  • This allows you to use relative paths in your TOML files that work regardless of where you run the script from, as long as the config file is in the same directory as your analysis script

source
EegFun.print_config Function
julia
print_config(config, [io=stdout])
print_config(config, filename::String)

Print configuration in TOML format.

Arguments

  • config: Configuration dictionary (typically loaded from TOML)

  • io=stdout: Optional IO object to print to (default: standard output)

  • filename: Optional filename to write TOML output to

Examples

julia
# Print to console in TOML format
print_config(config)

# Write to TOML file
print_config(config, "config_output.toml")
source
EegFun.print_filter_characteristics Method
julia
print_filter_characteristics(filter_info::FilterInfo; npoints::Int = 1000)

Print a formatted summary of filter characteristics.

source
EegFun.print_layout_neighbours Method
julia
print_layout_neighbours(neighbours_dict::OrderedDict{Symbol, Neighbours}, filename::String)

Write the neighbors dictionary to a TOML file in a structured format, showing for each electrode:

  • Its neighbors

  • The distances to each neighbor

  • The weights used for interpolation

  • The average number of neighbors per electrode

The electrode order from the original OrderedDict is preserved.

Arguments

  • neighbours_dict::OrderedDict{Symbol, Neighbours}: Dictionary returned by get_electrode_neighbours_xy/xyz

  • filename::String: Path to the output TOML file

Example

julia
layout = read_layout("./layouts/biosemi64.csv")
get_neighbours_xy!(layout, 0.4)

# Write to TOML file
print_layout_neighbours(layout.neighbours, "neighbours.toml")
source
EegFun.read_all_data Function
julia
read_all_data(path_pattern::String, participant_selection = participants())
read_all_data(::Type{T}, path_pattern::String, participant_selection = participants())

Find and load EEG data from JLD2 files whose names contain the basename of path_pattern, searching in the directory given by dirname(path_pattern).

This mirrors the single-argument form of read_data, so you compose the path the same way:

julia
read_data("./derivatives/erps/example1_erps_good.jld2")   # single file
read_all_data("./derivatives/erps/erps_good")             # all matching files

Arguments

  • path_pattern: A file-system path whose dirname is the search directory and whose basename is matched (via contains) against .jld2 filenames.

  • participant_selection: Predicate function (default: participants() — all).

source
EegFun.read_config Method
julia
read_config(config_file::String)

Load and merge configuration from a TOML file with defaults.

Arguments

  • config_file::String: Path to the configuration file

Returns

  • Union{Dict,Nothing}: The loaded configuration or nothing if loading failed
source
EegFun.read_data Method
julia
read_data(filepath::String)

Read data from JLD2 file, returning the data directly, a Dict of all variables, or nothing.

  • If file has 1 variable: returns the value directly

  • If file has multiple variables: returns a Dict with all key-value pairs

  • If file is empty: returns nothing

source
EegFun.read_eeglab Method
julia
read_eeglab(filepath::String; preserve_radial_distance::Bool = true)

Load EEGLAB .set file and return EEG data. If ICA decomposition is present, also returns ICA data as a tuple (eeg_data, ica_data).

Arguments

  • filepath::String: Path to .set file

  • preserve_radial_distance::Bool: If true, preserves anatomical distances for electrodes with incidence > 90° (default: true)

Returns

  • If ICA present: (eeg_data, ica_data::InfoIca)

  • If no ICA: eeg_data (EpochData, ContinuousData, or ErpData)

Examples

julia
# Without ICA
eeg = read_eeglab("data.set")

# With ICA (automatically detected)
eeg, ica = read_eeglab("data_with_ica.set")
source
EegFun.read_fieldtrip Method
julia
read_fieldtrip(filepath::String, layout::Layout)  ContinuousData/EpochData/ErpData

Load FieldTrip .mat file and convert to EegFun data structure.

Arguments

  • filepath::String: Path to FieldTrip .mat file

  • layout::Layout: Channel layout (FieldTrip doesn't store layout in data files)

Returns

  • ContinuousData: For single-trial continuous data

  • EpochData: For multi-trial epoched data

  • ErpData: For averaged ERP data

Example

julia
using EegFun
# We need to specify a layout to pass to our EegFun data types
layout = EegFun.read_layout("./resources/layouts/biosemi/biosemi64.csv")
data = EegFun.read_fieldtrip("./resources/data/fieldtrip/continuous.mat", layout)
source
EegFun.read_fieldtrip_csv Method
julia
read_fieldtrip_csv(data_dir::String; 
                  file::String="fieldtrip_data",
                  condition::Int=1,
                  condition_name::String="fieldtrip",
                  fsample::Union{Int,Nothing}=nothing) -> EpochData

Load FieldTrip epochs data saved as CSV and convert to EpochData. The CSV file should be created using the MATLAB function fieldtrip_epochs_to_csv.m.

Arguments

  • data_dir::String: Directory containing epochs_data.csv
    • The CSV has columns: [trial, time, channel1, channel2, ...]

Keyword Arguments

  • file::String="fieldtrip_data": Source filename for EpochData

  • condition::Int=1: Condition number

  • condition_name::String="fieldtrip": Condition name

  • fsample::Union{Int,Nothing}=nothing: Sample rate in Hz (if not provided, will be estimated from time column)

Returns

  • EpochData: EpochData object with the loaded data

Example

julia
epochs = read_fieldtrip_csv("/path/to/csv/files", fsample=256)
source
EegFun.read_layout Method
julia
read_layout(file::String)

Reads a layout file in CSV format and returns a DataFrame containing the layout information.

Arguments

  • file::String: The path to the CSV file containing the layout data.

Returns

  • DataFrame: A DataFrame containing the layout data, with columns for electrode labels, incidence angles, azimuth angles, and calculated Cartesian coordinates (if applicable).

Throws

  • EegFunError: If the file does not exist or cannot be accessed.
source
EegFun.read_raw_data Method
julia
read_raw_data(filepath::String; kwargs...) -> Union{BiosemiDataFormat.BiosemiData, BrainVisionDataFormat.BrainVisionData}

Read raw EEG data from various file formats (BDF, BrainVision).

Arguments

  • filepath::String: Path to the raw data file.

Returns

  • Raw data object from the underlying reader library.
source
EegFun.realign! Method
julia
realign!(dat::EpochData, realignment_column::Symbol)::Nothing

Realign epoched data in-place to a different time point specified in a DataFrame column.

This function takes stimulus-locked (or any reference-locked) epoched data and realigns each epoch so that the time specified in realignment_column becomes the new time zero. After realignment, all epochs are cropped to a common time interval determined by the latest start time and earliest end time across all epochs.

Arguments

  • dat::EpochData: Epoched EEG data to realign

  • realignment_column::Symbol: Name of the column containing realignment times (e.g., :rt, :response_time)

Effects

  • Modifies the input data in-place

  • Updates the :time column in each epoch

  • Crops all epochs to a common time interval

  • The realignment column values should be relative to the current time zero

Examples

julia
using EegFun

# Load stimulus-locked epoched data
epochs = read_data("participant_1_epochs.jld2")

# Realign to response times (stored in :rt column)
realign!(epochs, :rt)

Notes

  • The realignment column must exist in all epochs

  • The realignment value should be constant within each epoch (checked automatically)

  • After realignment, epoch length may be shorter due to cropping to common interval

  • Trials with insufficient data before/after the realignment point will determine the final epoch length

source
EegFun.realign Method
julia
realign(dat::EpochData, realignment_column::Symbol)::EpochData

Non-mutating version of realign!. Returns a new EpochData object with realigned epochs.

source
EegFun.reference Method
julia
reference(dat::EegData) -> Symbol

Get the reference information from the EEG data.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Symbol: Reference information
source
EegFun.reject_epochs! Method
julia
reject_epochs!(dat::EpochData, info::EpochRejectionInfo) -> EpochData

Remove epochs identified in info from dat in-place.

Arguments

  • dat::EpochData: Epoched EEG data to modify

  • info::EpochRejectionInfo: Rejection information from detect_bad_epochs_automatic etc.

Examples

julia
info = detect_bad_epochs_automatic(epochs, z_criterion = 2.0)
reject_epochs!(epochs, info)
source
EegFun.reject_epochs Method
julia
reject_epochs(dat::EpochData, info::EpochRejectionInfo) -> EpochData
reject_epochs(dat::Vector{EpochData}, info::Vector{EpochRejectionInfo}) -> Vector{EpochData}
reject_epochs(dat::EpochData, bad_column::Symbol) -> EpochData
reject_epochs(dat::EpochData, bad_columns::Vector{Symbol}) -> EpochData
reject_epochs(dat::Vector{EpochData}, bad_column::Symbol) -> Vector{EpochData}
reject_epochs(dat::Vector{EpochData}, bad_columns::Vector{Symbol}) -> Vector{EpochData}

Non-mutating epoch rejection. Returns new EpochData with bad epochs removed.

Accepts rejection info from detect_bad_epochs_automatic, or one or more boolean column names — epochs where any sample is true in those columns are removed.

Examples

julia
# From detection info
info = detect_bad_epochs_automatic(epochs, z_criterion = 2.0)
cleaned = reject_epochs(epochs, info)

# From boolean artifact column
cleaned = reject_epochs(epochs, :is_vEOG)
cleaned = reject_epochs(epochs, [:is_vEOG, :is_extreme_value_100])
source
EegFun.remove_ica_components! Method
julia
remove_ica_components!(dat::DataFrame, ica::InfoIca; component_selection::Function = components())
remove_ica_components!(dat::ContinuousData, ica::InfoIca; component_selection::Function = components())
remove_ica_components(dat::DataFrame, ica::InfoIca; component_selection::Function = components())
remove_ica_components(dat::ContinuousData, ica::InfoIca; component_selection::Function = components())

Remove ICA components from data. Mutating (!) versions operate in-place; non-mutating versions return a copy of the data with components removed along with an updated InfoIca object.

Example

julia
# In-place
remove_ica_components!(dat, ica_result)

# Non-mutating
cleaned_dat, ica_updated = remove_ica_components(dat, ica_result)

# Select specific components
remove_ica_components!(dat, ica_result, component_selection = components([1, 3, 5]))
source
EegFun.rename_channel! Method
julia
rename_channel!(dat::EegData, rename_dict::Dict{Symbol, Symbol})

Rename channels in EEG data using a dictionary mapping old names to new names. Modifies the data in place by updating both the data columns and the layout.

Arguments

  • dat::EegData: The EEG data object to modify

  • rename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names

Returns

  • nothing (modifies the data in place)

Examples

julia
# Rename Fp1 to Fpz and Fp2 to Fpz
rename_dict = Dict(:Fp1 => :Fpz, :Fp2 => :Fpz)
rename_channel!(dat, rename_dict)

# Rename a single channel
rename_channel!(dat, Dict(:Cz => :Cz_new))

Notes

  • Only channels that exist in the data will be renamed

  • If multiple channels would be renamed to the same name, an error is thrown to prevent duplicates

  • Updates both the data columns and the layout labels

  • Clears any cached neighbour information in the layout since channel names have changed

  • Properly handles swaps (e.g., Dict(:A => :B, :B => :A) correctly exchanges the channels)

source
EegFun.rename_channel! Method
julia
rename_channel!(layout::Layout, rename_dict::Dict{Symbol, Symbol})

Rename channels in a Layout object using a dictionary mapping old names to new names. Modifies the layout in place.

Arguments

  • layout::Layout: The layout object to modify

  • rename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names

Returns

  • nothing (modifies the layout in place)

Examples

julia
# Rename Fp1 to Fpz and Fp2 to Fpz
rename_dict = Dict(:Fp1 => :Fpz, :Fp2 => :Fpz)
rename_channel!(layout, rename_dict)

# Rename a single channel
rename_channel!(layout, Dict(:Cz => :Cz_new))

Throws

  • EegFunError: If multiple channels would be renamed to the same name (duplicate prevention)

Notes

  • Only channels that exist in the layout will be renamed

  • Clears any cached neighbour information since channel names have changed

source
EegFun.rename_channel Method
julia
rename_channel(dat::EegData, rename_dict::Dict{Symbol, Symbol})

Create a renamed copy of EEG data using a dictionary mapping old names to new names.

Arguments

  • dat::EegData: The EEG data object to rename

  • rename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names

Returns

  • EegData: A new EEG data object with renamed channels

Examples

julia
# Rename Fp1 to Fpz and Fp2 to Fpz
rename_dict = Dict(:Fp1 => :Fpz, :Fp2 => :Fpz)
new_dat = rename_channel(dat, rename_dict)

Notes

  • Only channels that exist in the data will be renamed

  • If multiple channels would be renamed to the same name, an error is thrown to prevent duplicates

  • Updates both the data columns and the layout labels

  • The original data is not modified

  • Properly handles swaps (e.g., Dict(:A => :B, :B => :A) correctly exchanges the channels)

source
EegFun.rename_channel Method
julia
rename_channel(layout::Layout, rename_dict::Dict{Symbol, Symbol})

Create a renamed copy of a Layout object using a dictionary mapping old names to new names.

Arguments

  • layout::Layout: The layout object to rename

  • rename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names

Returns

  • Layout: A new layout object with renamed channels

Examples

julia
# Rename Fp1 to Fpz and Fp2 to Fpz
rename_dict = Dict(:Fp1 => :Fpz, :Fp2 => :Fpz)
new_layout = rename_channel(layout, rename_dict)

Throws

  • EegFunError: If multiple channels would be renamed to the same name (duplicate prevention)

Notes

  • Only channels that exist in the layout will be renamed

  • The original layout is not modified

source
EegFun.repair_artifacts! Method
julia
repair_artifacts!(dat::EpochData, artifacts::EpochRejectionInfo; method::Symbol=:neighbor_interpolation, kwargs...)

Repair detected artifacts using the specified method.

Arguments

  • dat::EpochData: The epoch data to repair (modified in-place)

  • artifacts::EpochRejectionInfo: Artifact information from detect_artifacts

  • method::Symbol: Repair method to use

Available Methods

  • :neighbor_interpolation - Weighted neighbor interpolation (default). Uses dat.layout.neighbours for neighbor information.

  • :spherical_spline - Spherical spline interpolation

Keyword Arguments (for :spherical_spline method)

  • m::Int: Order of Legendre polynomials (default: 4)

  • lambda::Float64: Regularization parameter (default: 1e-5)

Returns

Nothing (mutates dat in-place)

source
EegFun.repair_artifacts Method
julia
repair_artifacts(dat::EpochData, artifacts::EpochRejectionInfo; method::Symbol=:neighbor_interpolation, kwargs...)

Non-mutating version of repair_artifacts!. Creates a copy of the data and repairs artifacts without modifying the original.

Arguments

  • dat::EpochData: The epoch data to repair (NOT modified)

  • artifacts::EpochRejectionInfo: Artifact information from detect_artifacts

  • method::Symbol: Repair method to use (default: :neighbor_interpolation)

Keyword Arguments

Same as repair_artifacts! for the respective methods.

Returns

  • EpochData: A new EpochData object with repaired artifacts

Examples

julia
# Basic artifact repair (creates new object)
repaired_epochs = repair_artifacts(epochs, artifacts)

# Using spherical spline method
repaired_epochs = repair_artifacts(epochs, artifacts, :spherical_spline)

# Rejecting bad epochs entirely (use reject_epochs instead)
clean_epochs = reject_epochs(epochs, artifacts)
source
EegFun.repair_artifacts_neighbor! Method
julia
repair_artifacts_neighbor!(dat::EpochData, artifacts::EpochRejectionInfo)

Repair artifacts using weighted neighbor interpolation. Uses artifacts.repaired to determine which channels to repair (should be populated by channel_repairable!). Uses dat.layout.neighbours for neighbor information.

Arguments

  • dat::EpochData: The epoch data to repair (modified in-place)

  • artifacts::EpochRejectionInfo: Artifact information with repaired already populated

Returns

  • EpochData: The repaired epoch data (same object, modified in-place)

See also

  • channel_repairable!: Analyze which channels can be repaired before calling this function
source
EegFun.repair_artifacts_spherical_spline! Method
julia
repair_artifacts_spherical_spline!(dat::EpochData, artifacts::EpochRejectionInfo; m::Int=4, lambda::Real=1e-5)

Repair artifacts using spherical spline interpolation.

Arguments

  • dat::EpochData: The epoch data to repair (modified in-place)

  • artifacts::EpochRejectionInfo: Artifact information from detect_artifacts

  • m::Int: Order of Legendre polynomials (default: 4)

  • lambda::Real: Regularization parameter (default: 1e-5)

Returns

  • EpochData: The repaired epoch data (same object, modified in-place)
source
EegFun.repair_channels Function
julia
repair_channels(data::Any, channels_to_repair::Any; kwargs...)

Non-mutating version of repair_channels!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.repair_channels! Method
julia
repair_channels!(data, channels_to_repair; method=:neighbor_interpolation, kwargs...)

Repair bad EEG channels using the specified method.

Arguments

  • data: EEG data object (ContinuousData, EpochData, or AbstractMatrix)

  • channels_to_repair::Vector{Symbol}: List of channel labels to repair

Keyword Arguments

  • method::Symbol: Repair method to use

    • :neighbor_interpolation (default): Weighted neighbor interpolation

    • :spherical_spline: Spherical spline interpolation

  • neighbours_dict::Union{OrderedDict, Nothing}: Neighbor information (for neighbor_interpolation)

  • m::Int: Order of Legendre polynomials (for spherical_spline, default: 4)

  • lambda::Float64: Regularization parameter (for spherical_spline, default: 1e-5)

  • epoch_selection::Function: Epoch selection predicate (for EpochData, default: all epochs)

Examples

julia
# Neighbor interpolation (default)
repair_channels!(dat, [:Fp1, :Fp2])

# Spherical spline
repair_channels!(dat, [:Fp1, :Fp2], method=:spherical_spline)

# With custom parameters
repair_channels!(dat, [:Fp1], method=:spherical_spline, m=6, lambda=1e-6)
source
EegFun.repair_channels! Method
julia
repair_channels!(data::ContinuousData, repair_info::ContinuousRepairInfo; method::Symbol=:neighbor_interpolation, kwargs...)

Repair channels using the information in repair_info. Orchestrator function similar to repair_artifacts! for epoch data.

Arguments

  • data::ContinuousData: Continuous EEG data to repair (modified in-place)

  • repair_info::ContinuousRepairInfo: Repair tracking struct with repaired channels already populated

  • method::Symbol: Repair method to use (default: :neighbor_interpolation)

Returns

  • Nothing: All modifications are in-place

Notes

  • repair_info.repaired should be populated by channel_repairable! before calling this function
source
EegFun.repair_channels_neighbor! Method
julia
repair_channels_neighbor!(data::ContinuousData, repair_info::ContinuousRepairInfo)

Repair channels using weighted neighbor interpolation. Uses repair_info.repaired to determine which channels to repair (should be populated by channel_repairable!). Updates repair_info during repair to track actual repairs vs skips.

Arguments

  • data::ContinuousData: Continuous EEG data to repair (modified in-place)

  • repair_info::ContinuousRepairInfo: Repair tracking struct with repaired channels already populated

Returns

  • Nothing: All modifications are in-place

See also

  • channel_repairable!: Analyze which channels can be repaired before calling this function
source
EegFun.repair_channels_per_epoch! Method
julia
repair_channels_per_epoch!(epochs::Vector{EpochData}, layout::Layout, threshold::Real, artifact_col::Symbol)

Repair channels on a per-epoch basis for epochs with artifacts.

This function identifies channels that exceed the artifact threshold in each epoch and repairs them using neighbor interpolation if good neighbors are available. This allows trial-by-trial repair when channels have good neighbors.

Arguments

  • epochs_list::Vector{EpochData}: Vector of EpochData objects (one per condition)

  • layout::Layout: Layout object containing neighbor information

  • threshold::Real: Artifact detection threshold (e.g., 100.0 μV)

  • artifact_col::Symbol: Column name for artifact flags (e.g., :is_artifact_value_100)

Returns

  • Tuple{Vector{Symbol}, Int}: (unique_channels_repaired, epochs_repaired_count)

Examples

julia
# Repair channels per epoch
channels_repaired, epochs_repaired = repair_channels_per_epoch!(
    epochs_cleaned, 
    layout, 
    100.0, 
    :is_artifact_value_100
)
source
EegFun.repair_channels_spherical! Method
julia
repair_channels_spherical!(data::ContinuousData, repair_info::ContinuousRepairInfo; m::Int=4, lambda::Real=1e-5)

Repair channels using spherical spline interpolation. Uses repair_info.repaired to determine which channels to repair (should be populated by channel_repairable!). Updates repair_info during repair to track actual repairs vs skips.

Arguments

  • data::ContinuousData: Continuous EEG data to repair (modified in-place)

  • repair_info::ContinuousRepairInfo: Repair tracking struct with repaired channels already populated

  • m::Int: Order of Legendre polynomials (default: 4)

  • lambda::Real: Regularization parameter (default: 1e-5)

Returns

  • Nothing: All modifications are in-place

See also

  • channel_repairable!: Analyze which channels can be repaired before calling this function
source
EegFun.rereference Function
julia
rereference(dat::Vector{EegFun.ErpData}, reference_selection::Union{Symbol, Vector{Symbol}}, channel_selection::Function; kwargs...)
rereference(dat::Vector{EegFun.ErpData}, reference_selection::Union{Symbol, Vector{Symbol}}; kwargs...)
rereference(dat::Vector{EegFun.EpochData}, reference_selection::Union{Symbol, Vector{Symbol}}, channel_selection::Function; kwargs...)
rereference(dat::Vector{EegFun.EpochData}, reference_selection::Union{Symbol, Vector{Symbol}}; kwargs...)
rereference(dat::EegFun.EegData, reference_selection::Union{Symbol, Vector{Symbol}}, channel_selection::Function; kwargs...)
rereference(dat::EegFun.EegData, reference_selection::Union{Symbol, Vector{Symbol}}; kwargs...)

Non-mutating version of rereference!. Creates a copy of the input data and applies the operation to the copy.

source
EegFun.rereference Method
julia
rereference(file_pattern::String; 
           input_dir::String = pwd(), 
           reference_selection::Union{Symbol, Vector{Symbol}} = :avg, 
           participant_selection::Function = participants(),
           condition_selection::Function = conditions(),
           output_dir::Union{String, Nothing} = nothing)

Rereference EEG/ERP data from JLD2 files and save to a new directory.

Arguments

  • file_pattern::String: Pattern to match files ("epochs", "erps", "cleaned", "original", or custom)

  • input_dir::String: Input directory containing JLD2 files (default: current directory)

  • reference_selection::Union{Symbol, Vector{Symbol}}: Reference channels, can be:

    • Special symbols: :avg (average reference), :mastoid (M1+M2)

    • Channel names: [:Cz], [:M1, :M2], etc.

  • participant_selection::Function: Participant selection predicate (default: participants() for all)

  • condition_selection::Function: Condition selection predicate (default: conditions() for all)

  • output_dir::Union{String, Nothing}: Output directory (default: creates subdirectory based on reference settings)

Example

julia
# Rereference all epochs to average reference
rereference("epochs")

# Rereference specific participant to mastoid reference
rereference("epochs", reference_selection=:mastoid, participants=3)

# Rereference to Cz for specific participants and conditions
rereference("epochs", reference_selection=[:Cz], participants=[3, 4], conditions=[1, 2])
source
EegFun.resample! Method
julia
resample!(dat::Union{ContinuousData, ErpData}, factor::Int)
resample!(dat::EpochData, factor::Int)
resample!(data_vec::Vector{T}, factor::Int) where {T<:EegData}
resample(dat, factor)
resample(data_vec::Vector{T}, factor) where {T<:EegData}
resample(file_pattern::String, factor::Int; input_dir = pwd(), participant_selection = participants(), output_dir = nothing)

Downsample EEG data by keeping every factor-th sample.

Mutating (!) variants modify in-place and update sample_rate; non-mutating variants return a copy. The file_pattern form batch-processes JLD2 files across participants.

⚠️ Simple decimation is used — low-pass filter the data first to avoid aliasing.

Notes

  • factor must be a positive integer; sample_rate must be divisible by factor

  • All DataFrame columns (triggers, time, metadata) are preserved

  • Trigger positions are scaled to the new sample grid for ContinuousData

Examples

julia
resample!(dat, 2)                    # 512 Hz → 256 Hz in-place
dat_256 = resample(dat, 2)           # non-mutating copy
resample!(all_epochs, 4)             # Vector: broadcasts to every element
resample("continuous", 2)            # batch: all JLD2 files in current dir
resample("continuous", 2, input_dir = "/data/study1", participant_selection = participants(1:20))
source
EegFun.resample_temporal_data Method
julia
resample_temporal_data(
    temporal_data::Array{Float64, 3},
    original_times::Vector{Float64},
    target_times::Vector{Float64};
    method::Symbol = :linear,
)

Resample temporal model data to match neural data timepoints.

Handles different sampling rates and time ranges by interpolating model data to match the neural data's temporal structure.

Arguments

  • temporal_data::Array{Float64, 3}: Temporal data [conditions × features × time]

  • original_times::Vector{Float64}: Original time points in seconds

  • target_times::Vector{Float64}: Target time points (e.g., from neural data)

  • method::Symbol: Interpolation method (:linear, :cubic, :nearest)

Returns

  • resampled_data::Array{Float64, 3}: Resampled data [conditions × features × target_time]

  • target_times::Vector{Float64}: Target time points (same as input)

Examples

julia
# Model data at 100 Hz
model_data = randn(3, 2, 50)  # [conditions × features × time]
model_times = collect(-0.2:(1/100):0.3)  # 50 timepoints at 100 Hz

# Neural data at 500 Hz
neural_times = collect(-0.2:(1/500):0.8)  # 500 timepoints at 500 Hz

# Resample model data to match neural times
resampled_data, _ = resample_temporal_data(model_data, model_times, neural_times)
# Now resampled_data has shape [3 × 2 × 500]
source
EegFun.restore_ica_components! Method
julia
restore_ica_components!(dat::DataFrame, ica::InfoIca; component_selection = components())
restore_ica_components!(dat::ContinuousData, ica::InfoIca; component_selection = components())
restore_ica_components(dat::DataFrame, ica::InfoIca; component_selection = components())
restore_ica_components(dat::ContinuousData, ica::InfoIca; component_selection = components())

Restore previously removed ICA components using stored activations. Mutating (!) versions operate in-place; non-mutating versions return a copy and an updated InfoIca. Components must have been removed first (activations stored in ica.removed_activations).

Example

julia
# In-place
restore_ica_components!(dat, ica_result)

# Non-mutating
restored_dat, ica_updated = restore_ica_components(dat, ica_result)

# Select specific components to restore
restore_ica_components!(dat, ica_result, component_selection = components([1, 3]))
source
EegFun.rsa Method
julia
rsa(
    epochs::Vector{EpochData};
    channel_selection::Function = channels(),
    sample_selection::Function = samples(),
interval_selection::Interval = times(),
    dissimilarity_measure::Symbol = :correlation,
    average_trials::Bool = true,
    normalize_method::Symbol = :none,
)

Perform Representational Similarity Analysis (RSA) on epoch data.

Computes Representational Dissimilarity Matrices (RDMs) at each time point, showing how dissimilar neural patterns are between different conditions.

Arguments

  • epochs::Vector{EpochData}: Vector of EpochData, one per condition, from a SINGLE participant

  • channel_selection::Function=channels(): Channel selection predicate. See channels() for options.

    • Example: channel_selection=channels([:Fz, :Cz, :Pz]) for specific channels

    • Example: channel_selection=channels(:Cz) for single channel

    • Example: channel_selection=channels() for all channels (default)

  • sample_selection::Function=samples(): Sample selection predicate. See samples() for options.

    • Example: sample_selection=samples((-0.2, 0.8)) for time interval from -0.2 to 0.8 seconds

    • Example: sample_selection=samples() for all time points (default)

  • dissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)

    • :correlation or :pearson - 1 - Pearson correlation (default, most common)

    • :spearman - 1 - Spearman rank correlation (robust to outliers)

    • :euclidean - Euclidean distance

    • :mahalanobis - Mahalanobis distance (accounts for covariance structure, automatically computed)

  • average_trials::Bool: Whether to average across trials before computing RDM (default: true)

  • normalize_method::Symbol: RDM normalization method (default: :none)

    • :none - No normalization

    • :zscore - Z-score normalization (mean=0, std=1)

    • :rank - Rank transformation (ordinal ranks)

    • :minmax - Min-max normalization (scales to [0, 1])

Returns

  • RsaData: Object containing RSA results

Examples

julia
# Basic RSA with correlation-based dissimilarity
epochs = [epoch_condition1, epoch_condition2, epoch_condition3]
rsa_result = rsa(epochs, channel_selection=channels([:Fz, :Cz, :Pz]), sample_selection=samples((-0.2, 0.8)))

# Using Euclidean distance
rsa_result = rsa(epochs, dissimilarity_measure=:euclidean)

# Using Mahalanobis distance (accounts for covariance structure)
rsa_result = rsa(epochs, dissimilarity_measure=:mahalanobis)

# Without averaging trials (computes RDM for each trial, then averages RDMs)
rsa_result = rsa(epochs, average_trials=false)

# With z-score normalization
rsa_result = rsa(epochs, normalize_method=:zscore)

# Use all channels and all time points (default)
rsa_result = rsa(epochs)
source
EegFun.rsa_crossvalidated Method
julia
rsa_crossvalidated(
    epochs::Vector{EpochData};
    channel_selection::Function = channels(),
    sample_selection::Function = samples(),
interval_selection::Interval = times(),
    dissimilarity_measure::Symbol = :correlation,
    cv_method::Symbol = :splithalf,
    n_folds::Int = 5,
    n_iterations::Int = 100,
    normalize_method::Symbol = :none,
    
)

Compute cross-validated RDMs to assess reliability of representational structure.

Cross-validation provides a robust estimate of RDM structure by computing RDMs on independent subsets of trials and averaging them. This reduces the impact of noise and outliers compared to computing a single RDM on all trials.

Cross-Validation Methods

Split-Half (:splithalf)

  • Randomly split trials into two halves

  • Compute RDM on each half independently

  • Average the two RDMs

  • Repeat for n_iterations and average results

  • Use when: You have many trials (20+) and want robust estimates

Leave-One-Out (:leaveoneout)

  • For each trial, compute RDM using all other trials

  • Average all leave-one-out RDMs

  • Use when: You have few trials (10-20) and want maximum data usage

  • Note: Computationally expensive for many trials

K-Fold (:kfold)

  • Split trials into n_folds groups

  • For each fold, compute RDM using all other folds

  • Average all fold RDMs

  • Use when: You want a balance between split-half and leave-one-out

Arguments

  • epochs::Vector{EpochData}: Vector of EpochData, one per condition

  • channel_selection::Function: Channel selection predicate (default: all channels)

  • sample_selection::Function: Sample selection predicate (default: all time points)

  • dissimilarity_measure::Symbol: Dissimilarity measure (:correlation, :spearman, :euclidean)

  • cv_method::Symbol: Cross-validation method (:splithalf, :leaveoneout, :kfold)

  • n_folds::Int: Number of folds for k-fold CV (default: 5)

  • n_iterations::Int: Number of iterations for split-half (default: 100)

  • normalize_method::Symbol: RDM normalization method (default: :none)

Returns

  • RsaData: Cross-validated RSA results

Examples

julia
# Split-half cross-validation (default)
rsa_cv = rsa_crossvalidated(epochs, cv_method=:splithalf, n_iterations=100)

# Leave-one-out cross-validation
rsa_cv = rsa_crossvalidated(epochs, cv_method=:leaveoneout)

# K-fold cross-validation
rsa_cv = rsa_crossvalidated(epochs, cv_method=:kfold, n_folds=5)

# With normalization
rsa_cv = rsa_crossvalidated(epochs, cv_method=:splithalf, normalize_method=:zscore)

Notes

  • Cross-validation requires sufficient trials per condition (minimum 10, preferably 20+)

  • Split-half is fastest and recommended for most cases

  • Leave-one-out is most thorough but computationally expensive

  • Results are more reliable than single RDM computation

source
EegFun.run_ica Method
julia
run_ica(dat::ContinuousData; n_components = nothing, sample_selection = samples(),
interval_selection = times(), channel_selection = channels(), include_extra = false,
percentage_of_data = 100.0, algorithm = :infomax, params = IcaPrms())
run_ica(epoched_data::Vector{EpochData}; n_components = nothing, sample_selection = samples(),
channel_selection = channels(), remove_duplicates = true, percentage_of_data = 100.0,
algorithm = :infomax, params = IcaPrms())

Run Independent Component Analysis (ICA) on EEG data.

The ContinuousData form runs ICA on a single recording. The Vector{EpochData} form concatenates all epochs into a continuous matrix before decomposition (the standard approach for epoched data, since ICA benefits from many data points).

⚠️ Pre-filter to ≥1 Hz before running ICA to ensure a good decomposition.

Key Arguments

  • n_components: Number of components (default: n_channels − 1)

  • sample_selection: Exclude bad samples (e.g., samples_not(:is_extreme_value_100))

  • percentage_of_data: Use a random subset of data for faster ICA (e.g., 25.0 = 25%)

  • algorithm: :infomax (default) or :infomax_extended (handles sub- and super-Gaussian sources)

  • remove_duplicates (Vector form only): Remove duplicate samples across conditions (default: true)

Returns

InfoIca with unmixing, mixing, sphere, variance, and metadata.

Examples

julia
# Continuous data
ica_result = run_ica(dat)
ica_result = run_ica(dat, sample_selection = samples_not(:is_extreme_value_100))
ica_result = run_ica(dat, percentage_of_data = 25.0)

# Epoched data
ica_result = run_ica(epochs_cleaned)
source
EegFun.sample_rate Method
julia
sample_rate(dat::EegData) -> Int

Get the sample rate of the EEG data.

Arguments

  • dat::EegData: The EEG data object

Returns

  • Int: Sample rate in Hz
source
EegFun.samples Method

Predicate generators for sample (row) selection.

source
EegFun.samples_or Method

Combine or negate sample predicates across boolean columns.

source
EegFun.search_sequence Method
julia
search_sequence(array, sequences::Vector{<:Vector}; ignore_values = [0], sort_indices = true) -> Vector{Vector{Int}}
search_sequence(array, sequence::Vector; ignore_values = [0], sort_indices = true) -> Vector{Vector{Int}}
search_sequence(array, value::Integer; ignore_values = [0]) -> Vector{Int}
search_sequence(array, range::UnitRange; sort_indices = true) -> Vector{Int}
search_sequence(array, ranges::Vector{<:UnitRange}; sort_indices = true) -> Vector{Int}

Search for trigger patterns in an array. Returns matched sample positions.

  • Multiple sequences (Vector{Vector}): OR logic — matches any of the provided sequences.

  • Single sequence (Vector): supports integer values, :any wildcard, and UnitRange elements.

  • Single value (Integer): onset detection — returns each start position.

  • Range / Vector{UnitRange}: returns indices of any value within the range(s).

Each match in the multi-element methods is a Vector{Int} of sample positions for each trigger in the matched sequence (e.g., [[30, 450], [800, 1220]]).

Examples

julia
# Single value
search_sequence([0, 1, 1, 0, 2], 1)              # => [2]

# Exact sequence
search_sequence([1, 0, 2, 3, 1, 0, 2, 3], [1, 2]) # => [[1,3], [5,7]]

# Wildcard
search_sequence([1, 2, 3], [[1, :any, 3]])        # => [[1,2,3]]

# Range
search_sequence([1, 2, 3, 4, 5], 1:3)             # => [1, 2, 3]

# Multiple sequences (OR)
search_sequence([1, 2, 1, 3, 1], [[1, 2, 1], [1, 3, 1]])  # => [[1,2,3], [3,4,5]]
source
EegFun.section Method

Return a triple-line section header with title centered between dashes.

source
EegFun.setup_logging Method
julia
setup_logging(log_file::String; log_level::Symbol = :info, is_global::Bool = false, include_kwargs::Bool = !is_global)

Set up logging to both stdout and a file.

Arguments

  • log_file: Path to the log file.

  • log_level: Logging level (:debug, :info, :warn, :error).

  • is_global: If true, sets up a session-wide log. If false (default), sets up for individual file processing.

  • include_kwargs: Whether to include metadata in the log. Defaults to true for local logs.

Returns

  • The file handle (for direct writing if desired).
source
EegFun.show_parameter_info Method
julia
show_parameter_info(; parameter_name::String="")

Display information about configuration parameters. If parameter_name is empty, shows all parameters. If parameter_name is provided, shows detailed information about that specific parameter.

Arguments

  • parameter_name::String: Optional path to a specific parameter (e.g., "filtering.highpass.cutoff")
source
EegFun.signal_example_composition Method
julia
signal_example_composition()

Interactive multi-signal composer with filtering and frequency-domain analysis.

Creates a GUI with three independent sine-wave generators, a noise source, optional low-pass and high-pass filters, and a live power spectrum. All controls update the plots in real time.

Plots

RowPlotDescription
1–3Signal 1–3Individual sine waves. Y-axes are linked — amplitude changes are visually comparable across all three.
4NoiseAdditive Gaussian noise.
5Combined SignalSum of all three signals plus noise. A red overlay shows the filtered version when a filter is active.
6Frequency DomainPower spectrum of the combined (or filtered) signal.

The x-axes of plots 1–5 are linked: panning or zooming one panel updates all time-domain panels.

Controls

ControlRangeDescription
Freq (×3)0–80 HzFrequency of each sine wave
Amp (×3)0–10Amplitude of each sine wave
Phase (×3)−π to πPhase offset of each sine wave
Noise0–2Standard deviation of additive Gaussian noise
☐ LP Filter0–100 HzLow-pass filter cutoff; enable with checkbox
☐ HP Filter0–2 HzHigh-pass filter cutoff; enable with checkbox

Examples

julia
using EegFun
EegFun.signal_example_composition()

Returns

  • fig::Figure: The Makie figure object containing the interactive GUI

  • axes::Vector{Axis}: The six axis objects (rows 1–6)

source
EegFun.signal_example_convolution Method
julia
signal_example_convolution()

Interactive Convolution Demo — Sliding a Kernel Across a Signal.

Demonstrates discrete convolution by sliding a kernel (filter) step by step across a signal, accumulating the output one position at a time. Three kernel types are provided: Gaussian (low-pass filter), Boxcar (running average), and Wavelet (Morlet) — which outputs the amplitude envelope at a specific frequency, bridging filtering and time-frequency analysis.

Plots

RowPlotDescription
1Signal + KernelThe input signal with the kernel overlaid at the current position. The shaded region shows where the kernel overlaps the signal.
2OutputThe convolution result accumulated up to the current position. For the Wavelet kernel this is the amplitude envelope (always ≥ 0, y-axis fixed 0–1.1).

Controls

ControlApplies toRangeDescription
Kernel PositionAll0–2 sScrub the kernel across the signal — output builds up as you drag right
Kernel WidthGaussian / Boxcar10–2000 msTotal span of the kernel (label shows "→ eff. X ms" in Wavelet mode)
CyclesWavelet1–10Number of oscillations in the wavelet kernel
Wavelet FreqWavelet1–40 HzTarget frequency to detect
Signal FreqAll1–20 HzFrequency of the input sine wave
NoiseAll0–1Additive Gaussian noise standard deviation
Kernel typeAllGaussian / Boxcar / Wavelet (Morlet)

Teaching Notes

Convolution computes, at each time point t, the weighted sum of the signal under the kernel:

julia
(x * h)[t] = Σ x[τ] · h[t  τ]

This is how FIR filters work. The kernel h is the filter's impulse response:

  • Gaussian → smooth low-pass filter (removes noise, preserves slow waves)

  • Boxcar → running average (same idea, sharper edges in frequency domain)

  • Wavelet (Morlet) → Gaussian × cosine at f Hz. Frequency-selective: output is the amplitude envelope "how much of f Hz is present right now?" This is one row of a time-frequency spectrogram.

The Kernel Width label shows "→ eff. X ms" in Wavelet mode — this is the equivalent Gaussian width, determined by cycles / wavelet_freq. Setting the Gaussian Kernel Width slider to that value produces the same Gaussian envelope.

See Also

  • Cohen, M. X. (2014). Analyzing Neural Time Series Data. MIT Press. — Chapter 11/12

Examples

julia
using EegFun
EegFun.signal_example_convolution()

Returns

  • fig::Figure: The Makie figure object

  • axes::Tuple: (ax_sig, ax_out) — the two axis objects

source
EegFun.signal_example_decoding Method
julia
signal_example_decoding()

Interactive MVPA Decoding Playground — Teaching Time-Resolved Classification.

Demonstrates how a Support Vector Machine (SVM) classifier can learn to distinguish two experimental conditions from multi-channel EEG data, one time point at a time. Synthetic data is generated internally — no real data required.

Plots

RowPlotDescription
1ERP WaveformsMean waveform per condition (blue vs red) with ±1 SE shading
2Decoding AccuracyTime-resolved classification accuracy with 50% chance line and SE band

Controls

ControlRangeDescription
Signal Strength0–3Amplitude of the condition difference (larger = easier to decode)
Noise Level0.1–5Background noise amplitude
N Trials10–200Trials per condition — more trials = better estimates
N Channels1–10Number of EEG channels (features for the classifier)
Effect Onset−0.2–0.8 sWhen the condition difference begins
Effect Duration0.05–0.8 sHow long the condition difference lasts
[Decode!]buttonRuns the SVM classification (takes a few seconds)

Teaching Notes

MVPA (Multi-Variate Pattern Analysis) asks: at each time point, can a classifier distinguish condition A from condition B using the pattern of activity across channels?

Unlike a t-test (which tests one channel at a time), MVPA considers the joint pattern across all channels — exploiting correlations that univariate methods miss.

Cross-validation prevents overfitting. The data is split into training and test sets; the classifier only sees training data during learning. If it performs above chance on the held-out test data, the effect is genuine.

Key insights to discover:

  • Signal strength directly controls accuracy — this is the effect size.

  • More channels help: the "multi" in multivariate adds information.

  • More trials produce smoother, more reliable accuracy estimates.

  • Effect onset/duration maps directly to the accuracy peak — the classifier finds the information exactly where it exists.

  • Zero signal strength → accuracy fluctuates around 50% (chance). This is what a null result looks like.

See Also

  • Grootswagers, T., Wardle, S. G., & Carlson, T. A. (2017). Decoding dynamic brain patterns from evoked responses: a tutorial on multivariate pattern analysis applied to time series neuroimaging data. Journal of Cognitive Neuroscience, 29(4), 677–697.

  • King, J.R. & Dehaene, S. (2014). Characterizing the dynamics of mental representations: the temporal generalization method. Trends in Cognitive Sciences, 18(4), 203-210.

Examples

julia
using EegFun
EegFun.signal_example_decoding()

Returns

  • fig::Figure: The Makie figure object

  • axes::Tuple: (ax_erp, ax_acc) — the two axis objects

source
EegFun.signal_example_dotproduct Method
julia
signal_example_dotproduct()

Interactive Dot Product Demo — How the DFT Detects Frequencies.

Shows the single core idea behind the Discrete Fourier Transform (DFT): multiplying a signal by a test sinusoid and summing the result. When the test frequency matches the signal frequency, the product is all-positive and the sum (dot product) is large. When they do not match, the product alternates and the sum is near zero.

A Complex toggle adds a cosine test sinusoid alongside the sine, showing how the DFT achieves phase-independence by computing the magnitude from both projections.

Plots

RowPlotDescription
1Signal + Test sinusoid(s)The target signal (blue), sine test (red), and cosine test (green, when Complex is on).
2Sine productSignal × sine test sinusoid, with dot product value.
3Cosine productSignal × cosine test sinusoid (visible only when Complex is on).

Controls

ControlRangeDescription
Signal Freq1–40 HzFrequency of the signal sine wave
Amplitude0.1–2Signal amplitude — dot product scales proportionally
Phase (°)0–355°Signal phase offset
Test Freq0.5–60 HzFrequency of the test sinusoid (the DFT "probe")
Noise0–1Noise standard deviation
ComplextoggleSwitch from sine-only to complex (sine + cosine) probing

Teaching Notes

The dot product measures how similar two signals are — multiply them point-by-point and sum the results:

julia
dot product = Σ a[t] × b[t]

The DFT uses exactly this idea to answer "how much of frequency f is in this signal?" by setting one of the signals to a test sinusoid:

julia
dot product = Σ signal[t] × sin(2πft)

Freq match, Phase match: product is all-positive → large sum.

Freq mismatch: product alternates → cancels to near zero.

Phase mismatch (90°): product alternates even at matching frequency → dot product ≈ 0.

The complex solution: the sine-only dot product is sensitive to phase. The DFT solves this by computing two dot products — one with a sine and one with a cosine — and combining them:

julia
magnitude =(sine_dp² + cosine_dp²)

The magnitude is phase-independent: it captures the signal's power at frequency f regardless of the signal's phase offset. Toggle Complex on and drag Phase to see this in action.

Amplitude: the test sinusoid is always ±1; the dot product scales directly with the signal amplitude.

The FFT runs this operation for every frequency simultaneously. This demo shows just one bin at a time.

See Also

  • Cohen, M. X. (2014). Analyzing Neural Time Series Data. MIT Press. — Chapter 11

Examples

julia
using EegFun
EegFun.signal_example_dotproduct()

Returns

  • fig::Figure: The Makie figure object

  • axes::Tuple: (ax_sig, ax_prod_sin, ax_prod_cos) — the three axis objects

source
EegFun.signal_example_ica Method
julia
signal_example_ica()

Interactive ICA Demo — Blind Source Separation.

Demonstrates how Independent Component Analysis (ICA) recovers independent source signals from their linear mixtures. Uses a 3-source / 3-sensor scenario where mixing is a composition of three 2D rotations (one per source pair), making the geometry tractable while realistic.

Layout

ColumnRow 1Row 2Row 3
LeftTrue sourcesEEG recordings (mixed)Recovered components
MiddleSources S1 vs S2Mixed M1 vs M2Recovered C1 vs C2
RightSources S1 vs S3Mixed M1 vs M3Recovered C1 vs C3

The scatter plots show pairwise joint distributions. Independent sources show a distinctive non-blob shape. Mixing rotates them into correlated blobs. Correct unmixing restores the shape.

Sources

  • S1: Neural oscillation — sine wave at user-controlled frequency

  • S2: Blink artifact — sparse gamma-shaped pulses (high kurtosis)

  • S3: Muscle burst — Gabor pulse (sinusoid × Gaussian envelope, high kurtosis)

Controls

ControlRangeDescription
Mix α (S1-S2)0 – 90°Rotation blending S1 and S2 into the recordings
Mix β (S1-S3)0 – 90°Rotation blending S1 and S3 into the recordings
Mix γ (S2-S3)0 – 90°Rotation blending S2 and S3 into the recordings
Unmix φ (S1-S2)0 – 90°Inverse rotation used to recover S1/S2 (manual mode)
Unmix ψ (S1-S3)0 – 90°Inverse rotation used to recover S1/S3 (manual mode)
Unmix χ (S2-S3)0 – 90°Inverse rotation used to recover S2/S3 (manual mode)
Signal Freq1 – 20 HzFrequency of the oscillatory source (S1)
Noise0 – 1Additive Gaussian noise on all sources
Auto-ICA buttonRuns Infomax ICA on the mixed signals to find W

Teaching Notes

The problem: EEG electrodes record mixtures of all underlying sources. Each electrode signal is a weighted sum — you never observe sources directly.

The geometry: With N sources and N sensors, mixing is an N×N rotation. For 3 sources we compose three pairwise 2D rotations (α, β, γ). Unmixing reverses the rotations in the opposite order with the same angles.

ICA's trick: It finds an unmixing matrix W such that components W·M are maximally non-Gaussian. Independent signals have higher combined non-Gaussianity than their mixtures (Central Limit Theorem in reverse).

The scatter plot: Scatter of two independent non-Gaussian signals shows a distinctive cross / T-shape. Mixed signals produce a correlated blob. The Auto-ICA button runs the real Infomax algorithm (Bell & Sejnowski, 1995) — the same one used by run_ica in EegFun — to find the unmixing matrix W directly.

What to try:

  1. Set all mixing angles > 0 and watch the scatters collapse into blobs.

  2. Drag unmixing sliders manually to match the mixing angles — sources reappear.

  3. Reset unmixing angles to 0, then click Auto-ICA — Infomax recovers the sources.

  4. Add Noise — Infomax still works because S2 and S3 are highly non-Gaussian.

  5. Compare manual rotation (angle sliders) with Infomax (Auto-ICA) — Infomax finds a general unmixing matrix, not just a rotation.

See Also

  • Bell, A. J., & Sejnowski, T. J. (1995). An information-maximization approach to blind separation. Neural Computation, 7(6), 1129–1159.

  • Hyvärinen, A., & Oja, E. (2000). Independent component analysis: algorithms and applications. Neural Networks, 13(4-5), 411–430.

Examples

julia
using EegFun
EegFun.signal_example_ica()

Returns

  • fig::Figure: The Makie figure object
source
EegFun.signal_example_mixing Method
julia
signal_example_mixing()

Interactive Demo — Signal Mixing & ICA Unmixing.

Demonstrates the fundamental concept behind ICA: EEG electrodes record mixtures of underlying sources, and ICA can separate them back out.

Uses a simple 2-source / 2-electrode scenario:

  • Source 1: A brain oscillation (alpha bursts at a controllable frequency)

  • Source 2: Eye blink artifacts (sharp upward deflections)

A single "Mix Amount" slider controls how much the two sources bleed into each electrode. Users can attempt manual unmixing before using the Unmix! button, which runs the Infomax ICA algorithm to automatically recover the original sources from the mixtures.

Layout

Column 1 (wide)Column 2 (wide)Column 3 (wide)
Source 1: BrainElectrode 1 (mix)Recovered 1
Source 2: BlinkElectrode 2 (mix)Recovered 2

Below the plots: mixing matrix, unmixing matrix, and match scores.

Controls

ControlRangeDescription
Mix Amount0 – 1How much sources bleed across electrodes
Brain Freq4 – 20 HzFrequency of the oscillatory source
Blink Size0.5 – 5Amplitude of blink artifacts
Noise0 – 1Additive sensor noise
Subtract E2→E10 – 1Manual: remove E2 contribution from E1
Subtract E1→E20 – 1Manual: remove E1 contribution from E2
Unmix!buttonRun Infomax ICA to find optimal weights
ResetbuttonClear ICA result

Teaching Notes

Think of it like two microphones in a room with two people talking. Each microphone picks up both voices, just at different volumes. ICA figures out who said what — even though neither microphone recorded a single voice cleanly.

In EEG terms:

  • The "people talking" are your brain signal and your eye blinks

  • The "microphones" are your electrodes

  • ICA is the clever algorithm that untangles them

See Also

  • signal_example_ica — more advanced 3-source version with rotation geometry and scatter plots

Examples

julia
using EegFun
EegFun.signal_example_mixing()

Returns

  • fig::Figure: The Makie figure object
source
EegFun.signal_example_sampling Method
julia
signal_example_sampling()

Interactive Signal Generator — Nyquist Theorem Demo

A Julia/Makie version of the MATLAB signalExample1.m GUI. Creates an interactive plot with sliders to control signal parameters and two optional overlays for comparing reconstruction methods.

Controls

ControlRangeDescription
Duration1–10 sLength of the displayed signal
Frequency1–100 HzFrequency of the sine wave
Phase Angle-π to πPhase offset of the sine wave
Sample Rate1–300 HzNumber of samples per second
LinearcheckboxShow linear (join-the-dots) reconstruction (red)
SinccheckboxShow ideal sinc reconstruction (green)

Teaching Notes

This demo illustrates the Nyquist–Shannon sampling theorem: a signal sampled at frequency fs can be perfectly reconstructed if fs ≥ 2f, where f is the signal frequency.

Linear reconstruction (red)

Joins consecutive sample points with straight lines. This is the simplest possible reconstruction and introduces high-frequency artifacts at the corners. To look convincingly smooth, linear reconstruction typically requires ≈5–10× the signal frequency — well above the Nyquist limit.

Sinc reconstruction (green)

Uses the Whittaker–Shannon interpolation formula: each sample is represented by a sinc function (sin(πt)/(πt)) centred at its sample time, and all contributions are summed to recover the original waveform:

julia
x(t) = Σ xₙ · sinc((t - nT) / T)

This is the ideal reconstruction implied by the Nyquist theorem. When the sample rate is ≥ 2× the signal frequency, the sinc reconstruction overlaps the original blue signal almost exactly. The green dots show the sample values the reconstruction is built from.

Try setting Sample Rate = 2 × Frequency and toggling each checkbox to see the difference between linear and ideal reconstruction.

See Also

Example

julia
using EegFun
EegFun.signal_example_sampling()

Returns

  • fig::Figure: The Makie figure object containing the interactive GUI

  • ax::Axis: The axis object containing the plots

source
EegFun.signal_example_spectrum Method
julia
signal_example_spectrum()

Interactive Power Spectrum Demo — From Time Domain to Frequency Domain.

Shows how a time-domain signal maps to a power spectrum via the FFT, and demonstrates the key spectral concepts relevant to EEG analysis: frequency resolution, spectral leakage, and windowing.

Plots

RowPlotDescription
1Time DomainThe composed signal for the full epoch duration.
2Power SpectrumOne-sided FFT power spectrum. Dashed vertical lines mark the true signal frequencies; the orange band marks one frequency-resolution bin (Δf = 1/T).

Controls

ControlRangeDescription
Freq 11–60 HzFrequency of the first sine component
Amp 10–2Amplitude of the first sine component
Freq 21–60 HzFrequency of the second sine component
Amp 20–2Amplitude of the second sine component (0 = off)
Noise0–2Standard deviation of additive Gaussian noise
Epoch1–10 sEpoch length — controls frequency resolution (Δf = 1/T)
Log scaletoggleLog y-axis on the spectrum — useful for comparing sidelobes

Teaching Notes

Frequency resolution is determined by the epoch length: Δf = 1/T. A 1-second epoch gives 1 Hz bins; a 4-second epoch gives 0.25 Hz bins.

Spectral leakage occurs when a signal frequency does not fall exactly on an FFT bin. Energy "leaks" into neighbouring bins, producing sidelobes. To see this: set Epoch = 1 s, Freq 1 = 10.25 Hz — the peak is smeared.

The orange band on the spectrum shows the current frequency resolution bin. Note how it shrinks as you increase the epoch length.

See Also

  • Cohen, M. X. (2014). Analyzing Neural Time Series Data. MIT Press. — Chapters 10–11

Examples

julia
using EegFun
EegFun.signal_example_spectrum()

Returns

  • fig::Figure: The Makie figure object

  • axes::Tuple: (ax_time, ax_freq) — the two axis objects

source
EegFun.signal_example_tf Method
julia
signal_example_tf()

Interactive Time-Frequency Analysis Demo — Method & Resolution Comparison

Demonstrates the time-frequency resolution trade-off (Heisenberg uncertainty principle) and compares three TF analysis methods on a synthetic signal composed of up to three independent frequency components.

Methods

MethodWindow shapeParameterKey property
Morlet WaveletGaussianCyclesAdaptive: window length = cycles / frequency
STFT (Hanning)HanningWindow (ms)Fixed: same window length for all frequencies
Multitaper (DPSS)Slepian tapersCyclesAdaptive: multiple tapers reduce variance

Note on the parameter slider: For Morlet and Multitaper the slider sets the number of wavelet cycles. For STFT it sets a fixed window length using a 30 Hz reference (e.g. 7 cycles → 233 ms). This makes the comparison explicit: Morlet adapts the window per frequency, STFT uses one window for all frequencies.

Plots

RowPlotDescription
1Time DomainSynthetic signal averaged across trials. Dashed line = t = 0.
2Time-Frequency MapPower heatmap computed with the selected method.

Controls

ControlRangeDescription
MethodMorlet / STFT / MultitaperTF decomposition method
Cycles / Window3–12Cycles (Morlet/Multitaper) or window = N/30 Hz (STFT)
Noise0.5–5Amplitude of additive white noise per trial
BaselineNone / dB / % Change / …Baseline correction type (uses tf_baseline)
Window−1.0–2.0 sBaseline time window (IntervalSlider)
Freq scaleLinear / LogToggle log₁₀ y-axis on the TF map
☐ Comp 1/2/3on/offEnable/disable each component
Freq (×3)1–40 HzComponent frequency
Amp (×3)0–5Component amplitude
Interval (×3)−1.0–2.0 sComponent onset–offset window

Teaching Notes

The Heisenberg uncertainty principle for TF analysis: you cannot have perfect time and frequency resolution simultaneously.

  • Morlet, low cycles: narrow Gaussian → good time localisation, blurry frequency.

  • Morlet, high cycles: wide Gaussian → sharp frequency, smeared in time.

  • STFT vs Morlet (same slider value): STFT uses the same window for all frequencies, so low-frequency bands are blurry (few cycles fit the window). Morlet adapts — resolution improves for high frequencies automatically.

  • Multitaper vs Morlet: similar resolution trade-off but lower variance, because multiple orthogonal tapers provide independent spectral estimates.

See Also

  • Cohen, M. X. (2014). Analyzing Neural Time Series Data. MIT Press. — Chapters 12–16

Examples

julia
using EegFun
EegFun.signal_example_tf()

Returns

  • fig::Figure: The Makie figure object

  • axes::NamedTuple: Named tuple with :time and :tf axes

source
EegFun.signal_to_data Method
julia
signal_to_data(times, signal, channel_name::Symbol, sample_rate::Real;
                 file::String="synthetic", condition::Int=1, condition_name::String="test") -> Union{EpochData, ErpData}

Internal function: Convert signal data from generate_signal to EpochData or ErpData format. Returns ErpData when there is only one epoch, otherwise returns EpochData.

source
EegFun.simple_string_param Function

Create a simple String parameter.

source
EegFun.simulate_erp Method
julia
simulate_erp()

Interactive ERP simulator for teaching signal averaging and component analysis.

Generates synthetic EEG trials from up to 5 independent ERP components and displays individual trial waveforms (grey) alongside the trial-average ERP (blue) in real time. Demonstrates how signal-to-noise ratio improves as the number of averaged trials increases.

What it shows

  • Trial waveforms — individual simulated EEG trials (shown in grey), each with realistic 1/f background noise.

  • ERP average — the mean across all trials (shown in blue), which clarifies as trial count increases.

  • ERP components — up to 5 independent cosine-shaped components, each with independently controllable frequency, amplitude, latency, and trial-to-trial jitter.

Teaching tips

  • Start with 1 trial and a single active component. The ERP and the trial are identical.

  • Increase noise to bury the component in background activity, then increase the number of trials to show the ERP emerging from the noise.

  • Activate a second component at a different latency to show how multiple ERP components superpose in the waveform.

  • Add amplitude or latency jitter to demonstrate why averaging can attenuate and smear ERP components.

Controls

ControlRangeDescription
Number of Trials1–500How many simulated trials to average
Noise Amplitude0–20Amplitude of background 1/f EEG noise
Active (toggle, ×5)on/offEnable/disable each component
Freq (×5)0.1–5.0 HzCosine frequency of the component
Amp (×5)−10 to 10 μVPeak amplitude of the component
Latency (×5)0–1000 msPeak latency of the component
Amp Jitter (×5)0–20Trial-to-trial amplitude variability
Lat Jitter (×5)0–50 msTrial-to-trial latency variability

Examples

julia
using EegFun
EegFun.simulate_erp()

Returns

  • fig::Figure: The Makie figure object

  • ax::Axis: The main plot axis

source
EegFun.string_param Function

Create a string (or string-vector) parameter.

source
EegFun.subsection Method

Return a single-line sub-section header.

source
EegFun.subset Method
julia
subset(dat::SingleDataFrameEeg; channel_selection, sample_selection, interval_selection, include_extra)

Create a subset of single-DataFrame EEG data (ContinuousData, ErpData).

Arguments

  • channel_selection::Function: Channel predicate (default: channels() for all)

  • sample_selection::Function: Sample predicate (default: samples() for all)

  • interval_selection::Interval: Time interval (default: times() for all)

  • include_extra::Bool: Include extra columns (default: false)

Examples

julia
# Select specific channels and time window
sub = subset(erp, channel_selection = channels(:Fz, :Cz), interval_selection = times(-0.2, 0.5))
source
EegFun.subset_bad_data Method
julia
subset_bad_data(data_path::String, threshold::Float64; 
                subset_directory::String = "excluded")

Identify and move files from participants with low data retention to a separate directory.

Participants are considered "bad" if they have less than the threshold percentage of data remaining in ANY condition. All files associated with bad participants are moved to a subdirectory (default: "excluded").

The function searches for "epoch_summary.jld2" in the specified directory.

Arguments

  • data_path::String: Path to the directory containing epoch_summary.jld2 and preprocessed files

  • threshold::Float64: Minimum percentage threshold (e.g., 75.0 means 75% retention required)

Keyword Arguments

  • subset_directory::String: Name of subdirectory for excluded participants (default: "excluded")

Examples

julia
# Move participants with < 75% data in any condition to "excluded" subdirectory
subset_bad_data("preprocessed_files", 75.0)

# Specify custom subset directory name
subset_bad_data("/path/to/preprocessed", 80.0, subset_directory="excluded")
source
EegFun.subset_layout! Method
julia
subset_layout!(layout::Layout; channel_selection = channels())

Subset a Layout object to include only channels that match the channel selection predicate. Modifies the layout in place.

Arguments

  • layout::Layout: The layout to subset

  • channel_selection::Function: Channel selection predicate function (default: all channels)

Returns

  • nothing (modifies the layout in place)
source
EegFun.subset_layout Method
julia
subset_layout(layout::Layout; channel_selection = channels())

Create a subset copy of a Layout object using channel selection predicates.

Arguments

  • layout::Layout: The layout to subset

  • channel_selection::Function: Channel selection predicate function (default: all channels)

Returns

  • Layout: A new subset layout object
source
EegFun.subsubsection Method

Return a # Title sub-sub-section header.

source
EegFun.summarize_electrode_repairs Method
julia
summarize_electrode_repairs(continuous_repairs::Vector{ContinuousRepairInfo})

Summarize electrode repairs from a vector of ContinuousRepairInfo objects (continuous level only). Each ContinuousRepairInfo is treated as coming from a separate participant.

Arguments

  • continuous_repairs::Vector{ContinuousRepairInfo}: Vector of ContinuousRepairInfo objects

Returns

  • DataFrame: Summary table with columns:
    • electrode::Symbol: Electrode name

    • n_participants::Int: Number of participants where this electrode was repaired at the continuous level

source
EegFun.summarize_ica_components Method
julia
summarize_ica_components(ica_components::Vector{ArtifactComponents})

Summarize ICA components removed across all participants.

Arguments

  • ica_components::Vector{ArtifactComponents}: Vector of ArtifactComponents objects

Returns

  • DataFrame: Summary table with columns:

    • file::String: Filename (empty string when called with Vector{ArtifactComponents})

    • total_components::Int: Total number of ICA components removed

    • vEOG::Int: Number of vEOG components

    • hEOG::Int: Number of hEOG components

    • ECG::Int: Number of ECG components

    • line_noise::Int: Number of line noise components

    • channel_noise::Int: Number of channel noise components

  • DataFrame: Average summary with columns:

    • component_type::String: Type of component

    • avg_per_participant::Float64: Average number of components per participant

source
EegFun.tail Method
julia
tail(dat::EegData; n=5)

Display and return the last n rows of the EEG data.

Examples

julia
tail(erps[1])        # Last 5 rows
tail(erps[1], n=10)  # Last 10 rows
source
EegFun.test_against_chance Method
julia
test_against_chance(decoded_list::Vector{DecodedData}; alpha::Float64 = 0.05, correction_method::Symbol = :none)

Perform one-sample t-test against chance level across multiple participants.

Tests whether decoding accuracy is significantly above chance at each time point, using the across-participant variance. This is a one-tailed test (right tail) testing if accuracy > chance level.

Arguments

  • decoded_list::Vector{DecodedData}: Vector of DecodedData objects, one per participant

Keyword Arguments

  • alpha::Float64: Significance threshold (default: 0.05)

  • correction_method::Symbol: Multiple comparison correction - :none (default) or :bonferroni

Returns

  • DecodingStatisticsResult: Results with t-statistics, p-values, and significance masks

Examples

julia
# Test across participants
decoded_list = [decoded_p1, decoded_p2, decoded_p3]
stats = test_against_chance(decoded_list, alpha=0.05, correction_method=:bonferroni)
source
EegFun.test_against_chance_cluster Method
julia
test_against_chance_cluster(decoded_list::Vector{DecodedData};
                           alpha::Float64 = 0.05,
                           n_permutations::Int = 1000,
                           cluster_statistic::Symbol = :sum,
                           show_progress::Bool = true)

Perform cluster-based permutation test against chance level for decoding results.

This method uses cluster-based permutation testing to control for multiple comparisons across time points. Clusters of contiguous significant time points are identified, and their cluster-level statistics are compared to a permutation distribution. This is a one-tailed test (right tail) testing if accuracy > chance level.

Arguments

  • decoded_list::Vector{DecodedData}: Vector of DecodedData objects, one per participant

Keyword Arguments

  • alpha::Float64: Significance threshold (default: 0.05)

  • n_permutations::Int: Number of permutations (default: 1000)

  • cluster_statistic::Symbol: Cluster statistic - :sum (default) or :max

  • show_progress::Bool: Show progress bar (default: true)

Notes

For reproducible results, call Random.seed!(xxx) in your Julia session before running the test.

Returns

  • DecodingStatisticsResult: Results with t-statistics, p-values, significance masks, and clusters

Examples

julia
# Test across participants with cluster-based correction
decoded_list = [decoded_p1, decoded_p2, decoded_p3]
stats = test_against_chance_cluster(decoded_list, alpha=0.05, n_permutations=1000)
source
EegFun.tf_baseline! Method
julia
tf_baseline!(tf_data::TimeFreqData, baseline_interval; method=:db)

Apply baseline correction to TimeFreqData in-place.

Arguments

  • tf_data::TimeFreqData: Time-frequency data to baseline correct

  • baseline_interval::Tuple{Real,Real}: Time window for baseline (start, stop) in seconds

Keyword Arguments

  • method::Symbol=:db: Baseline method
    • :absolute: Absolute change (power - baseline_mean) - simple subtraction, no normalization

    • :relative: Relative power (power / baseline_mean) - ratio,

    • :relchange: Relative change ((power - baseline_mean) / baseline_mean) - fractional change

    • :normchange: Normalized change ((power - baseline) / (power + baseline)) - symmetric normalization

    • :db: Decibel change (10 * log10(power/baseline_mean))

    • :vssum: Alias for :normchange

    • :zscore: Z-score normalization ((power - baseline_mean) / baseline_std)

    • :percent: Percent change (100 * (power - baseline) / baseline) - convenience alias for relchange × 100

Example

julia
tf_baseline!(tf_data, (-0.3, 0.1); method=:db)
source
EegFun.tf_baseline Method
julia
tf_baseline(file_pattern::String, baseline_interval;
            method=:db, input_dir=pwd(), output_dir=nothing,
            participant_selection=participants(), condition_selection=conditions())

Apply baseline correction to TimeFreqData files.

Example

julia
tf_baseline("tf_epochs_wavelet", (-0.3, 0.0); method=:db)
source
EegFun.tf_morlet Method
julia
tf_morlet(file_pattern::String;
          input_dir::String=pwd(),
          output_dir::Union{String,Nothing}=nothing,
          participant_selection::Function=participants(),
          condition_selection::Function=conditions(),
          kwargs...)

Batch Morlet wavelet time-frequency analysis on EpochData files.

Loads .jld2 files containing EpochData (one or more conditions per file), runs tf_morlet on each condition, and saves the results as Vector{TimeFreqData}.

Arguments

  • file_pattern::String: Pattern to match files (e.g., "epochs", "epochs_cleaned")

  • input_dir::String: Input directory containing JLD2 files (default: current directory)

  • output_dir::Union{String, Nothing}: Output directory (default: creates subdirectory)

  • participant_selection::Function: Participant selection predicate (default: participants() for all)

  • condition_selection::Function: Condition selection predicate (default: conditions() for all)

  • All other keyword arguments are forwarded to tf_morlet (e.g., frequencies, cycles, pad, filter_edges)

Examples

julia
# Run Morlet wavelet TF on all epoch files
tf_morlet("epochs_cleaned"; cycles=7)

# Specific participants, custom frequencies
tf_morlet("epochs_cleaned";
          participant_selection=participants([1, 2, 3]),
          frequencies=logrange(2, 40, length=30),
          cycles=(3, 10))

# Custom output directory
tf_morlet("epochs"; cycles=7, output_dir="/data/tf_results")
source
EegFun.tf_multitaper Method
julia
tf_multitaper(dat::EpochData; 
              channel_selection::Function=channels(),
              interval_selection::Interval=samples(),
              frequencies::Union{AbstractRange,AbstractVector{<:Real}}=range(1, 40, length=40),
              cycles::Real,
              frequency_smoothing::Union{Nothing,Real}=nothing,
              time_steps::Real=0.05,
              pad::Union{Nothing,Symbol}=nothing,
              return_trials::Bool=false,
              filter_edges::Bool=true)

Multitaper time-frequency analysis using DPSS (Discrete Prolate Spheroidal Sequences) tapers (Cohen Chapter 16, equivalent to FieldTrip's 'mtmconvol' method).

Uses multiple orthogonal tapers (Slepian sequences) to reduce variance in spectral estimates compared to single-taper methods. Uses adaptive window lengths (cycles per frequency) to match the time-frequency trade-off of Morlet wavelets and adaptive STFT.

Arguments

  • dat::EpochData: Epoched EEG data

Keyword Arguments

  • channel_selection::Function=channels(): Channel selection predicate. See channels() for options.

    • Example: channel_selection=channels(:Cz) for single channel

    • Example: channel_selection=channels([:Cz, :Pz]) for multiple channels

  • interval_selection::Interval=samples(): Sample selection predicate. See samples() for options.

    • Example: sample_selection=samples((-0.5, 2.0)) for time interval from -0.5 to 2.0 seconds

    • Example: sample_selection=samples() for all time points (default)

    • Default: all samples

  • frequencies::Union{AbstractRange,AbstractVector{<:Real}}=range(1, 40, length=40): Frequency specification.

    • Can be any range or vector of frequencies in Hz

    • For linear spacing: frequencies=1:1:40 or frequencies=range(1, 40, length=40)

    • For logarithmic spacing: frequencies=logrange(1, 40, length=30)

    • Default: range(1, 40, length=40) (40 linearly-spaced frequencies from 1 to 40 Hz)

  • cycles::Real: Number of cycles per frequency. Window length = cycles / frequency (in seconds).

    • Example: cycles=5 uses 5 cycles for all frequencies

    • Lower frequencies will have longer windows, higher frequencies will have shorter windows

  • frequency_smoothing::Union{Nothing,Real}=nothing: Frequency smoothing parameter

    • If nothing, uses frequency_smoothing = 0.4 * frequency

    • If a number, uses that value multiplied by frequency: tapsmofrq = frequency_smoothing * frequency

    • Example: frequency_smoothing=0.4

    • Controls time-bandwidth product: NW = tapsmofrq * window_length / 2

    • Number of tapers used: K = 2*NW - 1 (rounded down)

  • time_steps::Real=0.05: Step size for extracting time points in seconds.

    • Creates time points from the selected time range with the specified step size

    • Default: 0.05 (50 ms)

    • Example: time_steps=0.01 creates time points every 0.01 seconds within the selected time interval

  • pad::Union{Nothing,Symbol}=nothing: Padding method to reduce edge artifacts. Options:

    • nothing: No padding (default)

    • :pre: Mirror data before each epoch

    • :post: Mirror data after each epoch

    • :both: Mirror data on both sides (recommended)

  • return_trials::Bool=false: If true, returns TimeFreqEpochData with individual trials preserved.

    • If false (default), returns TimeFreqData with trials averaged.
  • filter_edges::Bool=true: If true (default), filters out edge regions where the window extends beyond the data

Returns

  • TimeFreqData (if return_trials=false): Time-frequency data with trials averaged

  • TimeFreqEpochData (if return_trials=true): Time-frequency data with individual trials preserved

Examples

julia
# Default: linear frequencies 1-40 Hz, 40 points
tf_data = tf_multitaper(epochs; cycles=5)

# Log-spaced frequencies with default frequency smoothing (0.4 * frequency)
tf_data = tf_multitaper(epochs; frequencies=logrange(2, 80, length=30), cycles=5)

# Custom frequency smoothing
tf_data = tf_multitaper(epochs; frequencies=1:2:30, cycles=5, frequency_smoothing=0.4)
source
EegFun.tf_stft Method
julia
tf_stft(dat::EpochData; 
        channel_selection::Function=channels(),
        interval_selection::Interval=samples(),
        frequencies::Union{AbstractRange,AbstractVector{<:Real}}=range(1, 40, length=40),
        window_length::Union{Nothing,Real}=nothing,
        cycles::Union{Nothing,Real}=nothing,
        time_steps::Real=0.05,
        pad::Union{Nothing,Symbol}=nothing,
        return_trials::Bool=false,
        filter_edges::Bool=true)

Short-Time Fourier Transform (STFT) time-frequency analysis using a sliding Hanning window

Supports both fixed-length windows (consistent time resolution) and adaptive windows (constant cycles per frequency).

Arguments

  • dat::EpochData: Epoched EEG data

Keyword Arguments

  • channel_selection::Function=channels(): Channel selection predicate. See channels() for options.

    • Example: channel_selection=channels(:Cz) for single channel

    • Example: channel_selection=channels([:Cz, :Pz]) for multiple channels

  • interval_selection::Interval=samples(): Sample selection predicate. See samples() for options.

    • Example: sample_selection=samples((-0.5, 2.0)) for time interval from -0.5 to 2.0 seconds

    • Example: sample_selection=samples() for all time points (default)

    • Default: all samples

  • frequencies::Union{AbstractRange,AbstractVector{<:Real}}=range(1, 40, length=40): Frequency specification.

    • Can be any range or vector of frequencies in Hz

    • For linear spacing: frequencies=1:1:40 or frequencies=range(1, 40, length=40)

    • For logarithmic spacing: frequencies=logrange(1, 40, length=30)

    • Default: range(1, 40, length=40) (40 linearly-spaced frequencies from 1 to 40 Hz)

  • window_length::Union{Nothing,Real}=nothing: Fixed window length in seconds (same for all frequencies).

    • Example: window_length=0.3 uses a fixed 0.3 second window for all frequencies

    • Exactly one of window_length or cycles must be specified.

  • cycles::Union{Nothing,Real}=nothing: Number of cycles per frequency (adaptive window).

    • Example: cycles=7 uses 7 cycles per frequency (window length = 7/frequency)

    • Exactly one of window_length or cycles must be specified.

  • time_steps::Real=0.05: Step size for extracting time points in seconds.

    • Creates time points from the selected time range with the specified step size

    • Default: 0.05 (50 ms)

    • Example: time_steps=0.01 creates time points every 0.01 seconds within the selected time interval

  • pad::Union{Nothing,Symbol}=nothing: Padding method to reduce edge artifacts. Options:

    • nothing: No padding (default). Time points where intervals extend beyond data boundaries are excluded with a warning.

    • :pre: Mirror data before each epoch

    • :post: Mirror data after each epoch

    • :both: Mirror data on both sides (recommended)

  • return_trials::Bool=false: If true, returns TimeFreqEpochData with individual trials preserved.

    • If false (default), returns TimeFreqData with trials averaged.
  • filter_edges::Bool=true: If true (default), filters out edge regions where the interval extends beyond the data

Returns

  • TimeFreqData (if return_trials=false): Time-frequency data with trials averaged

  • TimeFreqEpochData (if return_trials=true): Time-frequency data with individual trials preserved

Examples

julia
# Default: linear frequencies 1-40 Hz, 40 points, fixed window
tf_data = tf_stft(epochs; window_length=0.3)

# Log-spaced frequencies with fixed window
tf_data = tf_stft(epochs; frequencies=logrange(2, 80, length=30), window_length=0.3)

# Adaptive window: 7 cycles per frequency 
tf_data = tf_stft(epochs; frequencies=2:1:30, cycles=7, sample_selection=samples((-0.5, 1.5)), time_steps=0.05)
source
EegFun.time_vector Method
julia
time_vector(dat::EegData) -> Vector{Float64}
time_vector(dat::EegData, epoch::Int) -> Vector{Float64}
time_vector(df::DataFrame) -> Vector{Float64}

Get the time column from the EEG data or DataFrame as a vector.

Arguments

  • dat::EegData: The EEG data object

  • df::DataFrame: A DataFrame with a :time column

Returns

  • Vector{Float64}: Time vector in seconds

Notes

  • For MultiDataFrameEeg types (EpochData, etc.), returns time from first epoch

  • All epochs are assumed to have identical time vectors

source
EegFun.times Method
julia
times()

Select all time points (default for interval parameters). Returns nothing which means no time filtering.

source
EegFun.to_data_frame Method
julia
to_data_frame(dat::MultiDataFrameEeg) -> DataFrame

Convert a multi-dataframe EEG object to a single DataFrame by concatenating all epochs.

source
EegFun.trigger_count Method
julia
trigger_count(dat::ContinuousData)::TriggerInfo
trigger_count(df::DataFrame)::TriggerInfo

Count occurrences of each trigger value in ContinuousData or DataFrame.

This function analyzes trigger data to provide a summary of how many times each trigger value appears in the dataset. Zero values are excluded from the count. Useful for validating experimental paradigms and checking trigger timing.

The returned TriggerInfo object displays as a formatted table and can be accessed like a DataFrame (e.g., info.data.trigger, info.data.count).

Arguments

  • dat::ContinuousData: The ContinuousData object containing EEG data.

  • df::DataFrame: A DataFrame with a :trigger column.

Returns

A TriggerInfo object containing trigger counts. Display the object to see a formatted table.

Examples

julia
# Get trigger counts from ContinuousData
trigger_counts = trigger_count(dat)

# Get trigger counts from DataFrame
trigger_counts = trigger_count(df)

# Display the table
trigger_counts

# Access DataFrame properties
trigger_counts.data.trigger
trigger_counts.data.count
source
EegFun.unique_channels Method

Return unique channels from an EpochRejectionInfo or vector thereof.

source
EegFun.unique_channels Method

Return the distinct channel symbols across a vector of rejections.

source
EegFun.unique_epochs Method

Return unique epochs from an EpochRejectionInfo or vector thereof.

source
EegFun.unique_epochs Method

Return the distinct epoch indices across a vector of rejections.

source
EegFun.unique_rejections Method

Return unique rejections from an EpochRejectionInfo.

source
EegFun.unique_rejections Method

Return unique rejections for each EpochRejectionInfo in a vector.

source
EegFun.unique_rejections Method

De-duplicate a vector of Rejections by (channel, epoch).

source
EegFun.unmirror Function
julia
unmirror(dat::EpochData, side::Symbol = :both) -> EpochData
unmirror(dat::ErpData, side::Symbol = :both) -> ErpData
unmirror(data_vec::Vector{EpochData}, side::Symbol = :both) -> Vector{EpochData}
unmirror(data_vec::Vector{ErpData}, side::Symbol = :both) -> Vector{ErpData}

Non-mutating version of unmirror!. Returns a new object with mirrored sections removed, leaving the original unchanged.

Arguments

  • dat / data_vec: EpochData, ErpData, or a Vector of either

  • side: :pre, :post, or :both (default: :both) — must match the original mirror! call

source
EegFun.unmirror! Function
julia
unmirror!(dat::EpochData, side::Symbol = :both) -> Nothing
unmirror!(dat::ErpData, side::Symbol = :both) -> Nothing
unmirror!(data_vec::Vector{EpochData}, side::Symbol = :both) -> Nothing
unmirror!(data_vec::Vector{ErpData}, side::Symbol = :both) -> Nothing

Remove mirrored sections from data in-place, restoring the original length. Must be called with the same side as the preceding mirror! call.

Arguments

  • dat / data_vec: EpochData, ErpData, or a Vector of either

  • side: :pre, :post, or :both (default: :both)

Examples

julia
mirror!(epochs, :both)
filter!(epochs, 1.0, filter_type = "hp")
unmirror!(epochs, :both)  # restores original length
source
EegFun.validate_layout Method
julia
validate_layout(layout::Layout) -> Layout

Validate that a layout contains all required columns and data.

This function checks that the layout DataFrame contains the minimum required columns for EEG layout operations.

Arguments

  • layout::Layout: The layout object to validate

Returns

  • Layout: The validated layout object

Throws

  • ArgumentError: If required columns are missing

Required Columns

  • :label: Electrode labels (Symbol type)

  • :inc: Incidence angles in degrees

  • :azi: Azimuth angles in degrees

Examples

julia
layout = read_layout("biosemi64.csv")
validated_layout = validate_layout(layout)
source
EegFun.version_info Method
julia
version_info() -> Dict{String,Any}

Get version information for logging purposes.

Returns a dictionary with the keys:

  • "julia_version": Julia version string

  • "EegFun_version": EegFun package version string

  • "timestamp": Current timestamp string

Examples

julia
ver_info = version_info()
@info "Starting analysis" ver_info...

@info "Running EegFun $(ver_info["EegFun_version"]) on Julia $(ver_info["julia_version"])"
source
EegFun.viewer Method
julia
viewer(dat)

Display data using VS Code viewer if available, otherwise use standard display. If all_data(dat) is defined for the type, it is called first to extract a DataFrame — so adding viewer support for a new type only requires adding an all_data method in data.jl.

Arguments

  • dat: Any data object to display
source
EegFun.@add_nonmutating Macro
julia
@add_nonmutating function_name!

Create non-mutating versions of all methods of a function that ends with !. Preserves method signatures and documentation.

Example: @add_nonmutating filter_data!

source
EegFun.@log_call Macro
julia
@log_call func_name

Macro to log a function call by reading the last command from history file. If history is unavailable, it logs the provided function name.

Arguments

  • func_name: String or Symbol representing the function name.
source
EegFun.@minimal_error Macro
julia
@minimal_error(msg)

Throws an EegFunError with a clean user-facing error message. Also logs the error via @error for diagnostics.

source
EegFun.@minimal_stacktrace Macro
julia
@minimal_stacktrace(msg, e, max_lines=5)

Log an error with a limited stacktrace to avoid huge log files.

Arguments

  • msg::String: Error message

  • e: The exception object

  • max_lines::Int: Maximum number of stacktrace lines to include (default: 5)

Example

julia
catch e
    @minimal_stacktrace "Error processing file" e
    @minimal_stacktrace "Error processing file" e 10  # with custom max_lines
end
source
EegFun.@minimal_warning Macro
julia
@minimal_warning(msg)

Displays a warning message without showing module/file/line metadata.

source