Analysis Functions
All public analysis and processing functions in EegFun.jl.
Functions
Base.copy Method
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) -> InfoIcaCreate a shallow copy of an EegFun data object. DataFrames are copied with copycols=true for independence; small immutable fields are shared.
Base.show Method
Pretty-print an EpochRejectionInfo summary with criterion, counts, and breakdowns.
Base.show Method
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.
sourceEegFun.add_noise_ceiling! Method
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 objectscorrelation_method::Symbol: Correlation method (:spearman or :pearson)
Returns
rsa_data::RsaData: The same RsaData object with noise_ceiling field populated
Examples
# 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)EegFun.add_topo_rois! Method
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 tolayout: 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
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)EegFun.add_zscore_columns Function
add_zscore_columns(df::DataFrame, exclude_columns::Vector{Symbol} = [:row])Non-mutating version of add_zscore_columns!.
Arguments
df::DataFrame: The correlation matrix DataFrameexclude_columns::Vector{Symbol}: Columns to exclude from z-score calculation (default: [:row])
Returns
DataFrame: A new DataFrame with z-score columns added
Examples
# 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])
))EegFun.add_zscore_columns! Function
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 modifyexclude_columns::Vector{Symbol}: Columns to exclude from z-score calculation (default: [:row])
Returns
DataFrame: The modified DataFrame with z-score columns added
Examples
# 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])EegFun.all_data Method
Flatten all rejection data (abs + z-score) into a single DataFrame with reason tags.
sourceEegFun.all_data Method
all_data(dat::EegData) -> DataFrameGet the complete DataFrame with all columns.
Arguments
dat::EegData: The EEG data object
Returns
DataFrame: Complete DataFrame with all columns
EegFun.all_labels Method
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
EegFun.analytic_test Method
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 fromprepare_statsalpha::Float64: Significance threshold (default: 0.05)tail::Symbol: Test tail -:both(default),:left, or:rightcorrection_method::Symbol: Multiple comparison correction -:no(default) or:bonferroni
Returns
AnalyticResult: Results structure with t-statistics, p-values, and significant masks
Examples
result = analytic_test(prepared, alpha=0.05, correction_method=:no)
result = analytic_test(prepared, alpha=0.05, correction_method=:bonferroni)EegFun.apply_analysis_settings Function
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.
EegFun.apply_analysis_settings! Method
apply_analysis_settings!(data::EegData, settings::AnalysisSettings)Apply analysis settings to data in-place.
sourceEegFun.average_epochs Method
average_epochs(dat::EpochData)Average epochs to create an ERP. This function:
Concatenates all epochs
Groups by time point and condition
Averages the EEG channels at each time point
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
EegFun.average_number_of_neighbours Method
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
layout = read_layout("./layouts/biosemi64.csv")
get_neighbours_xy!(layout, 0.4)
avg_neighbours = average_number_of_neighbours(layout.neighbours)EegFun.baseline Function
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.
EegFun.baseline! Method
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()) -> NothingApply 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}, orVector{ErpData}baseline_interval: time range as a tuple(-0.2, 0.0)or a range-0.2:0.0channel_selection: channel predicate (default: all channels)
Examples
# 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)EegFun.baseline Method
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 secondsRange:
-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
# 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 directlyEegFun.basename_without_ext Method
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
filename = basename_without_ext("data/file.bdf")EegFun.calculate_eog_channels! Method
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 modifyeog_cfg::EogConfig: EOG configuration object (primary method)eog_cfg::Dict: Configuration dictionary containing EOG channel settings (convenience method, converted toEogConfig)
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
# 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)EegFun.channel_average Function
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.
EegFun.channel_average! Method
channel_average!(dat::EegData; channel_selections::AbstractVector{<:Function} = [channels()],
output_labels = nothing,
include_extra::Bool = false,
reduce::Bool = false) -> NothingCreate averaged channels for each provided channel-selection predicate and add them to dat.
channel_selectionsis a vector of channel predicates (seechannels(...)) 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
# 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)EegFun.channel_average Method
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
# 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)EegFun.channel_data Method
channel_data(eeg_data::EegData) -> DataFrameGet EEG channel data columns from the EEG data.
sourceEegFun.channel_delete! Method
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 modifychannels_to_delete::Union{Symbol, Vector{Symbol}}: Channel(s) to delete
Returns
nothing(modifies the data in place)
Examples
channel_delete!(dat, :Fp1) # Delete a single channel
channel_delete!(dat, [:Fp1, :Fp2, :Diode]) # Delete multiple channelsNotes
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
EegFun.channel_delete Method
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 modifychannels_to_delete::Union{Symbol, Vector{Symbol}}: Channel(s) to delete
Returns
EegData: A new EEG data object with channels deleted
Examples
new_dat = channel_delete(dat, :Fp1) # Delete a single channel
new_dat = channel_delete(dat, [:Fp1, :Fp2, :Diode]) # Delete multiple channelsNotes
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
EegFun.channel_difference Function
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.
EegFun.channel_difference! Method
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
# 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)EegFun.channel_joint_probability Method
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 objectchannel_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
# 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)EegFun.channel_labels Method
channel_labels(dat::EegData) -> Vector{Symbol}Get EEG channel column names from the EEG data.
sourceEegFun.channel_labels Method
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
EegFun.channel_repairable Function
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.
EegFun.channel_repairable! Method
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 considerationlayout::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.
EegFun.channel_repairable! Method
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.
EegFun.channel_summary Method
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
# 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]))EegFun.channels Method
Predicate generators for channel selection. channels() selects all; variants filter by name, number, or range.
EegFun.check_channel_neighbors Method
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 nameslayout::Layout: Layout object containing neighbor information
Returns
Vector{Symbol}: Bad channels that have ALL good neighbors (can be repaired)
Examples
# Check which bad channels can be repaired
repairable_channels = check_channel_neighbors(bad_channels, layout)EegFun.check_files_exist Method
Return true if all paths in files exist, warning for each missing file.
EegFun.clear_neighbours! Method
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
clear_neighbours!(layout)EegFun.close_logging Method
close_logging(; is_global::Bool = false)Close the currently active log file and restore previous logger.
sourceEegFun.combine_artifact_components Method
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 dictionaryecg_comps::Vector{Int}: ECG components vectorline_noise_comps::Vector{Int}: Line noise components vectorchannel_noise_comps::Vector{Int}: Channel noise components vector
Returns
ArtifactComponents: Combined structure containing all artifact components
EegFun.common_channels Method
common_channels(dat1::EegData, dat2::EegData) -> Vector{Symbol}Find common channels between two EEG data objects.
Arguments
dat1::EegData: First EEG data objectdat2::EegData: Second EEG data object
Returns
Vector{Symbol}: Vector of common channel symbols
EegFun.compare_models Method
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:
- Static RDM:
Matrix{Float64}[n_conditions × n_conditions]
Single RDM used at all time points
Example: Reaction time RDM, similarity ratings RDM
- 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.timesExample: 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 RDMsStatic:
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)
# 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)
# 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)
# 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):
# 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"])EegFun.compare_rejections Method
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 repairrejection_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
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)EegFun.compute_noise_ceiling Method
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 participantAll 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
# 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.
sourceEegFun.compute_probability! Method
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 valuesdata::AbstractVector{Float64}: Input data vectorbins::Int: Number of bins for discretization
Returns
Vector{Float64}: Probability distribution (same as probaMap)
EegFun.condition_average Method
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}orVector{TimeFreqData}, one element per conditioncondition_groups: Groups of condition indices to average (e.g.,[[1, 2], [3, 4]])
Example
erps = average_epochs(epochs)
avg_waves = condition_average(erps, [[1, 2], [3, 4]])
# Batch
condition_average("erps_cleaned", [[1, 2], [3, 4]])EegFun.condition_combine Method
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 conditioncondition_groups::Vector{Vector{Int}}: Groups of condition indices to combine (e.g.,[[1, 2], [3, 4]])
Example
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]])EegFun.condition_difference Method
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
erps = average_epochs(epochs)
diff_waves = condition_difference(erps, [(1, 2)])
# Batch
condition_difference("erps_cleaned", [(1, 2)])EegFun.condition_info Method
Return (condition_number, condition_name) for any EegFunData object.
EegFun.condition_name Method
condition_name(dat::EegFunData) -> StringGet the condition name.
sourceEegFun.condition_number Method
condition_number(dat::EegFunData) -> Union{Int, String}Get the condition number or identifier.
sourceEegFun.condition_parse_epoch Method
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
config = TOML.parsefile("epoch_conditions.toml")
conditions = condition_parse_epoch(config)EegFun.consecutive Method
consecutive(f::Function, A::AbstractVector; step::Int=1) -> VectorApply function f to consecutive pairs of elements in vector A.
Arguments
f::Function: Function to apply to pairsA::AbstractVector: Input vectorstep::Int=1: Step size between pairs
Returns
Vector: Results of applying f to consecutive pairs
EegFun.correlation_matrix Method
correlation_matrix(dat::SingleDataFrameEeg; channel_selection::Function = channels())Calculate the correlation matrix between EEG channels.
Arguments
dat::SingleDataFrameEeg: The EEG data objectchannel_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
# 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]))EegFun.correlation_matrix_dual_selection Method
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 objectsample_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
# 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
)EegFun.correlation_matrix_eog Method
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 objecteog_cfg::EogConfig: The EOG configuration objectsample_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
# 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)EegFun.create_continuous_repair_info Method
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 usename::String: Optional name/identifier (default: "continuous_repair")
EegFun.create_custom_layout Method
create_custom_layout(positions::Vector{Tuple{Float64, Float64}}, channels::Vector{Symbol})Create a custom layout with specific positions for each channel.
sourceEegFun.create_eegfun_data Method
create_eegfun_data(dat::BrainVisionDataFormat.BrainVisionData, layout::Layout)::ContinuousDataCreates 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
# 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)EegFun.create_epoch_repair_info Function
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.
sourceEegFun.create_highpass_filter Method
create_highpass_filter(cutoff_freq::Real, sample_rate::Real;
filter_method::String = "iir",
order::Integer = 2,
transition_width::Real = 0.1)::FilterInfoCreate a digital filter object for the specified parameters.
Arguments
cutoff_freq: Cutoff frequency in Hzsample_rate: Sampling rate in Hzfilter_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
# 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)EegFun.create_layout Method
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_heightfor topo layouts)
EegFun.create_lowpass_filter Method
create_lowpass_filter(cutoff_freq::Real, sample_rate::Real;
filter_method::String = "iir",
order::Integer = 2,
transition_width::Real = 0.1)::FilterInfoCreate a digital filter object for the specified parameters.
Arguments
cutoff_freq: Cutoff frequency in Hzsample_rate: Sampling rate in Hzfilter_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
# 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)EegFun.create_model_rdms Method
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 dataVector{Vector{Float64}}: Feature vectors → RDM viacreate_rdm_from_vectorsMatrix{Float64}: Data matrix → RDM viacreate_rdm_from_matrixVector{Float64}: Reaction times (one value per condition)Vector{Int}: Categorical labelsSymmetric
Matrix{Float64}: Treated as similarity matrix or pre-computed RDM
Returns
model_rdms::Vector{Matrix{Float64}}: Vector of model RDMsmodel_names::Vector{String}: Vector of model names
EegFun.create_rdm_from_categorical Method
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
# Create RDM from category labels
categories = [1, 1, 2, 2, 3] # 5 conditions in 3 categories
rdm = create_rdm_from_categorical(categories)EegFun.create_rdm_from_distances Method
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
# 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)EegFun.create_rdm_from_matrix Method
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
# 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)EegFun.create_rdm_from_reaction_times Method
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
# 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.0EegFun.create_rdm_from_similarity_ratings Method
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
# 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)EegFun.create_rdm_from_timeseries Method
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
- Must match length of dimension 3 in
Arguments
temporal_data::Array{Float64, 3}: Temporal data [conditions × features × time]times::Vector{Float64}: Time points in seconds for temporal_datadissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)align_to::Union{Vector{Float64}, RsaData, Nothing}: Target timepoints to align toVector{Float64}: Time vector (e.g.,neural_rsa.times)RsaData: Automatically usesneural_rsa.timesnothing: 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_toprovided: matches length ofalign_totimepointsIf
align_toisnothing: matches length of inputtimes
EegFun.create_rdm_from_vectors Method
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 conditiondissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis)
Returns
rdm::Matrix{Float64}: RDM matrix [condition × condition]
Examples
# 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)EegFun.create_synthetic_epochs Method
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) -> EpochDataCreate 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 stringn_epochs: Number of epochs to generate
Keyword Arguments
n_timepoints::Int=500: Samples per epochchannels::Vector{Symbol}=[:Fz, :Cz, :Pz]: Channel labelssignal_strength::Float64=1.0: Amplitude of the Gaussian signalnoise_level::Float64=0.3: Amplitude of additive Gaussian noiseseed::Union{Int,Nothing}=nothing: Random seed for reproducibility
Example
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)EegFun.create_temporal_model_rdms Method
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 dataTuple{Array{Float64, 3}, Vector{Float64}}: (data, times) - data with its own timepointsArray{Float64, 3}: Direct temporal data (usestimesparameter)Vector{Vector{Vector{Float64}}}: Temporal vectors (seecreate_rdm_from_timeseries)
times::Vector{Float64}: Time points in seconds (used if model data doesn't provide its own)dissimilarity_measure::Symbol: Measure to usealign_to::Union{Vector{Float64}, RsaData, Nothing}: Target timepoints to align toVector{Float64}: Time vector (e.g.,neural_rsa.times)RsaData: Automatically usesneural_rsa.timesnothing: 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
EegFun.create_test_batch_erp_data Method
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.
sourceEegFun.create_test_continuous_data Method
create_test_continuous_data(; n::Int = 2000, fs::Int = 1000, n_channels::Int = 3, seed = nothing)Create synthetic ContinuousData for testing.
EegFun.create_test_continuous_data_empty_triggers Method
create_test_continuous_data_empty_triggers(; n_samples = 1000, fs = 1000, seed = nothing)Create ContinuousData with zero triggers.
EegFun.create_test_continuous_data_with_artifacts Method
create_test_continuous_data_with_artifacts(; n=1000, fs=1000)Create ContinuousData with two channels: one clean and one containing large-amplitude artifacts.
EegFun.create_test_continuous_data_with_triggers Method
create_test_continuous_data_with_triggers(; n = 1000, fs = 1000, seed = nothing)Create synthetic ContinuousData with trigger sequences.
EegFun.create_test_epoch_data Method
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.
EegFun.create_test_epoch_data_vector Method
create_test_epoch_data_vector(; conditions=1:2, n_epochs=10, n_channels=3, kwargs...)Create a Vector{EpochData} across multiple conditions.
EegFun.create_test_epoch_data_with_artifacts Method
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.
EegFun.create_test_epoch_data_with_rt Method
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.
EegFun.create_test_erp_data Method
create_test_erp_data(; participant = 1, condition = 1, fs = 1000, n_channels = 3, seed = nothing)Create synthetic ErpData for testing.
EegFun.create_test_layout Method
create_test_layout(; n_channels=4, layout_type=:grid)Create a test Layout. Supports :grid (auto-positioned) and :topo (fixed 6-channel topographic) types.
EegFun.create_test_lrp_data Method
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.
EegFun.create_test_summary_data Method
create_test_summary_data()Create a DataFrame of per-channel summary statistics for testing artifact detection displays.
EegFun.create_test_summary_data_with_epochs Method
create_test_summary_data_with_epochs()Create a DataFrame of per-channel, per-epoch summary statistics for testing epoch-wise artifact displays.
EegFun.create_test_tf_data Method
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 constructfilefieldfile::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
EegFun.create_test_tf_epoch_data Method
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 constructfilefieldfile::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
EegFun.create_work_arrays Method
Allocate and initialise WorkArrays for n_components and a given block size.
EegFun.decode_libsvm Method
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(orVector{DecodedData}for batch)
Example
decoded = decode_libsvm(epochs; channel_selection = channels(:Cz))
all_decoded = decode_libsvm(participant_epochs; n_iterations = 100)EegFun.detect_bad_epochs_automatic Method
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])::EpochRejectionInfoDetect bad epochs using statistical criteria and/or absolute voltage thresholds.
This function can use two types of criteria for epoch rejection:
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]).Absolute voltage criteria: Epochs with any channel exceeding the absolute voltage threshold (in μV) are rejected.
Arguments
dat::EpochData: Epoched EEG data to processz_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_criterionorabs_criterionmust be greater than 0
Examples
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
EegFun.detect_bad_epochs_interactive Method
detect_bad_epochs_interactive(dat::EpochData;
channel_selection::Function = channels(),
epochs_per_page::Int = 12,
grid_size::Tuple{Int,Int} = (3, 4))::EpochRejectionStateCreate 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 reviewchannel_selection::Function: Channels to display (default: all channels)artifact_info::Union{Nothing,EpochRejectionInfo}: Optional artifact detection info for bad channel filteringkwargs: 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 plotygrid::Bool=false: Whether to show y-axis griddims::Tuple{Int64, Int64}=(4, 6): Grid dimensions as (rows, cols) for epoch displaycolormap::Symbol=jet: Colormap for channel tracesyminorgrid::Bool=false: Whether to show y-axis minor gridgood_epoch_color::Symbol=green: Color for good epoch spinesylim::Nothing=nothing: Y-axis limits as (min, max) tuple, or nothing for automaticxlim::Nothing=nothing: X-axis limits as (min, max) tuple, or nothing for automaticxminorgrid::Bool=false: Whether to show x-axis minor gridspine_width::Int64=2: Width of axis spinesglobal_ylim::Bool=false: Whether to use global Y-axis limits across all epochsxgrid::Bool=false: Whether to show x-axis gridadd_xy_origin::Bool=true: Whether to add origin lines at x=0 and y=0theme_fontsize::Int64=20: Font size for themelinewidth::Int64=1: Line width for epoch tracesbad_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:
# 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
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
EegFun.detect_eog_signals! Method
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 objecteog_cfg::Dict: EOG configuration dictionary containing vEOG and hEOG settings
Example
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)EegFun.detect_eog_signals! Method
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 objecteog_cfg::EogConfig: EOG configuration object containing vEOG and hEOG settings
Example
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)EegFun.detrend Method
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
EegFun.duration Method
duration(dat::EegData) -> Float64Get the duration of the EEG data in seconds.
Arguments
dat::EegData: The EEG data object
Returns
Float64: Duration in seconds
EegFun.epoch_to_continuous Method
epoch_to_continuous(dat::MultiDataFrameEeg, epoch_idx::Int) -> ContinuousDataExtract a single epoch from multi-epoch EEG data and return it as ContinuousData.
Arguments
dat::MultiDataFrameEeg: The multi-DataFrame EEG dataepoch_idx::Int: Index of the epoch to extract (1-based)
Returns
ContinuousData: Single DataFrame containing only the specified epoch
Examples
# 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)EegFun.epochs_table Method
epochs_table(epochs::Vector{EpochData})Display a pretty table showing epoch information to console and return the DataFrame.
sourceEegFun.erp_measurements! Method
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
# 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]))EegFun.example_path Method
example_path(relative_path::String) -> StringReturn 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 theresources/directory
Returns
String: Absolute path to the resource file
Examples
# 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"))EegFun.export_bids Method
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"). Ifnothing, IDs are auto-extracted from filenames.dataset_name::String: Name fordataset_description.json(default:"").dataset_authors::Vector{String}: Authors fordataset_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
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,
)EegFun.extra_data Method
extra_data(eeg_data::EegData) -> DataFrameGet extra/derived columns (EOG, flags, etc.) from the EEG data.
sourceEegFun.extra_labels Method
extra_labels(dat::EegData) -> Vector{Symbol}Get extra/derived column names (EOG, flags, etc.) from the EEG data.
sourceEegFun.extract_epochs Method
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 datacondition::Int: Condition number to assign to epochsepoch_condition::EpochCondition: EpochCondition object defining trigger sequence and timing constraintsepoch_interval::Tuple{Real,Real}: Time window relative to reference point (start_time, end_time) in seconds
Returns
EpochData: The extracted epochs
Examples
# 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))EegFun.filter_info Method
filter_info(dat::AnalysisInfo) -> VectorGet filter information from the analysis info.
Arguments
dat::AnalysisInfo: The analysis info object
Returns
Vector: Filter information [hp_filter, lp_filter]
EegFun.find_file Method
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 inrecursive::Bool: Whether to search subdirectories recursively (default: true)extensions::Vector{String}: Optional file extensions to match (e.g., [".csv", ".toml"])
Returns
Stringornothing: Full path to the file if found,nothingotherwise
Examples
# 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)EegFun.find_times Method
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 pointstimes::Vector{Float64}: Actual time values at those indices
Example
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]EegFun.freq_spectrum Method
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. Seechannels()for options.Example:
channel_selection=channels(:Cz)for single channelExample:
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.
- Options:
max_freq::Union{Nothing,Real}=nothing: Maximum frequency to return in Hz.If
nothing, returns all frequencies up to NyquistIf 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
# 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)EegFun.generate_config_template Method
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")
EegFun.generate_pipeline_template Function
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 savedfunction_name::String: Name for the main preprocessing functionoptions::PipelineTemplateOptions: Configuration options for template generationnum_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
# 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)
)EegFun.generate_signal Method
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 generatetime_window: [start_time, end_time] in secondssample_rate: Sampling rate in Hzsignal_freqs: Vector of frequencies for each componentsignal_amps: Vector of amplitudes for each componentsignal_times: Vector of [start_time, end_time] for each componentnoise: Amplitude of random noise
Returns
times: Time vectorsignal: Matrix (samples × trials)
EegFun.get_all_ica_components Method
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
EegFun.get_eog_channels Method
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
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]EegFun.get_file_extension Method
get_file_extension(filename::String) -> StringGet the lowercase file extension including the dot (e.g., ".bdf", ".vhdr").
sourceEegFun.get_filter_characteristics Method
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 Hztransition_width::Real: Width of transition band in Hznpoints::Int: Number of frequency points for analysis (default: 1000)
Returns
A NamedTuple containing:
cutoff_freq_3db: Frequency at -3dB point(s) in Hzcutoff_freq_6db: Frequency at -6dB point(s) in Hzpassband_ripple: Maximum ripple in passband in dBstopband_atten: Mean attenuation in stopband in dBphase_delay: Mean phase delay in passband (samples)group_delay: Mean group delay in passband (samples)transition_band: Width of transition band in Hzfilter_type: Detected filter type ("hp" or "lp")
EegFun.get_neighbours_xy! Method
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 objectdistance_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
get_neighbours_xy!(layout, 0.4)EegFun.get_neighbours_xyz! Method
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 objectdistance_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.”
EegFun.get_package_version Method
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
EegFun.get_rejected Method
get_rejected(info::EpochRejectionInfo)::Vector{Rejection}Get the list of rejected channel-epoch pairs from the rejection info.
Examples
info = detect_bad_epochs_automatic(epochs)
rejected = get_rejected(info)EegFun.get_rejected_epochs Method
get_rejected_epochs(state::EpochRejectionState)::Vector{Int}Get indices of rejected epochs from the rejection state.
Examples
state = detect_bad_epochs_interactive(epochs)
# ... after review ...
rejected_indices = get_rejected_epochs(state)EegFun.get_selected_channels Method
Apply a channel predicate to data and return the matching column names (with optional metadata/extra).
sourceEegFun.get_selected_channels Method
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 objectchannel_selection::Function: Channel selection predicate function
Returns
Vector{Symbol}: Vector of selected channel labels
Examples
selected = get_selected_channels(layout, channels([:Fp1, :Fp2]))EegFun.get_selected_conditions Method
Apply a condition predicate and return matching dataset indices.
sourceEegFun.get_selected_epochs Method
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 epochsepoch_selection::Function: Function that returns boolean vector for epoch filtering
Returns
Vector{Int}: Indices of selected epochs
Examples
# 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]))EegFun.gfp Method
gfp(dat::ErpData;
channel_selection::Function = channels(),
normalize::Bool = false)::DataFrameCalculate 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 structurechannel_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
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.gfpNotes
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
EegFun.gfp_and_dissimilarity Method
gfp_and_dissimilarity(dat::ErpData;
channel_selection::Function = channels(),
normalize::Bool = false)::DataFrameCalculate 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 structurechannel_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
# 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)EegFun.global_dissimilarity Method
global_dissimilarity(dat::ErpData;
channel_selection::Function = channels(),
normalize::Bool = false)::DataFrameCalculate 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 structurechannel_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
# 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
EegFun.grand_average Method
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
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")EegFun.group_by_condition Method
group_by_condition(items::Vector{T}) where {T}Group items by their .condition field.
Arguments
items::Vector{T}: Items to group (must have a.conditionfield, e.g.ErpData,EpochData)
Returns
OrderedDict{Int, Vector{T}}: Items grouped by condition number (sorted)
EegFun.has_2d_coords Method
has_2d_coords(layout::Layout) -> BoolCheck if the layout has 2D Cartesian coordinates.
Arguments
layout::Layout: The layout object
Returns
Bool: True if x2 and y2 columns exist
EegFun.has_3d_coords Method
has_3d_coords(layout::Layout) -> BoolCheck if the layout has 3D Cartesian coordinates.
Arguments
layout::Layout: The layout object
Returns
Bool: True if x3, y3, and z3 columns exist
EegFun.has_channels Method
has_channels(dat::EegData, chans::Vector{Symbol}) -> BoolCheck if the EEG data contains all specified channels.
Arguments
dat::EegData: The EEG data objectchans::Vector{Symbol}: Vector of channel symbols to check
Returns
Bool: True if all channels are present
EegFun.has_neighbours Method
has_neighbours(layout::Layout) -> BoolCheck if the layout has calculated neighbor information.
Arguments
layout::Layout: The layout object
Returns
Bool: True if neighbors have been calculated and stored
Examples
if has_neighbours(layout)
# Use neighbor information
endEegFun.has_valid_coordinates Method
has_valid_coordinates(layout::Layout) -> BoolCheck 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
if !has_valid_coordinates(layout)
error("Cannot create topographic plot without a proper electrode layout")
endEegFun.head Method
head(dat::EegData; n=5)Display and return the first n rows of the EEG data.
Examples
head(erps[1]) # First 5 rows
head(erps[1], n=10) # First 10 rowsEegFun.highpass_filter Function
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.
EegFun.highpass_filter! Method
Apply highpass filter using settings from a FilterConfig section.
EegFun.highpass_filter! Method
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")
EegFun.highpass_filter Method
highpass_filter(file_pattern::String, cutoff_freq::Real; kwargs...)Batch highpass filter EEG/ERP data from JLD2 files.
sourceEegFun.identify_bad_channels Method
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
# 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)EegFun.identify_components Method
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 dataica::InfoIca: The ICA result objectsample_selection::Function: Sample selection function (default: samples())kwargs...: Additional keyword arguments passed to individual identification functions
Returns
ArtifactComponents: Combined structure containing all identified artifact componentsDict{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
# 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]EegFun.identify_ecg_components Method
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: Sincenum_valid_ibisis the number of valid intervals between peaks (which isnum_peaks - 1if all are valid), the check usesnum_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 fromdat.data. Defaults tosamples(). 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).
EegFun.identify_eog_components Method
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:
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).
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 fromdat.data. Defaults tosamples(). 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_zscoreor:vEOG_zscore_step1: Z-score of correlation with vertical EOG channel. Whentwo_step=true, only step1 z-scores are provided.:vEOG_spatial_corr: (Only whentwo_step=true) Maximum spatial correlation (topography) with any primary vEOG component.NaNfor primary vEOG components identified in step1.:vEOG_spatial_corr_z: Z-score of:vEOG_spatial_corr.:vEOG_temporal_corr: (Only whentwo_step=true) Maximum lagged temporal correlation with any primary vEOG component.NaNfor 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_zscoreor:hEOG_zscore_step1: Z-score of correlation with horizontal EOG channel. Whentwo_step=true, only step1 z-scores are provided.:hEOG_spatial_corr: (Only whentwo_step=true) Maximum spatial correlation (topography) with any primary hEOG component.NaNfor primary hEOG components identified in step1.:hEOG_spatial_corr_z: Z-score of:hEOG_spatial_corr.:hEOG_temporal_corr: (Only whentwo_step=true) Maximum lagged temporal correlation with any primary hEOG component.NaNfor primary hEOG components identified in step1.:hEOG_temporal_corr_z: Z-score of:hEOG_temporal_corr.
Examples
# 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))EegFun.identify_line_noise_components Method
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
EegFun.identify_spatial_kurtosis_components Method
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.
EegFun.infomax_extended_ica Method
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 preprocessedlayout::Layout: Layout information for channelsn_components::Int: Number of ICA components to extractparams::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.
sourceEegFun.is_equal_rejection Method
Check whether two Rejection objects refer to the same channel and epoch.
EegFun.is_extreme_value! Method
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
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 conditionsEegFun.is_extreme_value Method
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
extreme_mask = is_extreme_value(dat, 100)
results = is_extreme_value(dat, 100, mode = :separate)EegFun.is_step_value! Method
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
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 conditionsEegFun.is_step_value Method
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 objectthreshold::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) orDataFrame(separate mode)
Examples
# 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]))EegFun.jackknife_average Method
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
EegFun.libsvm_classifier Method
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
y_pred = libsvm_classifier(X_train, y_train, X_test, cost=1.0)EegFun.log_epochs_table Method
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.
sourceEegFun.log_pretty_table Method
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 loglog_level::Symbol: Log level (:debug, :info, :warn, :error) (default: :info)kwargs...: Additional arguments passed to pretty_table
Examples
# 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")EegFun.lowpass_filter Function
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.
EegFun.lowpass_filter! Method
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")
EegFun.lowpass_filter Method
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
EegFun.lrp Method
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
ErpDatawith LRP values.Vector{ErpData}form: computes LRP for multiple condition pairs at once.file_patternform: 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. Defaultchannels()auto-detects all odd/even pairs (C3/C4, C1/C2, Fp1/Fp2, …)
Examples
# 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")EegFun.mark_epoch_intervals! Method
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_interestColumn form: marks windows around each
truesample in a boolean column (e.g.,:is_vEOG)Multi-column form: unions
truesamples across multiple boolean columns and marks windows around all of themEpochCondition form: marks windows around trigger sequences defined by
epoch_conditions
Examples
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])EegFun.meta_data Method
meta_data(eeg_data::EegData) -> DataFrameGet meta data columns from the EEG data.
sourceEegFun.meta_labels Method
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
EegFun.mirror Function
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 aVectorof eitherside::pre,:post, or:both(default::both)
EegFun.mirror! Function
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) -> NothingMirror 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 aVectorof eitherside::pre,:post, or:both(default::both)
Notes
Mirrored data will be approximately 3× longer with
:bothThe
sideargument tounmirror!must match
Examples
mirror!(epochs, :both)
filter!(epochs, 1.0, filter_type = "hp")
unmirror!(epochs, :both)EegFun.n_channels Method
n_channels(dat::EegData) -> IntGet the number of channels in the EEG data.
Arguments
dat::EegData: The EEG data object
Returns
Int: Number of channels
EegFun.n_epochs Method
n_epochs(dat::EegData) -> IntGet the number of epochs in the EEG data.
sourceEegFun.n_extreme_value Method
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 objectthreshold::Real: Threshold for extreme value detectionchannel_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
# 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)EegFun.n_layout Method
n_layout(layout::Layout) -> IntGet the number of channels in the layout.
Arguments
layout::Layout: The layout object
Returns
Int: Number of channels in the layout
EegFun.n_samples Method
n_samples(dat::EegData) -> IntGet the number of samples in the EEG data.
Arguments
dat::EegData: The EEG data object
Returns
Int: Number of samples
EegFun.n_step_value Method
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 objectthreshold::Real: Threshold for step value detectionchannel_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 (:separateor:combined, default::combined)
Returns
Int(combined mode): Total number of samples with step values across all selected channelsDataFrame(separate mode): DataFrame with step value counts for each channel
Examples
# 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]))EegFun.n_values Method
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.
EegFun.neighbour_criterion Method
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
criterion = neighbour_criterion(layout)EegFun.neighbours Method
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
neighbor_dict = neighbours(layout)EegFun.normalize_rdm Method
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
# 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
EegFun.participants_not Method
Predicate generators that exclude the specified participant IDs.
sourceEegFun.partition_channels_by_eog_correlation Method
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 nameseog_correlation_df::DataFrame: EOG correlation matrix DataFrameeog_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)
EegFun.permutation_test Method
permutation_test(prepared::StatisticalData; kwargs...)Perform cluster-based permutation test on prepared ERP data.
Arguments
prepared::StatisticalData: Prepared data fromprepare_statsn_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_commoncluster_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:rightshow_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.
EegFun.polar_to_cartesian_xy! Method
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 whenpreserve_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:incand:azicolumns are missing or contain non-numeric values.
Modifies
The input
layoutis modified in place to include new columnsx2andy2, 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
layoutdirectly.
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.
EegFun.polar_to_cartesian_xyz! Method
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:incand:azicolumns are missing or contain non-numeric values.
Modifies
The input
layoutis modified in place to include new columnsx3,y3, andz3, 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
layoutdirectly.
EegFun.positions_2D Method
positions_2D(layout::Layout) -> DataFrameGet 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
EegFun.positions_3D Method
positions_3D(layout::Layout) -> DataFrameGet 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
EegFun.positions_polar Method
positions_polar(layout::Layout) -> DataFrameGet polar coordinate data from the layout.
Arguments
layout::Layout: The layout object
Returns
DataFrame: DataFrame containing polar coordinate columns (inc, azi)
EegFun.prepare_decoding Method
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
# 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))EegFun.prepare_stats Method
Load ERP/TF files by pattern and call the typed prepare_stats method.
EegFun.prepare_stats Method
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/participantsdesign::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
EegFun.prepare_stats Method
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/participantsdesign::Symbol: Design type -:pairedor:independentcondition_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)) ornothingfor all frequenciesinterval_selection::Interval: Time interval as tuple (e.g.,(0.0, 1.0)) ornothingfor all time pointsbaseline_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
# 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)EegFun.preprocess Method
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 formatbase_dir::Union{String,Nothing}: Base directory for resolving relative paths in TOML file. Ifnothing(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_dirIf
base_diris not provided, it defaults to the directory containing the config fileAbsolute 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
EegFun.preprocess_v2 Method
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 formatbase_dir::Union{String,Nothing}: Base directory for resolving relative paths in TOML file. Ifnothing(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_dirIf
base_diris not provided, it defaults to the directory containing the config fileAbsolute 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
EegFun.print_config Function
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
# Print to console in TOML format
print_config(config)
# Write to TOML file
print_config(config, "config_output.toml")EegFun.print_filter_characteristics Method
print_filter_characteristics(filter_info::FilterInfo; npoints::Int = 1000)Print a formatted summary of filter characteristics.
sourceEegFun.print_layout_neighbours Method
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/xyzfilename::String: Path to the output TOML file
Example
layout = read_layout("./layouts/biosemi64.csv")
get_neighbours_xy!(layout, 0.4)
# Write to TOML file
print_layout_neighbours(layout.neighbours, "neighbours.toml")EegFun.read_all_data Function
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:
read_data("./derivatives/erps/example1_erps_good.jld2") # single file
read_all_data("./derivatives/erps/erps_good") # all matching filesArguments
path_pattern: A file-system path whosedirnameis the search directory and whosebasenameis matched (viacontains) against.jld2filenames.participant_selection: Predicate function (default:participants()— all).
EegFun.read_config Method
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
EegFun.read_data Method
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
EegFun.read_eeglab Method
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 filepreserve_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
# Without ICA
eeg = read_eeglab("data.set")
# With ICA (automatically detected)
eeg, ica = read_eeglab("data_with_ica.set")EegFun.read_fieldtrip Method
read_fieldtrip(filepath::String, layout::Layout) → ContinuousData/EpochData/ErpDataLoad FieldTrip .mat file and convert to EegFun data structure.
Arguments
filepath::String: Path to FieldTrip .mat filelayout::Layout: Channel layout (FieldTrip doesn't store layout in data files)
Returns
ContinuousData: For single-trial continuous dataEpochData: For multi-trial epoched dataErpData: For averaged ERP data
Example
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)EegFun.read_fieldtrip_csv Method
read_fieldtrip_csv(data_dir::String;
file::String="fieldtrip_data",
condition::Int=1,
condition_name::String="fieldtrip",
fsample::Union{Int,Nothing}=nothing) -> EpochDataLoad 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 containingepochs_data.csv- The CSV has columns: [trial, time, channel1, channel2, ...]
Keyword Arguments
file::String="fieldtrip_data": Source filename for EpochDatacondition::Int=1: Condition numbercondition_name::String="fieldtrip": Condition namefsample::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
epochs = read_fieldtrip_csv("/path/to/csv/files", fsample=256)EegFun.read_layout Method
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.
EegFun.read_raw_data Method
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.
EegFun.realign! Method
realign!(dat::EpochData, realignment_column::Symbol)::NothingRealign 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 realignrealignment_column::Symbol: Name of the column containing realignment times (e.g.,:rt,:response_time)
Effects
Modifies the input data in-place
Updates the
:timecolumn in each epochCrops all epochs to a common time interval
The realignment column values should be relative to the current time zero
Examples
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
EegFun.realign Method
realign(dat::EpochData, realignment_column::Symbol)::EpochDataNon-mutating version of realign!. Returns a new EpochData object with realigned epochs.
sourceEegFun.reference Method
reference(dat::EegData) -> SymbolGet the reference information from the EEG data.
Arguments
dat::EegData: The EEG data object
Returns
Symbol: Reference information
EegFun.reject_epochs! Method
reject_epochs!(dat::EpochData, info::EpochRejectionInfo) -> EpochDataRemove epochs identified in info from dat in-place.
Arguments
dat::EpochData: Epoched EEG data to modifyinfo::EpochRejectionInfo: Rejection information fromdetect_bad_epochs_automaticetc.
Examples
info = detect_bad_epochs_automatic(epochs, z_criterion = 2.0)
reject_epochs!(epochs, info)EegFun.reject_epochs Method
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
# 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])EegFun.remove_ica_components! Method
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
# 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]))EegFun.rename_channel! Method
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 modifyrename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names
Returns
nothing(modifies the data in place)
Examples
# 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)
EegFun.rename_channel! Method
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 modifyrename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names
Returns
nothing(modifies the layout in place)
Examples
# 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
EegFun.rename_channel Method
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 renamerename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names
Returns
EegData: A new EEG data object with renamed channels
Examples
# 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)
EegFun.rename_channel Method
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 renamerename_dict::Dict{Symbol, Symbol}: Dictionary mapping old channel names to new names
Returns
Layout: A new layout object with renamed channels
Examples
# 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
EegFun.repair_artifacts! Method
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_artifactsmethod::Symbol: Repair method to use
Available Methods
:neighbor_interpolation- Weighted neighbor interpolation (default). Usesdat.layout.neighboursfor 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)
sourceEegFun.repair_artifacts Method
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_artifactsmethod::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
# 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)EegFun.repair_artifacts_neighbor! Method
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 withrepairedalready 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
EegFun.repair_artifacts_spherical_spline! Method
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_artifactsm::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)
EegFun.repair_channels Function
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.
EegFun.repair_channels! Method
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
# 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)EegFun.repair_channels! Method
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 withrepairedchannels already populatedmethod::Symbol: Repair method to use (default: :neighbor_interpolation)
Returns
Nothing: All modifications are in-place
Notes
repair_info.repairedshould be populated bychannel_repairable!before calling this function
EegFun.repair_channels_neighbor! Method
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 withrepairedchannels already populated
Returns
Nothing: All modifications are in-place
See also
channel_repairable!: Analyze which channels can be repaired before calling this function
EegFun.repair_channels_per_epoch! Method
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 informationthreshold::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
# Repair channels per epoch
channels_repaired, epochs_repaired = repair_channels_per_epoch!(
epochs_cleaned,
layout,
100.0,
:is_artifact_value_100
)EegFun.repair_channels_spherical! Method
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 withrepairedchannels already populatedm::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
EegFun.rereference Function
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.
EegFun.rereference Method
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
# 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])EegFun.resample! Method
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
factormust be a positive integer;sample_ratemust be divisible byfactorAll DataFrame columns (triggers, time, metadata) are preserved
Trigger positions are scaled to the new sample grid for
ContinuousData
Examples
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))EegFun.resample_temporal_data Method
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 secondstarget_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
# 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]EegFun.restore_ica_components! Method
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
# 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]))EegFun.rsa Method
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 participantchannel_selection::Function=channels(): Channel selection predicate. Seechannels()for options.Example:
channel_selection=channels([:Fz, :Cz, :Pz])for specific channelsExample:
channel_selection=channels(:Cz)for single channelExample:
channel_selection=channels()for all channels (default)
sample_selection::Function=samples(): Sample selection predicate. Seesamples()for options.Example:
sample_selection=samples((-0.2, 0.8))for time interval from -0.2 to 0.8 secondsExample:
sample_selection=samples()for all time points (default)
dissimilarity_measure::Symbol: Measure to use (:correlation, :spearman, :euclidean, :mahalanobis):correlationor: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
# 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)EegFun.rsa_crossvalidated Method
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_iterationsand average resultsUse 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_foldsgroupsFor 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 conditionchannel_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
# 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
EegFun.run_ica Method
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
# 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)EegFun.sample_rate Method
sample_rate(dat::EegData) -> IntGet the sample rate of the EEG data.
Arguments
dat::EegData: The EEG data object
Returns
Int: Sample rate in Hz
EegFun.search_sequence Method
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,:anywildcard, andUnitRangeelements.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
# 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]]EegFun.setup_logging Method
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).
EegFun.show_parameter_info Method
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")
EegFun.signal_example_composition Method
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
| Row | Plot | Description |
|---|---|---|
| 1–3 | Signal 1–3 | Individual sine waves. Y-axes are linked — amplitude changes are visually comparable across all three. |
| 4 | Noise | Additive Gaussian noise. |
| 5 | Combined Signal | Sum of all three signals plus noise. A red overlay shows the filtered version when a filter is active. |
| 6 | Frequency Domain | Power 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
| Control | Range | Description |
|---|---|---|
| Freq (×3) | 0–80 Hz | Frequency of each sine wave |
| Amp (×3) | 0–10 | Amplitude of each sine wave |
| Phase (×3) | −π to π | Phase offset of each sine wave |
| Noise | 0–2 | Standard deviation of additive Gaussian noise |
| ☐ LP Filter | 0–100 Hz | Low-pass filter cutoff; enable with checkbox |
| ☐ HP Filter | 0–2 Hz | High-pass filter cutoff; enable with checkbox |
Examples
using EegFun
EegFun.signal_example_composition()Returns
fig::Figure: The Makie figure object containing the interactive GUIaxes::Vector{Axis}: The six axis objects (rows 1–6)
EegFun.signal_example_convolution Method
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
| Row | Plot | Description |
|---|---|---|
| 1 | Signal + Kernel | The input signal with the kernel overlaid at the current position. The shaded region shows where the kernel overlaps the signal. |
| 2 | Output | The 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
| Control | Applies to | Range | Description |
|---|---|---|---|
| Kernel Position | All | 0–2 s | Scrub the kernel across the signal — output builds up as you drag right |
| Kernel Width | Gaussian / Boxcar | 10–2000 ms | Total span of the kernel (label shows "→ eff. X ms" in Wavelet mode) |
| Cycles | Wavelet | 1–10 | Number of oscillations in the wavelet kernel |
| Wavelet Freq | Wavelet | 1–40 Hz | Target frequency to detect |
| Signal Freq | All | 1–20 Hz | Frequency of the input sine wave |
| Noise | All | 0–1 | Additive Gaussian noise standard deviation |
| Kernel type | All | — | Gaussian / Boxcar / Wavelet (Morlet) |
Teaching Notes
Convolution computes, at each time point t, the weighted sum of the signal under the kernel:
(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
using EegFun
EegFun.signal_example_convolution()Returns
fig::Figure: The Makie figure objectaxes::Tuple:(ax_sig, ax_out)— the two axis objects
EegFun.signal_example_decoding Method
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
| Row | Plot | Description |
|---|---|---|
| 1 | ERP Waveforms | Mean waveform per condition (blue vs red) with ±1 SE shading |
| 2 | Decoding Accuracy | Time-resolved classification accuracy with 50% chance line and SE band |
Controls
| Control | Range | Description |
|---|---|---|
| Signal Strength | 0–3 | Amplitude of the condition difference (larger = easier to decode) |
| Noise Level | 0.1–5 | Background noise amplitude |
| N Trials | 10–200 | Trials per condition — more trials = better estimates |
| N Channels | 1–10 | Number of EEG channels (features for the classifier) |
| Effect Onset | −0.2–0.8 s | When the condition difference begins |
| Effect Duration | 0.05–0.8 s | How long the condition difference lasts |
| [Decode!] | button | Runs 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
using EegFun
EegFun.signal_example_decoding()Returns
fig::Figure: The Makie figure objectaxes::Tuple:(ax_erp, ax_acc)— the two axis objects
EegFun.signal_example_dotproduct Method
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
| Row | Plot | Description |
|---|---|---|
| 1 | Signal + Test sinusoid(s) | The target signal (blue), sine test (red), and cosine test (green, when Complex is on). |
| 2 | Sine product | Signal × sine test sinusoid, with dot product value. |
| 3 | Cosine product | Signal × cosine test sinusoid (visible only when Complex is on). |
Controls
| Control | Range | Description |
|---|---|---|
| Signal Freq | 1–40 Hz | Frequency of the signal sine wave |
| Amplitude | 0.1–2 | Signal amplitude — dot product scales proportionally |
| Phase (°) | 0–355° | Signal phase offset |
| Test Freq | 0.5–60 Hz | Frequency of the test sinusoid (the DFT "probe") |
| Noise | 0–1 | Noise standard deviation |
| Complex | toggle | Switch 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:
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:
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:
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
using EegFun
EegFun.signal_example_dotproduct()Returns
fig::Figure: The Makie figure objectaxes::Tuple:(ax_sig, ax_prod_sin, ax_prod_cos)— the three axis objects
EegFun.signal_example_ica Method
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
| Column | Row 1 | Row 2 | Row 3 |
|---|---|---|---|
| Left | True sources | EEG recordings (mixed) | Recovered components |
| Middle | Sources S1 vs S2 | Mixed M1 vs M2 | Recovered C1 vs C2 |
| Right | Sources S1 vs S3 | Mixed M1 vs M3 | Recovered 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
| Control | Range | Description |
|---|---|---|
| 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 Freq | 1 – 20 Hz | Frequency of the oscillatory source (S1) |
| Noise | 0 – 1 | Additive Gaussian noise on all sources |
| Auto-ICA button | — | Runs 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:
Set all mixing angles > 0 and watch the scatters collapse into blobs.
Drag unmixing sliders manually to match the mixing angles — sources reappear.
Reset unmixing angles to 0, then click Auto-ICA — Infomax recovers the sources.
Add Noise — Infomax still works because S2 and S3 are highly non-Gaussian.
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
using EegFun
EegFun.signal_example_ica()Returns
fig::Figure: The Makie figure object
EegFun.signal_example_mixing Method
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: Brain | Electrode 1 (mix) | Recovered 1 |
| Source 2: Blink | Electrode 2 (mix) | Recovered 2 |
Below the plots: mixing matrix, unmixing matrix, and match scores.
Controls
| Control | Range | Description |
|---|---|---|
| Mix Amount | 0 – 1 | How much sources bleed across electrodes |
| Brain Freq | 4 – 20 Hz | Frequency of the oscillatory source |
| Blink Size | 0.5 – 5 | Amplitude of blink artifacts |
| Noise | 0 – 1 | Additive sensor noise |
| Subtract E2→E1 | 0 – 1 | Manual: remove E2 contribution from E1 |
| Subtract E1→E2 | 0 – 1 | Manual: remove E1 contribution from E2 |
| Unmix! | button | Run Infomax ICA to find optimal weights |
| Reset | button | Clear 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
using EegFun
EegFun.signal_example_mixing()Returns
fig::Figure: The Makie figure object
EegFun.signal_example_sampling Method
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
| Control | Range | Description |
|---|---|---|
| Duration | 1–10 s | Length of the displayed signal |
| Frequency | 1–100 Hz | Frequency of the sine wave |
| Phase Angle | -π to π | Phase offset of the sine wave |
| Sample Rate | 1–300 Hz | Number of samples per second |
| Linear | checkbox | Show linear (join-the-dots) reconstruction (red) |
| Sinc | checkbox | Show 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:
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
using EegFun
EegFun.signal_example_sampling()Returns
fig::Figure: The Makie figure object containing the interactive GUIax::Axis: The axis object containing the plots
EegFun.signal_example_spectrum Method
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
| Row | Plot | Description |
|---|---|---|
| 1 | Time Domain | The composed signal for the full epoch duration. |
| 2 | Power Spectrum | One-sided FFT power spectrum. Dashed vertical lines mark the true signal frequencies; the orange band marks one frequency-resolution bin (Δf = 1/T). |
Controls
| Control | Range | Description |
|---|---|---|
| Freq 1 | 1–60 Hz | Frequency of the first sine component |
| Amp 1 | 0–2 | Amplitude of the first sine component |
| Freq 2 | 1–60 Hz | Frequency of the second sine component |
| Amp 2 | 0–2 | Amplitude of the second sine component (0 = off) |
| Noise | 0–2 | Standard deviation of additive Gaussian noise |
| Epoch | 1–10 s | Epoch length — controls frequency resolution (Δf = 1/T) |
| Log scale | toggle | Log 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
using EegFun
EegFun.signal_example_spectrum()Returns
fig::Figure: The Makie figure objectaxes::Tuple:(ax_time, ax_freq)— the two axis objects
EegFun.signal_example_tf Method
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
| Method | Window shape | Parameter | Key property |
|---|---|---|---|
| Morlet Wavelet | Gaussian | Cycles | Adaptive: window length = cycles / frequency |
| STFT (Hanning) | Hanning | Window (ms) | Fixed: same window length for all frequencies |
| Multitaper (DPSS) | Slepian tapers | Cycles | Adaptive: 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
| Row | Plot | Description |
|---|---|---|
| 1 | Time Domain | Synthetic signal averaged across trials. Dashed line = t = 0. |
| 2 | Time-Frequency Map | Power heatmap computed with the selected method. |
Controls
| Control | Range | Description |
|---|---|---|
| Method | Morlet / STFT / Multitaper | TF decomposition method |
| Cycles / Window | 3–12 | Cycles (Morlet/Multitaper) or window = N/30 Hz (STFT) |
| Noise | 0.5–5 | Amplitude of additive white noise per trial |
| Baseline | None / dB / % Change / … | Baseline correction type (uses tf_baseline) |
| Window | −1.0–2.0 s | Baseline time window (IntervalSlider) |
| Freq scale | Linear / Log | Toggle log₁₀ y-axis on the TF map |
| ☐ Comp 1/2/3 | on/off | Enable/disable each component |
| Freq (×3) | 1–40 Hz | Component frequency |
| Amp (×3) | 0–5 | Component amplitude |
| Interval (×3) | −1.0–2.0 s | Component 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
using EegFun
EegFun.signal_example_tf()Returns
fig::Figure: The Makie figure objectaxes::NamedTuple: Named tuple with:timeand:tfaxes
EegFun.signal_to_data Method
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.
EegFun.simulate_erp Method
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
| Control | Range | Description |
|---|---|---|
| Number of Trials | 1–500 | How many simulated trials to average |
| Noise Amplitude | 0–20 | Amplitude of background 1/f EEG noise |
| Active (toggle, ×5) | on/off | Enable/disable each component |
| Freq (×5) | 0.1–5.0 Hz | Cosine frequency of the component |
| Amp (×5) | −10 to 10 μV | Peak amplitude of the component |
| Latency (×5) | 0–1000 ms | Peak latency of the component |
| Amp Jitter (×5) | 0–20 | Trial-to-trial amplitude variability |
| Lat Jitter (×5) | 0–50 ms | Trial-to-trial latency variability |
Examples
using EegFun
EegFun.simulate_erp()Returns
fig::Figure: The Makie figure objectax::Axis: The main plot axis
EegFun.subset Method
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
# Select specific channels and time window
sub = subset(erp, channel_selection = channels(:Fz, :Cz), interval_selection = times(-0.2, 0.5))EegFun.subset_bad_data Method
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 filesthreshold::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
# 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")EegFun.subset_layout! Method
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 subsetchannel_selection::Function: Channel selection predicate function (default: all channels)
Returns
nothing(modifies the layout in place)
EegFun.subset_layout Method
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 subsetchannel_selection::Function: Channel selection predicate function (default: all channels)
Returns
Layout: A new subset layout object
EegFun.summarize_electrode_repairs Method
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 namen_participants::Int: Number of participants where this electrode was repaired at the continuous level
EegFun.summarize_ica_components Method
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 removedvEOG::Int: Number of vEOG componentshEOG::Int: Number of hEOG componentsECG::Int: Number of ECG componentsline_noise::Int: Number of line noise componentschannel_noise::Int: Number of channel noise components
DataFrame: Average summary with columns:component_type::String: Type of componentavg_per_participant::Float64: Average number of components per participant
EegFun.tail Method
tail(dat::EegData; n=5)Display and return the last n rows of the EEG data.
Examples
tail(erps[1]) # Last 5 rows
tail(erps[1], n=10) # Last 10 rowsEegFun.test_against_chance Method
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
# Test across participants
decoded_list = [decoded_p1, decoded_p2, decoded_p3]
stats = test_against_chance(decoded_list, alpha=0.05, correction_method=:bonferroni)EegFun.test_against_chance_cluster Method
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:maxshow_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
# 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)EegFun.tf_baseline! Method
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 correctbaseline_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
tf_baseline!(tf_data, (-0.3, 0.1); method=:db)EegFun.tf_baseline Method
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
tf_baseline("tf_epochs_wavelet", (-0.3, 0.0); method=:db)EegFun.tf_morlet Method
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
# 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")EegFun.tf_multitaper Method
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. Seechannels()for options.Example:
channel_selection=channels(:Cz)for single channelExample:
channel_selection=channels([:Cz, :Pz])for multiple channels
interval_selection::Interval=samples(): Sample selection predicate. Seesamples()for options.Example:
sample_selection=samples((-0.5, 2.0))for time interval from -0.5 to 2.0 secondsExample:
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:40orfrequencies=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=5uses 5 cycles for all frequenciesLower frequencies will have longer windows, higher frequencies will have shorter windows
frequency_smoothing::Union{Nothing,Real}=nothing: Frequency smoothing parameterIf
nothing, usesfrequency_smoothing = 0.4 * frequencyIf a number, uses that value multiplied by frequency:
tapsmofrq = frequency_smoothing * frequencyExample:
frequency_smoothing=0.4Controls time-bandwidth product:
NW = tapsmofrq * window_length / 2Number 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.01creates 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: Iftrue, returnsTimeFreqEpochDatawith individual trials preserved.- If
false(default), returnsTimeFreqDatawith trials averaged.
- If
filter_edges::Bool=true: Iftrue(default), filters out edge regions where the window extends beyond the data
Returns
TimeFreqData(ifreturn_trials=false): Time-frequency data with trials averagedTimeFreqEpochData(ifreturn_trials=true): Time-frequency data with individual trials preserved
Examples
# 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)EegFun.tf_stft Method
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. Seechannels()for options.Example:
channel_selection=channels(:Cz)for single channelExample:
channel_selection=channels([:Cz, :Pz])for multiple channels
interval_selection::Interval=samples(): Sample selection predicate. Seesamples()for options.Example:
sample_selection=samples((-0.5, 2.0))for time interval from -0.5 to 2.0 secondsExample:
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:40orfrequencies=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.3uses a fixed 0.3 second window for all frequenciesExactly one of
window_lengthorcyclesmust be specified.
cycles::Union{Nothing,Real}=nothing: Number of cycles per frequency (adaptive window).Example:
cycles=7uses 7 cycles per frequency (window length = 7/frequency)Exactly one of
window_lengthorcyclesmust 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.01creates 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: Iftrue, returnsTimeFreqEpochDatawith individual trials preserved.- If
false(default), returnsTimeFreqDatawith trials averaged.
- If
filter_edges::Bool=true: Iftrue(default), filters out edge regions where the interval extends beyond the data
Returns
TimeFreqData(ifreturn_trials=false): Time-frequency data with trials averagedTimeFreqEpochData(ifreturn_trials=true): Time-frequency data with individual trials preserved
Examples
# 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)EegFun.time_vector Method
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 objectdf::DataFrame: A DataFrame with a:timecolumn
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
EegFun.times Method
times()Select all time points (default for interval parameters). Returns nothing which means no time filtering.
EegFun.to_data_frame Method
to_data_frame(dat::MultiDataFrameEeg) -> DataFrameConvert a multi-dataframe EEG object to a single DataFrame by concatenating all epochs.
sourceEegFun.trigger_count Method
trigger_count(dat::ContinuousData)::TriggerInfo
trigger_count(df::DataFrame)::TriggerInfoCount 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:triggercolumn.
Returns
A TriggerInfo object containing trigger counts. Display the object to see a formatted table.
Examples
# 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.countEegFun.unique_channels Method
Return unique channels from an EpochRejectionInfo or vector thereof.
EegFun.unique_channels Method
Return the distinct channel symbols across a vector of rejections.
sourceEegFun.unique_rejections Method
Return unique rejections for each EpochRejectionInfo in a vector.
EegFun.unmirror Function
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 aVectorof eitherside::pre,:post, or:both(default::both) — must match the originalmirror!call
EegFun.unmirror! Function
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) -> NothingRemove 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 aVectorof eitherside::pre,:post, or:both(default::both)
Examples
mirror!(epochs, :both)
filter!(epochs, 1.0, filter_type = "hp")
unmirror!(epochs, :both) # restores original lengthEegFun.validate_layout Method
validate_layout(layout::Layout) -> LayoutValidate 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
layout = read_layout("biosemi64.csv")
validated_layout = validate_layout(layout)EegFun.version_info Method
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
ver_info = version_info()
@info "Starting analysis" ver_info...
@info "Running EegFun $(ver_info["EegFun_version"]) on Julia $(ver_info["julia_version"])"EegFun.viewer Method
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
EegFun.@add_nonmutating Macro
@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!
sourceEegFun.@log_call Macro
@log_call func_nameMacro 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.
EegFun.@minimal_error Macro
@minimal_error(msg)Throws an EegFunError with a clean user-facing error message. Also logs the error via @error for diagnostics.
EegFun.@minimal_stacktrace Macro
@minimal_stacktrace(msg, e, max_lines=5)Log an error with a limited stacktrace to avoid huge log files.
Arguments
msg::String: Error messagee: The exception objectmax_lines::Int: Maximum number of stacktrace lines to include (default: 5)
Example
catch e
@minimal_stacktrace "Error processing file" e
@minimal_stacktrace "Error processing file" e 10 # with custom max_lines
endEegFun.@minimal_warning Macro
@minimal_warning(msg)Displays a warning message without showing module/file/line metadata.
source