mkh5 data in MNE Python (experimental)
Note
Requires mkpy >= 0.2.5 and MNE Python >=1.0 (installed by default
when mkpy is installed with mamba
or conda
)
An mkh5 data file can be imported into MNE Python and used with the
native MNE mne.Raw
continous EEG methods and
mne.Epochs
Import options support automatically converting mkh5 codemapped event
tags to mne.Epochs.metadata
and mkh5 garv artifacts (log_flags > 0) to
mne.Annotations for MNE native epoch artifact screening.
EEG channel and electrode location information in 3D cartesian RAS coordinate space measured in metric units is required and provided via a YAML file apparatus map. The apparatus map can be included as a YAML doc in the .yhdr and baked into the mkh5 HDF5 or provided later when the mkh5 data are converted to MNE.
The steps to prepare mkh5 data for use in MNE are as usual:
Prepare mkh5 data for MNE
For continuous EEG
Type up a YAML file with a mkpy standard apparatus map document where
For each channel in the streams map, the pos value must be a label in the sensors and the mne_type value must an MNE channel type: eeg, eog, stim, misc.
For each electrode in the sensors map, give x, y, z coordinates in Right Anterior Superior (RAS) space in the metric units given in the the apparatus space map.
Convert .crw/.log to mkh5 as usual.
For single trial epochs
Define an event codemap.
Look up the event table (
mkh5.get_event_table()
)Set one or more named epochs tables (
mkh5.set_epochs()
).
Convert mkh5 to MNE
For MNE continuous EEG recordings use mkh5mne.from_mkh5()
to create a RawMkh5 instance.
For MNE epochs mkh5mne.get_epochs()
on a RawMkh5 instance.
See mkh5_with_mne for usage and examples.
Gotcha: mne.find_events()
IS UNSAFE
Warning
Use mkh5 data use mkh5mne.find_mkh5_events()
to create
MNE event arrays. DO NOT use the native MNE
mne.find_events()
the results may be incorrect.
Various MNE raw data methods including plotting and epoching ingest a 3-column event_array of [sample, 0, event] that says where (column 0) and what (column 2) the events are in the EEG recording.
The event array format is sound and processing event arrays is reliable.
Unfortunately the native MNE event lookup utility
mne.find_events()
for creating event arrays from
event channels forcibly converts negative events to positive.
The .crw/.log/mkh5 data format routinely uses negative event codes for pause marks, data errors, and manually logpoked negative event codes. Changing the sign to positive folds them back back into the experimental event code range and creates creates spurious events.
Sample YAML apparatus map
---
# Recording apparatus information used to populate MNE python data
# structures. For reproducibility this yaml doc can be included in the
# mkpy.mkh5 .yhdr so it is baked into the mkh5 header and travels with
# the data.
license: Creative Commons Attribution-NonCommercial-ShareAlike (CC BY-NC-SA) 4.0
notes: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike
4.0 International License. https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
# apparatus/instrumentation parameters
name: apparatus
# set for MNE python data import
mne_montage_name: 26chan
# ALL CHAN settings via YAML anchor-reference syntax
common_ref: &A1 A1
gain20K: &20K 20000
gain10K: &10K 10000
lp: &lp 100.0
hp: &hp 000.01
# Notes:
# * pos = positive input to differential bioamp
# * neg = negative input
# * #n indicates original dig header channel index
# digitized EEG bioamp output data streams
streams:
# 0
lle:
pos: lle
neg: *A1
gain: *10K
hphz: *hp
lphz: *lp
mne_type: eeg
# 1
lhz:
pos: lhz
neg: *A1
gain: *10K
hphz: *hp
lphz: *lp
mne_type: eeg
# 2
MiPf:
pos: MiPf
neg: *A1
gain: *10K
hphz: *hp
lphz: *lp
mne_type: eeg
# 3
LLPf:
pos: LLPf
neg: *A1
gain: *10K
hphz: *hp
lphz: *lp
mne_type: eeg
# 9
LLFr:
pos: LLFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 23
LLTe:
pos: LLTe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 25
LLOc:
pos: LLOc
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 27
MiOc:
pos: MiOc
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 7
LDFr:
pos: LDFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 17
LDCe:
pos: LDCe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 19
LDPa:
pos: LDPa
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 5
LMPf:
pos: LMPf
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 11
LMFr:
pos: LMFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 13
LMCe:
pos: LMCe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 21
LMOc:
pos: LMOc
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 15
MiCe:
pos: MiCe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 16
MiPa:
pos: MiPa
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 30
rle:
pos: rle
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 31
rhz:
pos: rhz
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 28
A2:
pos: A2
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 4
RLPf:
pos: RLPf
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 10
RLFr:
pos: RLFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 24
RLTe:
pos: RLTe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 26
RLOc:
pos: RLOc
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 8
RDFr:
pos: RDFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 18
RDCe:
pos: RDCe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 20
RDPa:
pos: RDPa
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 6
RMPf:
pos: RMPf
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 12
RMFr:
pos: RMFr
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 14
RMCe:
pos: RMCe
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 22
RMOc:
pos: RMOc
neg: *A1
gain: *20K
hphz: *hp
lphz: *lp
mne_type: eeg
# 29
HEOG:
pos: lhz
neg: rhz
gain: *10K
hphz: *hp
lphz: *lp
mne_type: eog
# 3D RAS based on a measured red cap
space:
coordinates: cartesian
distance_unit: m
orientation: ras
fiducials:
lpa:
x: -0.08075
y: 0.0
z: 0.0
nasion:
x: 0.0
y: 0.11875
z: 0.0
rpa:
x: 0.08075
y: 0.0
z: 0.0
sensors:
A1:
x: -0.0659
y: -0.0307
z: -0.061
A2:
x: 0.0659
y: -0.0307
z: -0.061
gnd:
x: 0.0
y: 0.0903
z: 0.0293
LDCe:
x: -0.0814
y: 0.0
z: 0.0489
LDFr:
x: -0.0658
y: 0.0478
z: 0.0489
LDPa:
x: -0.0658
y: -0.0478
z: 0.0489
LLFr:
x: -0.0903
y: 0.0293
z: 0.0
LLOc:
x: -0.0558
y: -0.0768
z: 0.0
LLPf:
x: -0.0558
y: 0.0768
z: 0.0
LLTe:
x: -0.0903
y: -0.0293
z: 0.0
LMCe:
x: -0.0396
y: -0.0128
z: 0.0853
LMFr:
x: -0.0244
y: 0.0336
z: 0.0853
LMOc:
x: -0.0251
y: -0.0774
z: 0.0489
LMPf:
x: -0.0251
y: 0.0774
z: 0.0489
MiCe:
x: 0.0
y: 0.0
z: 0.095
MiOc:
x: 0.0
y: -0.095
z: 0.0
MiPa:
x: 0.0
y: -0.0416
z: 0.0853
MiPf:
x: 0.0
y: 0.095
z: 0.0
RDCe:
x: 0.0814
y: 0.0
z: 0.0489
RDFr:
x: 0.0658
y: 0.0478
z: 0.0489
RDPa:
x: 0.0658
y: -0.0478
z: 0.0489
RLFr:
x: 0.0903
y: 0.0293
z: 0.0
RLOc:
x: 0.0558
y: -0.0768
z: 0.0
RLPf:
x: 0.0558
y: 0.0768
z: 0.0
RLTe:
x: 0.0903
y: -0.0293
z: 0.0
RMCe:
x: 0.0396
y: -0.0128
z: 0.0853
RMFr:
x: 0.0244
y: 0.0336
z: 0.0853
RMOc:
x: 0.0251
y: -0.0774
z: 0.0489
RMPf:
x: 0.0251
y: 0.0774
z: 0.0489
lhz:
x: -0.065
y: 0.0850
z: -0.0293
lle:
x: -0.0095
y: 0.0240
z: -0.0727
rhz:
x: 0.065
y: 0.0850
z: -0.0293
rle:
x: 0.0095
y: 0.0240
z: -0.0727