From b6871ccb8444468881ccdc93b09134f617654a11 Mon Sep 17 00:00:00 2001
From: Dung Truong
Date: Tue, 14 Jan 2025 13:14:14 -0500
Subject: [PATCH] update preload to support new single .set EEGLAB format
---
mne/io/eeglab/_eeglab.py | 26 +++++++++++++++++++++-----
mne/io/eeglab/eeglab.py | 29 ++++++++++-------------------
2 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/mne/io/eeglab/_eeglab.py b/mne/io/eeglab/_eeglab.py
index 28df469fada..d32f47446a1 100644
--- a/mne/io/eeglab/_eeglab.py
+++ b/mne/io/eeglab/_eeglab.py
@@ -9,7 +9,7 @@
except ImportError: # scipy < 1.8
from scipy.io.matlab.mio5 import MatlabFunction
from scipy.io.matlab.mio5_params import MatlabOpaque
-from scipy.io import loadmat
+from scipy.io import loadmat, whosmat
from ...utils import _import_pymatreader_funcs
@@ -71,13 +71,29 @@ def _check_for_scipy_mat_struct(data): # taken from pymatreader.utils
return data
-def _readmat(fname, uint16_codec=None):
+def _readmat(fname, uint16_codec=None, preload=False):
try:
read_mat = _import_pymatreader_funcs("EEGLAB I/O")
except RuntimeError: # pymatreader not installed
- eeg = loadmat(
- fname, squeeze_me=True, mat_dtype=False, uint16_codec=uint16_codec
- )
+ if preload:
+ eeg = loadmat(
+ fname, squeeze_me=True, mat_dtype=False, uint16_codec=uint16_codec
+ )
+ else:
+ info_fields = ['setname', 'filename', 'filepath', 'subject', 'group', 'condition', 'session', 'comments', 'nbchan', 'trials', 'pnts', 'srate', 'xmin', 'xmax', 'times', 'icaact', 'icawinv', 'icasphere', 'icaweights', 'icachansind', 'chanlocs', 'urchanlocs', 'chaninfo', 'ref', 'event', 'urevent', 'eventdescription', 'epoch', 'epochdescription', 'reject', 'stats', 'specdata', 'specicaact', 'splinefile', 'icasplinefile', 'dipfit', 'history', 'saved', 'etc']
+ eeg = loadmat(
+ fname, variable_names=info_fields, squeeze_me=True, mat_dtype=False, uint16_codec=uint16_codec
+ )
+ variables = whosmat(str(fname))
+ for var in variables:
+ if var[0] == 'data':
+ numeric_types = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'single', 'double']
+ if var[2] in numeric_types:
+ # in preload=False mode and data is in .set file
+ eeg['data'] = str(fname)
+ else:
+ eeg['data'] = loadmat(fname, variable_names=['data'], squeeze_me=True, mat_dtype=False, uint16_codec=uint16_codec)
+ break
return _check_for_scipy_mat_struct(eeg)
else:
return read_mat(fname, uint16_codec=uint16_codec)
diff --git a/mne/io/eeglab/eeglab.py b/mne/io/eeglab/eeglab.py
index 3aa611b4e28..9a883493a16 100644
--- a/mne/io/eeglab/eeglab.py
+++ b/mne/io/eeglab/eeglab.py
@@ -39,9 +39,9 @@
def _check_eeglab_fname(fname, dataname):
"""Check whether the filename is valid.
- Check if the file extension is ``.fdt`` (older ``.dat`` being invalid) or
- whether the ``EEG.data`` filename exists. If ``EEG.data`` file is absent
- the set file name with .set changed to .fdt is checked.
+ Check if the file extension is ``.fdt`` (older ``.dat`` being invalid)
+ or ``.set`` (new EEGLAB format) or whether the ``EEG.data`` filename exists.
+ If ``EEG.data`` file is absent the set file name with .set changed to .fdt is checked.
"""
fmt = str(op.splitext(dataname)[-1])
if fmt == ".dat":
@@ -49,7 +49,10 @@ def _check_eeglab_fname(fname, dataname):
"Old data format .dat detected. Please update your EEGLAB "
"version and resave the data in .fdt format"
)
-
+ if fmt != ".set" and fmt != ".fdt":
+ raise ValueError(
+ "The file extension must be .set or .fdt, not {}".format(fmt)
+ )
basedir = op.dirname(fname)
data_fname = op.join(basedir, dataname)
if not op.exists(data_fname):
@@ -68,10 +71,10 @@ def _check_eeglab_fname(fname, dataname):
return data_fname
-def _check_load_mat(fname, uint16_codec):
+def _check_load_mat(fname, uint16_codec, preload=False):
"""Check if the mat struct contains 'EEG'."""
fname = _check_fname(fname, "read", True)
- eeg = _readmat(fname, uint16_codec=uint16_codec)
+ eeg = _readmat(fname, uint16_codec=uint16_codec, preload=preload)
if "ALLEEG" in eeg:
raise NotImplementedError(
"Loading an ALLEEG array is not supported. Please contact"
@@ -302,8 +305,6 @@ def read_raw_eeglab(
If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
Defaults to empty tuple.
%(preload)s
- Note that ``preload=False`` will be effective only if the data is
- stored in a separate binary file.
%(uint16_codec)s
%(montage_units)s
@@ -420,8 +421,6 @@ class RawEEGLAB(BaseRaw):
If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
Defaults to empty tuple.
%(preload)s
- Note that preload=False will be effective only if the data is stored
- in a separate binary file.
%(uint16_codec)s
%(montage_units)s
%(verbose)s
@@ -447,7 +446,7 @@ def __init__(
verbose=None,
):
input_fname = str(_check_fname(input_fname, "read", True, "input_fname"))
- eeg = _check_load_mat(input_fname, uint16_codec)
+ eeg = _check_load_mat(input_fname, uint16_codec, preload)
if eeg.trials != 1:
raise TypeError(
f"The number of trials is {eeg.trials:d}. It must be 1 for raw"
@@ -472,14 +471,6 @@ def __init__(
verbose=verbose,
)
else:
- if preload is False or isinstance(preload, str):
- warn(
- "Data will be preloaded. preload=False or a string "
- "preload is not supported when the data is stored in "
- "the .set file"
- )
- # can't be done in standard way with preload=True because of
- # different reading path (.set file)
if eeg.nbchan == 1 and len(eeg.data.shape) == 1:
n_chan, n_times = [1, eeg.data.shape[0]]
else: