From 570f0d5ee743c524b5579dc1127f5c94f78401d3 Mon Sep 17 00:00:00 2001 From: Scott Huberty Date: Mon, 26 Feb 2024 15:30:44 -0800 Subject: [PATCH] ENH: Add option to get HREF data to read_mne_eyetracking_raw - if you pass "eyetrack_unit='href', a copy of the eyetracking file with units converted to HREF radians will be downloaded and synced with the raw EEG --- eoglearn/datasets/eegeyenet.py | 6 +---- eoglearn/datasets/mne.py | 33 +++++++++++++++++++++--- eoglearn/datasets/tests/test_datasets.py | 15 ++++++----- eoglearn/datasets/utils.py | 5 ++++ examples/plot_model.py | 6 +++++ 5 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 eoglearn/datasets/utils.py diff --git a/eoglearn/datasets/eegeyenet.py b/eoglearn/datasets/eegeyenet.py index 1167a2d..03500d4 100644 --- a/eoglearn/datasets/eegeyenet.py +++ b/eoglearn/datasets/eegeyenet.py @@ -1,4 +1,4 @@ -import mne +from .utils import _fetch_dataset PARAMS = { "EP10_DOTS": list( @@ -17,10 +17,6 @@ DOTS = {"EP10": PARAMS["EP10_DOTS"]} -def _fetch_dataset(fetch_dataset_kwargs): - return mne.datasets.fetch_dataset(**fetch_dataset_kwargs) - - def fetch_eegeyenet(subject="EP10", run=0, fetch_dataset_kwargs=None): """Fetch a sample file from the EEG Eyenet dataset. diff --git a/eoglearn/datasets/mne.py b/eoglearn/datasets/mne.py index 78ae83f..372b8a7 100644 --- a/eoglearn/datasets/mne.py +++ b/eoglearn/datasets/mne.py @@ -7,8 +7,10 @@ import mne from mne.utils import logger +from .utils import _fetch_dataset -def read_mne_eyetracking_raw(return_events=False, bandpass=True): + +def read_mne_eyetracking_raw(return_events=False, bandpass=True, eyetrack_unit="px"): """Return an MNE Raw object containing the EyeLink dataset. Parameters @@ -16,7 +18,13 @@ def read_mne_eyetracking_raw(return_events=False, bandpass=True): return_events : bool If ``True``, return the events for the eyetracking and EEG data. bandpass: bool - If ``True``, applied a [1, 30]Hz bandpass. + If ``True``, apply a [1, 30]Hz bandpass to the EEG data. + eyetrack_unit : str + The desired unit of the eyetracking data. Must be "px" or "href", corresponding + to pixels-on-screen or head-referenced-eye-angle, respectively. Note that + HREF data are reported in radians. If HREF data are requested, a separate data + file will be downloaded from OSF, to ``~/mne_data/eog-learn-example-data``. + Defaults to "px". Returns ------- @@ -33,11 +41,30 @@ def read_mne_eyetracking_raw(return_events=False, bandpass=True): 90_eyetracking_data.html>`_ for more information on this dataset. """ + if eyetrack_unit not in ["px", "href"]: + raise ValueError("eyetrack_unit must be 'px' or 'href'") + data_path = mne.datasets.eyelink.data_path() if mne.utils.check_version("mne", "1.6"): data_path = data_path / "eeg-et" - et_fpath = data_path / "sub-01_task-plr_eyetrack.asc" + eeg_fpath = data_path / "sub-01_task-plr_eeg.mff" + # now get the eyetracking data + if eyetrack_unit == "px": + et_fpath = data_path / "sub-01_task-plr_eyetrack.asc" + elif eyetrack_unit == "href": + # the HREF file is not used by MNE and so we store it separately on OSF... + ds_params = dict( + dataset_params=dict( + url="https://osf.io/829m4/download?version=1", + archive_name="sub-01_task-plr-href_eyetrack.asc", + folder_name="eog-learn-example-data", + hash="sha256:e595e92efc63f608c0214caf4cee69a5b7ae2d08959503a3eef641931fcce32a", + dataset_name="PLR_HREF", + ) + ) + et_fpath = _fetch_dataset(ds_params) + et_fpath = et_fpath / "sub-01_task-plr-href_eyetrack.asc" logger.debug(f"## EOGLEARN: Reading data from {et_fpath} and {eeg_fpath}") raw_et = mne.io.read_raw_eyelink(et_fpath, create_annotations=["blinks"]) diff --git a/eoglearn/datasets/tests/test_datasets.py b/eoglearn/datasets/tests/test_datasets.py index bed25ab..6e4680e 100644 --- a/eoglearn/datasets/tests/test_datasets.py +++ b/eoglearn/datasets/tests/test_datasets.py @@ -1,16 +1,19 @@ -from eoglearn.datasets import fetch_eegeyenet +import pytest +from eoglearn.datasets import fetch_eegeyenet, read_mne_eyetracking_raw -def test_read_mne_eyetracking_raw(mne_fixture): + +@pytest.mark.parametrize("unit", ["px", "href"]) +def test_read_mne_eyetracking_raw(unit): """Test the read_mne_eyetracking_raw function.""" - ch_types = mne_fixture.raw.get_channel_types() + raw, events = read_mne_eyetracking_raw(eyetrack_unit=unit, return_events=True) + ch_types = raw.get_channel_types() assert ch_types.count("eyegaze") == 2 assert ch_types.count("pupil") == 1 assert ch_types.count("misc") == 3 assert ch_types.count("eeg") == 129 - events_dict = mne_fixture.events_dict - assert len(events_dict["eeg"][:, -1]) == 16 - assert len(events_dict["eyetrack"][:, -1]) == 16 + assert len(events["eeg"][:, -1]) == 16 + assert len(events["eyetrack"][:, -1]) == 16 def test_fetch_eegeyenet(): diff --git a/eoglearn/datasets/utils.py b/eoglearn/datasets/utils.py new file mode 100644 index 0000000..276aee4 --- /dev/null +++ b/eoglearn/datasets/utils.py @@ -0,0 +1,5 @@ +import mne + + +def _fetch_dataset(fetch_dataset_kwargs): + return mne.datasets.fetch_dataset(**fetch_dataset_kwargs) diff --git a/examples/plot_model.py b/examples/plot_model.py index c4b9b2b..31a7b64 100644 --- a/examples/plot_model.py +++ b/examples/plot_model.py @@ -22,6 +22,12 @@ # %% raw +# %% +# .. note:: +# If you want eye tracking data in head-referenced-eye-angle (HREF) units, you can +# pass ``eyetrack_unit="href"`` to +# :func:`~eoglearn.datasets.read_mne_eyetracking_raw`. + # %% # Plot the data raw.plot()