Repository: mne-tools/mne-python

Issue:
can’t import my set file using read_epochs_eeglab()
### Description of the problem

I get the error: “TypeError: ‘int’ object is not iterable”.I am not sure where I could be making a mistake.


### Steps to reproduce

```Python
these are my codes:
import mne
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import pickle
import pymatreader
import mffpy
#single sub evoked
eeg1 = mne.io.read_epochs_eeglab(‘1_LH.set’)
```


### Link to data

_No response_

### Expected results

how to solve the problem

### Actual results

eeg1 = mne.io.read_epochs_eeglab('1_LH.set')
Extracting parameters from E:\code\EEG ML code\EEG\part2\2_extra_features\Example_data\1_LH.set...
Traceback (most recent call last):

  File "C:\Users\Administrator\AppData\Local\Temp/ipykernel_17044/1102068857.py", line 1, in <module>
    eeg1 = mne.io.read_epochs_eeglab('1_LH.set')

  File "C:\ProgramData\Anaconda3\lib\site-packages\mne\io\eeglab\eeglab.py", line 337, in read_epochs_eeglab
    epochs = EpochsEEGLAB(input_fname=input_fname, events=events, eog=eog,

  File "<decorator-gen-296>", line 12, in __init__

  File "C:\ProgramData\Anaconda3\lib\site-packages\mne\io\eeglab\eeglab.py", line 569, in __init__
    info, eeg_montage, _ = _get_info(eeg, eog=eog, scale_units=scale_units)

  File "C:\ProgramData\Anaconda3\lib\site-packages\mne\io\eeglab\eeglab.py", line 199, in _get_info
    _get_montage_information(eeg, has_pos, scale_units=scale_units)

  File "C:\ProgramData\Anaconda3\lib\site-packages\mne\io\eeglab\eeglab.py", line 150, in _get_montage_information
    for item in list(zip(*eeg.chaninfo['nodatchans'].values())):

TypeError: 'int' object is not iterable

### Additional information

my mne version is 1.3.1
windows 11



Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴

please provide a download link to the problematic file. Without that it is very hard to guess what might be going wrong.

Cross-ref to forum posts:
- https://mne.discourse.group/t/i-am-running-into-an-error-when-importing-my-data-with-read-epochs-eeglab/6657
- https://mne.discourse.group/t/cant-import-my-set-file-using-read-epochs-eeglab/6718
> please provide a download link to the problematic file. Without that it is very hard to guess what might be going wrong.
> 
> Cross-ref to forum posts:

> * https://mne.discourse.group/t/i-am-running-into-an-error-when-importing-my-data-with-read-epochs-eeglab/6657
> * https://mne.discourse.group/t/cant-import-my-set-file-using-read-epochs-eeglab/6718

This  is my data, please check. Thanks!
 https://github.com/INRING/data/blob/7e5f3a386e2b3d8dec4e807cb5bce04178eef06a/1_LH.zip

your file is inconsistent:

here is what eeg.chaninfo['nodatchans'].keys() contains:

dict_values(['VEO', array([], dtype='<U1'), array([], dtype=uint8),
array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8),
array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8),
array([], dtype=uint8), 32, 'VEO', 0])

the structure of this file has been updated manually and broke the format.
I don't think we can really fix this in the mne reader.

can you load the data in matlab and use the eeglab checkset function to see
if you can fix it?

> Message ID: ***@***.***>
>

> your file is inconsistent: here is what eeg.chaninfo['nodatchans'].keys() contains: dict_values(['VEO', array([], dtype='<U1'), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), array([], dtype=uint8), 32, 'VEO', 0]) the structure of this file has been updated manually and broke the format. I don't think we can really fix this in the mne reader. can you load the data in matlab and use the eeglab checkset function to see if you can fix it?
> […](#)
> Message ID: ***@***.***>

I also run other data , the data is: https://github.com/INRING/data/blob/e2eaf48bcf223fd207794b16379f6d36502ad3e5/4.23.1.zip
and I also use the eeglab checkset function ,my matlab code is:
for i = [1:2];
setname = [num2str(i) '.set'];
EEG = pop_loadset('filename',setname,'filepath','E:\loss aversion\duyan\loss aversion\EEG\geti data\4.23');
EEG = eeg_checkset( EEG );
EEG = pop_saveset( EEG, 'filename',setname,'filepath','E:\loss aversion\duyan\loss aversion\EEG\geti data\4.23.1');
end
but I also have the same problem.
what does eeg.chaninfo.nodatchans give you in matlab?
can you check that the values correct and consistent across channels?

> Message ID: ***@***.***>
>

> what does eeg.chaninfo.nodatchans give you in matlab? can you check that the values correct and consistent across channels?
> […](#)
> Message ID: ***@***.***>

Can you load this data on eeglab and check if it is correct? I checked it on eeglab and I think it is error-free. But when I load it on mne, this error cannot be solved. I tried different data and the error is almost the same (TypeError: ‘int’ objecr is not interable) or (‘float’ object is not iterable). I tried to solve it and looked at many similar questions and answers, but this problem keeps appearing. Others have encountered it but have not been able to solve it. If it cannot be solved, it will affect the later feature extraction and other work. Or do you have a better way to read it? I look forward to your reply, thank you
sorry I don't have a matlab licence to test.

> Message ID: ***@***.***>
>

Have you solved this problem
Please provide a quick fix or workaround. There are multiple public dataset which have this issue. 
Example: https://openneuro.org/datasets/ds003655
> Please provide a quick fix or workaround.

The workaround is to load the data in EEGLAB and export as some other format besides `.set` (such as EDF). I'm sorry, I know that's not a very satisfactory answer, but we (MNE-Python maintainers) are not all EEGLAB users (much less experts) and those that are have limited time. 

I am one of the non-EEGLAB experts and I just spent ~45 minutes in the python debugger, reading EEGLAB source code, loading the file in Octave, etc. I can tell you that the *reason* for the error is that `eeg.chaninfo['nodatchans']['datachan'] = 0` and our code expects all entries in `eeg.chaninfo['nodatchans']` to be iterables (lists or arrays).  But I have little idea what the value represents so I can't propose a reasonable fix to the code. maybe @cbrnr or @agramfort or @smathot has some ideas?
here's a reproducer, for the next person:

```console
$ pip install openneuro-py
$ openneuro-py download --dataset=ds003655 --include sub-008
$ python
>>> import mne_bids as mb
>>> bp = BIDSPath(root="ds00365", subject='008', task='VerbalWorkingMemory')
>>> raw = mb.read_raw_bids(bp)
```
I also can't find any information about the meaning of the data in `eeg.chaninfo['nodatchans']`. But it has seemingly no relationship with the currently implemented code. 

`eeg.chaninfo['nodatchans']` value in example recording:
`{'labels': '[A1A2]', 'ref': '[A1A2]', 'theta': array([], dtype=uint8), 'radius': array([], dtype=uint8), 'X': array([], dtype=uint8), 'Y': array([], dtype=uint8), 'Z': array([], dtype=uint8), 'sph_theta': array([], dtype=uint8), 'sph_phi': array([], dtype=uint8), 'sph_radius': array([], dtype=uint8), 'type': array([], dtype='<U1'), 'urchan': array([], dtype=uint8), 'datachan': 0}`

Processing code segment:
https://github.com/mne-tools/mne-python/blob/a4d4c10c6546bd53fa01f7b81ca87c6d2c0f155a/mne/io/eeglab/eeglab.py#L167C5-L178C57

The code which uses `eeg.chaninfo['nodatchans']` seems to fill three variable which have None-s for default value. If someone needs a workaround then I propose a simple iterator check after line 168. So we simply keep the default variable values if the check fails. Example (tested on my files):
`
all_iterable = all(isinstance(v, (list, tuple, np.ndarray)) for v in eeg.chaninfo["nodatchans"].values())
`
I'm also just guessing here, but apparently `eeg.chaninfo['nodatchans']` can contain information on fiducials. I am not quite sure what `eeg.chaninfo['nodatchans']['datachan']` refers to, but `0` might indicate that this channel is not part of the data array (because MATLAB is `1`-indexed, so `0` means it is not present). If this is true, then we can safely special-case `0` to mean "not present".

I would like to see an example of a file where this field has some value other than `0` though.

And maybe @arnodelorme can help us out here?
Yes, ```eeg.chaninfo['nodatchans']['datachan']``` is always 0.
Great, thanks!

Resolving patch:
diff --git a/mne/io/eeglab/eeglab.py b/mne/io/eeglab/eeglab.py
--- a/mne/io/eeglab/eeglab.py
+++ b/mne/io/eeglab/eeglab.py
@@ -165,17 +165,23 @@ def _get_montage_information(eeg, get_pos, *, montage_units):
         )
 
     lpa, rpa, nasion = None, None, None
-    if hasattr(eeg, "chaninfo") and len(eeg.chaninfo.get("nodatchans", [])):
-        for item in list(zip(*eeg.chaninfo["nodatchans"].values())):
-            d = dict(zip(eeg.chaninfo["nodatchans"].keys(), item))
-            if d.get("type", None) != "FID":
+    if hasattr(eeg, "chaninfo") and isinstance(eeg.chaninfo["nodatchans"], dict):
+        nodatchans = eeg.chaninfo["nodatchans"]
+        types = nodatchans.get("type", [])
+        descriptions = nodatchans.get("description", [])
+        xs = nodatchans.get("X", [])
+        ys = nodatchans.get("Y", [])
+        zs = nodatchans.get("Z", [])
+
+        for type_, description, x, y, z in zip(types, descriptions, xs, ys, zs):
+            if type_ != "FID":
                 continue
-            elif d.get("description", None) == "Nasion":
-                nasion = np.array([d["X"], d["Y"], d["Z"]])
-            elif d.get("description", None) == "Right periauricular point":
-                rpa = np.array([d["X"], d["Y"], d["Z"]])
-            elif d.get("description", None) == "Left periauricular point":
-                lpa = np.array([d["X"], d["Y"], d["Z"]])
+            if description == "Nasion":
+                nasion = np.array([x, y, z])
+            elif description == "Right periauricular point":
+                rpa = np.array([x, y, z])
+            elif description == "Left periauricular point":
+                lpa = np.array([x, y, z])
 
     # Always check this even if it's not used
     _check_option("montage_units", montage_units, ("m", "dm", "cm", "mm", "auto"))


Test patch: