Repository: mne-tools/mne-python

Issue:
Annotation-related error reading EEGLAB file
### Description of the problem

When trying to read a specific EEGLAB file, there is an error related to annotations and absolute onset time. This problem was reported in our forum: https://mne.discourse.group/t/read-raw-eeglab-gives-valueerror-cannot-convert-float-nan-to-integer/8285

### Steps to reproduce

```Python
import openneuro as on

import mne

on.download(
    dataset="ds004842",
    include=[
        "sub-001/**/*run-1*.set",
        "sub-001/**/*run-1*.fdt",
    ],
)

raw = mne.io.read_raw_eeglab(
    "ds004842/sub-001/ses-C1C/eeg/sub-001_ses-C1C_task-DriveOnMission_run-1_eeg.set"
)
```


### Link to data

https://openneuro.org/datasets/ds004842/versions/1.0.0

### Expected results

I should be able to import the data.

### Actual results

There is an error when importing the data:

```
<stdin>:1: RuntimeWarning: Unknown types found, setting as type EEG:
inferiorveog: ['EXG4']
leftheog: ['EXG1']
leftmastoid: ['EXG5']
rightheog: ['EXG2']
rightmastoid: ['EXG6']
superiorveog: ['EXG3']
Reading /Users/clemens/Projects/mne-python/ds004842/sub-001/ses-C1C/eeg/sub-001_ses-C1C_task-DriveOnMission_run-1_eeg.fdt
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/clemens/Projects/mne-python/mne/io/eeglab/eeglab.py", line 332, in read_raw_eeglab
    return RawEEGLAB(
           ^^^^^^^^^^
  File "<decorator-gen-238>", line 12, in __init__
  File "/Users/clemens/Projects/mne-python/mne/io/eeglab/eeglab.py", line 505, in __init__
    self.set_annotations(annot)
  File "<decorator-gen-225>", line 12, in set_annotations
  File "/Users/clemens/Projects/mne-python/mne/io/base.py", line 734, in set_annotations
    new_annotations.crop(
  File "<decorator-gen-38>", line 12, in crop
  File "/Users/clemens/Projects/mne-python/mne/annotations.py", line 636, in crop
    absolute_onset = timedelta(seconds=onset) + offset
                     ^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: cannot convert float NaN to integer
```

### Additional information

```
Platform             macOS-14.3-arm64-arm-64bit
Python               3.12.1 (main, Jan 24 2024, 11:32:51) [Clang 15.0.0 (clang-1500.1.0.2.5)]
Executable           /Users/clemens/.pyenv/versions/3.12.1/envs/mne/bin/python3.12
CPU                  arm (12 cores)
Memory               32.0 GB

Core
├☑ mne               1.7.0.dev77+g566c6ea5a (devel, latest release is 1.6.1)
├☑ numpy             1.26.3 (OpenBLAS 0.3.23.dev with 12 threads)
├☑ scipy             1.12.0
└☑ matplotlib        3.8.2 (backend=MacOSX)

Numerical (optional)
└☐ unavailable       sklearn, numba, nibabel, nilearn, dipy, openmeeg, cupy, pandas

Visualization (optional)
├☑ qtpy              2.4.1 (PySide6=6.6.1)
├☑ pyqtgraph         0.13.3
├☑ mne-qt-browser    0.6.1
└☐ unavailable       pyvista, pyvistaqt, vtk, ipympl, ipywidgets, trame_client, trame_server, trame_vtk, trame_vuetify

Ecosystem (optional)
└☐ unavailable       mne-bids, mne-nirs, mne-features, mne-connectivity, mne-icalabel, mne-bids-pipeline, neo
```


Discussion:
Hello @cbrnr I was actually about to make an issue for this. I can try to work on this soon. I imported it into EEGLAB and I believe it just ignores them:
<img width="463" alt="image" src="https://github.com/mne-tools/mne-python/assets/20428109/f4f66879-044f-4d93-8035-eb95b1357b70">

@withmywoessner are you still up for fixing this issue? Just asking if we can try to get it into the next release. If not, that's totally fine, and we'll just move it to a future release.
Hey @cbrnr! I got a little busy with some other things and forgot to make a PR. I'll make one this weekend. Is 1.7 planned on being released on April 14th? 
No worries! The milestone deadline is set to April 15, so I guess this is what we're aiming for (but @larsoner can tell for sure). 
Yeah mid April is the tentative plan

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
@@ -799,6 +799,22 @@ def _read_annotations_eeglab(eeg, uint16_codec=None):
             )
             duration[idx] = np.nan if is_empty_array else event.duration
 
+    # Drop events with NaN onset see PR #12484
+    valid_indices = [
+        idx for idx, onset_idx in enumerate(onset) if not np.isnan(onset_idx)
+    ]
+    n_dropped = len(onset) - len(valid_indices)
+    if len(valid_indices) != len(onset):
+        warn(
+            f"{n_dropped} events have an onset that is NaN. These values are "
+            "usually ignored by EEGLAB and will be dropped from the "
+            "annotations."
+        )
+
+    onset = np.array([onset[idx] for idx in valid_indices])
+    duration = np.array([duration[idx] for idx in valid_indices])
+    description = [description[idx] for idx in valid_indices]
+
     return Annotations(
         onset=np.array(onset) / eeg.srate,
         duration=duration / eeg.srate,


Test patch:
diff --git a/mne/io/eeglab/tests/test_eeglab.py b/mne/io/eeglab/tests/test_eeglab.py
--- a/mne/io/eeglab/tests/test_eeglab.py
+++ b/mne/io/eeglab/tests/test_eeglab.py
@@ -719,3 +719,36 @@ def get_bad_information(eeg, get_pos, *, montage_units):
     assert len(pos["lpa"]) == 3
     assert len(pos["rpa"]) == 3
     assert len(raw.info["dig"]) == n_eeg + 3
+
+
+@testing.requires_testing_data
+def test_eeglab_drop_nan_annotations(tmp_path):
+    """Test reading file with NaN annotations."""
+    pytest.importorskip("eeglabio")
+    from eeglabio.raw import export_set
+
+    file_path = tmp_path / "test_nan_anno.set"
+    raw = read_raw_eeglab(raw_fname_mat, preload=True)
+    data = raw.get_data()
+    sfreq = raw.info["sfreq"]
+    ch_names = raw.ch_names
+    anno = [
+        raw.annotations.description,
+        raw.annotations.onset,
+        raw.annotations.duration,
+    ]
+    anno[1][0] = np.nan
+
+    export_set(
+        str(file_path),
+        data,
+        sfreq,
+        ch_names,
+        ch_locs=None,
+        annotations=anno,
+        ref_channels="common",
+        ch_types=np.repeat("EEG", len(ch_names)),
+    )
+
+    with pytest.raises(RuntimeWarning, match="1 .* have an onset that is NaN.*"):
+        raw = read_raw_eeglab(file_path, preload=True)