mne-tools / mne-tools/mne-python

Bug in interpolate_blinks

Open
#13,531 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

BUG
Dominant language
Python
Stars
3.5k
Forks
1.6k
Avg merge
1d 6h
Merged PRs (30d)
100

Description

Description of the problem
Bug in interpolate_blinks:

The interpolate_blinks function asks for inputs to "match":
"description of annotations to interpolate over. If a list, the data within all annotations that match any of the strings in the list will be interpolated over."

However the helper function _interpolate_blinks hardcodes the description to "BAD_blink" and doesn't use the inputs entered in "match". Therefore any interpolated blinks, no matter which description is entered in match use the start and end times of any "BAD_blink" annotations.

line 91:
starts, ends = _annotations_starts_stops(raw, "BAD_blink")

Solution:

I rewrote the function to avoid this issue by removing the line defining the starts and ends with the _annotations_starts_stops function and instead just used the “blink_annots” created in the function to define starts and stops of the annotations to interpolate over:

        # Create an empty boolean mask
        mask = np.zeros_like(raw.times, dtype=bool)

        # for annot, start, end in zip(blink_annots, starts, ends):
        for annot in blink_annots:
            if "ch_names" not in annot or not annot["ch_names"]:
                msg = f"Blink annotation missing values for 'ch_names' key: {annot}"
                raise ValueError(msg)
            if ch_info["ch_name"] not in annot["ch_names"]:
                continue  # skip if the channel is not in the blink annotation
            # Update the mask for times within the current blink period
            start = annot["onset"] - pre_buffer
            end = annot["onset"] + annot["duration"] + post_buffer
            
            mask |= (raw.times >= start) & (raw.times <= end)

I also added other interpolation methods to choose from and made the function ignore NaNs in the data. I’ve attached the function below in case this should be useful.

interpolate_blinks_function_custom.rtf

Steps to reproduce
raw = interpolate_blinks(raw, buffer=[0.05, 0.05], match=["BAD_velo_blink", "BAD_merged_blink"], interpolate_gaze=False)

-> would only interpolate over time periods where BAD_blink is annotated. This is especially confusing when blinks are automatically annotated from the eyetracker used (e.g. EyeLink)
Link to data

No response

Expected results
Actual results
Additional information

MNE version: 1.9.0

Operating system (on remote server connected to via ssh): Ubuntu 20.04.6

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating interpolate_blinks and _interpolate_blinks, then inspect the line calling _annotations_starts_stops with "BAD_blink". Reproduce the issue with match=["BAD_velo_blink", "BAD_merged_blink"] and confirm that interpolation uses the annotations selected by match rather than hardcoding "BAD_blink".

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.