diff --git a/mne/io/eeglab/_eeglab.py b/mne/io/eeglab/_eeglab.py index 28df469fada..682793bde6c 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,90 @@ 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 10e23b811a0..ff7a15d9602 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,8 @@ 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(f"The file extension must be .set or .fdt, not {fmt}") basedir = op.dirname(fname) data_fname = op.join(basedir, dataname) if not op.exists(data_fname): @@ -68,10 +69,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 +303,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 +419,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 +444,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 +469,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: