{"id":18600559,"url":"https://github.com/smathot/eeg_eyetracking_parser","last_synced_at":"2025-04-10T18:31:25.478Z","repository":{"id":40686421,"uuid":"502553369","full_name":"smathot/eeg_eyetracking_parser","owner":"smathot","description":"Python routines for parsing of combined EEG and eye-tracking data","archived":false,"fork":false,"pushed_at":"2025-04-03T13:55:23.000Z","size":187,"stargazers_count":23,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-03T14:40:58.831Z","etag":null,"topics":["data","data-science","eeg","eye","eye-tracking","mne","pupillometry","python"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/eeg-eyetracking-parser/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smathot.png","metadata":{"files":{"readme":"readme-template.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-12T07:56:13.000Z","updated_at":"2025-04-03T13:55:25.000Z","dependencies_parsed_at":"2024-02-14T14:30:53.797Z","dependency_job_id":"6fe43084-292c-46ee-a4d7-1238325edd16","html_url":"https://github.com/smathot/eeg_eyetracking_parser","commit_stats":{"total_commits":132,"total_committers":6,"mean_commits":22.0,"dds":"0.10606060606060608","last_synced_commit":"6143e40986d71c945a8d6865776ddc1141c7e750"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smathot%2Feeg_eyetracking_parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smathot%2Feeg_eyetracking_parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smathot%2Feeg_eyetracking_parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smathot%2Feeg_eyetracking_parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smathot","download_url":"https://codeload.github.com/smathot/eeg_eyetracking_parser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248271683,"owners_count":21075800,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","data-science","eeg","eye","eye-tracking","mne","pupillometry","python"],"created_at":"2024-11-07T02:04:26.101Z","updated_at":"2025-04-10T18:31:25.068Z","avatar_url":"https://github.com/smathot.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Python parser for combined EEG and eye-tracking data \n\nCopyright (2022-2024) Hermine Berberyan, Wouter Kruijne, Sebastiaan Mathôt, Ana Vilotijević\n\n\n[![Publish to PyPi](https://github.com/smathot/eeg_eyetracking_parser/actions/workflows/publish-package.yaml/badge.svg)](https://github.com/smathot/eeg_eyetracking_parser/actions/workflows/publish-package.yaml)\n\n\n## Table of contents\n\n- [About](#about)\n- [Example](#example)\n- [Installation](#installation)\n- [Dependencies](#dependencies)\n- [Assumptions](#assumptions)\n- [Function reference](#function-reference)\n- [License](#license)\n\n## About\n\nA Python module for reading concurrently recorded EEG and eye-tracking data, and parsing this data into convenient objects for further analysis. For this to work, several assumptions need to be met, as described under [Assumptions](#assumptions). At present, this module is largely for internal use, and focused on our own recording environment.\n\nKey features:\n\n- Experimental variables (such as conditions) from the eye-tracking data are used as metadata for the EEG analysis.\n- Gaze and pupil data is added as channels to the EEG data.\n- Automated preprocessing of eye-tracking and EEG data.\n\n\n## Example\n\nParse the data.\n\n```python\nimport eeg_eyetracking_parser as eet\n\n# eet.read_subject.clear()  # uncomment to clear the cache and reparse\nraw, events, metadata = eet.read_subject(2)\nraw.plot()\n```\n\nTo avoid having to parse the data over and over again, `read_subject()` uses persistent [memoization](https://pydatamatrix.eu/memoization/), which is a way to store the return values of a function on disk and return them right away on subsequent calls. To clear the memoization cache, either call the `read_subject.clear()` function or remove the `.memoize` folder.\n\nPlot the voltage across four occipital electrodes locked to cue onset for three seconds. This is done separately for three different conditions, defined by `cue_eccentricity`. The function `eet.autoreject_epochs()` behaves similarly to `mne.Epochs()`, except that autorejection is applied and that, like `read_subject()`, it uses persistent memoization.\n\n```python\nimport numpy as np\nimport mne\nfrom matplotlib import pyplot as plt\nfrom datamatrix import convert as cnv\n\nCUE_TRIGGER = 1\nCHANNELS = 'O1', 'O2', 'Oz', 'P3', 'P4'\n\ncue_epoch = eet.autoreject_epochs(raw, eet.epoch_trigger(events, CUE_TRIGGER),\n                                  tmin=-.1, tmax=3, metadata=metadata,\n                                  picks=CHANNELS)\n```\n\nWe can convert the metadata, which is a `DataFrame`, to a `DataMatrix`, and add `cue_epoch` as a multidimensional column\n\n```python\nfrom datamatrix import convert as cnv\nimport time_series_test as tst\n\ndm = cnv.from_pandas(metadata)\ndm.erp = cnv.from_mne_epochs(cue_epoch)  # rows x channel x time\ndm.mean_erp = dm.erp[:, ...]             # Average over channels: rows x time\ntst.plot(dm, dv='mean_erp', hue_factor='cue_eccentricity')\n```\n\nBecause the regular `mne.Epoch()` object doesn't play nice with non-data channels, such as pupil size, you need to use the `eet.PupilEpochs()` class instead. This is class otherwise identical, except that it by default removes trials where baseline pupil size is more than 2 SD from the mean baseline pupil size.\n\n```python\npupil_cue_epoch = eet.PupilEpochs(raw, eet.epoch_trigger(events, CUE_TRIGGER),\n                                  tmin=0, tmax=3, metadata=metadata,\n                                  baseline=(0, .05))\ndm.pupil = cnv.from_mne_epochs(pupil_cue_epoch, ch_avg=True)  # only 1 channel\ntst.plot(dm, dv='pupil', hue_factor='cue_eccentricity')\n```\n\n\n## Installation\n\n```\npip install eeg_eyetracking_parser\n```\n\n## Dependencies\n\n- datamatrix \u003e= 1.0\n- eyelinkparser\n- mne\n- autoreject\n- h5io\n- braindecode\n- python-picard\n- json_tricks\n\n\n## Assumptions\n\n### Data format\n\n- EEG data should be in BrainVision format (`.vhdr`), recorded at 1000 Hz\n- Eye-tracking data should be EyeLink format (`.edf`), recorded monocularly at 1000 Hz\n\n### File and folder structure\n\nFiles should be organized following [BIDS](https://bids-specification.readthedocs.io/).\n\n```\n# Container folder for all data\ndata/\n    # Subject 2\n    sub-02/\n        # EEG data\n        eeg/\n            sub-02_task-attentionalbreadth_eeg.eeg\n            sub-02_task-attentionalbreadth_eeg.vhdr\n            sub-02_task-attentionalbreadth_eeg.vmrk\n        # Behavioral data (usually not necessary)\n        beh/\n            sub-02_task-attentionalbreadth_beh.csv\n        # Eye-tracking data\n        eyetracking/\n            sub-02_task-attentionalbreadth_physio.edf\n```\n\nYou can re-organize data files into the above structure automatically with the `data2bids` command, which is part of this package. \n\nAssumptions:\n\n-   all EEG files (.eeg, .vhdr, .vmrk) \n    are named in a 'Subject-00X-timestamp' format (e.g. Subject-002-[2022.06.12-14.35.46].eeg)\n-   eye-tracking files (.edf)\n    are named in a 'sub_X format' (e.g. sub_2.edf)\n    \nFor example, to re-organize from participants 1, 2, 3, and 4 for a task called 'attentional-breadth', you can run the following command. This assumes that the unorganized files are in a subfolder called `data` and that the re-organized (BIDS-compatible) files are also in this subfolder, i.e. as shown above.\n\n```\ndata2bids --source-path=data --target-path=data -s=1,2,3,4 -t=attentional-breadth\n```\n\n### Trigger codes\n\nThe start of each trial is indicated by a counter that starts at 128 for the first trial, and wraps around after 255, such that trial 129 is indicated again by 128. This trigger does not need to be sent to the eye tracker, which uses its own `start_trial` message. A temporal offset between the `start_trial` message of the eye tracker and the start-trial trigger of the EEG is ok, and will be compensated for during parsing.\n\n```python\nEE.PulseLines(128 + trialid % 128, 10)  # EE is the EventExchange object\n```\n\nThe onset of each epoch is indicated by a counter that starts at 1 for the first epoch, and then increases for subsequent epochs. In other words, if the target presentation is the second epoch of the trial, then this would correspond to trigger 2 as in the example below. This trigger needs to be sent to both the EEG and the eye tracker at the exact same moment (a temporal offset is *not* ok).\n\n```python\ntarget_trigger = 2\neyetracker.log(f'start_phase {target_trigger}')  # eyetracker is created by PyGaze\nEE.PulseLines(target_trigger, 10)\n```\n\nTriggers should only be used for temporal information. Conditions are only logged in the eye-tracking data.\n\n\n## Function reference\n\n``` { .python silent }\nimport sys, os\nsys.path.insert(0, os.getcwd())\nfrom npdoc_to_md import render_obj_docstring\n\nprint(render_obj_docstring(\n    'eeg_eyetracking_parser.autoreject_epochs._fnc',\n    'autoreject_epochs'))\nprint('\\n\\n')\nprint(render_obj_docstring('eeg_eyetracking_parser.epoch_trigger',\n    'epoch_trigger'))\nprint('\\n\\n')\nprint(render_obj_docstring('eeg_eyetracking_parser.PupilEpochs',\n    'PupilEpochs'))\nprint('\\n\\n')\nprint(render_obj_docstring('eeg_eyetracking_parser.read_subject._fnc',\n    'read_subject'))\nprint('\\n\\n')\nprint(render_obj_docstring('eeg_eyetracking_parser.trial_trigger',\n    'trial_trigger'))\nprint('\\n\\n')\nprint(render_obj_docstring('eeg_eyetracking_parser.braindecode_utils.decode_subject._fnc',\n    'braindecode_utils.decode_subject'))\n```\n\n\n\n## License\n\n`eeg_eyetracking_parser` is licensed under the [GNU General Public License\nv3](http://www.gnu.org/licenses/gpl-3.0.en.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmathot%2Feeg_eyetracking_parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmathot%2Feeg_eyetracking_parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmathot%2Feeg_eyetracking_parser/lists"}