eegdash.dataset.channel_positions module#

Positions for iEEG channels whose names are not rows of electrodes.tsv.

A recording’s channels are often named differently from its contacts:

  • an EDF type word: POL A3 is the contact A3 (on004551);

  • a reference suffix: EEG A1-Ref is contact A1 against a reference that is not a contact (on004551);

  • a bipolar pair: RAFA1-RAFA2 (on004789, on004809 acq-bipolar) or POL B10-B18 (on006910, on006914, on005545) is the difference of two contacts.

MNE-BIDS places only channels named exactly like an electrodes.tsv row, so these channels had no position although both contacts are positioned.

resolve_channel_positions() is the one rule the digest (stored montages, scripts/ingestions/_montage.py) and the runtime (fill_missing_positions()) share:

  1. A channel named exactly like a contact is that contact.

  2. Otherwise a name names one contact by lookup only: case and white space are ignored, and a leading POL/EEG type word may be dropped. Two contacts that match the same name this way make it ambiguous: no position.

  3. <contact>-Ref is that contact, when Ref is not itself a contact.

  4. A pair <A>-<B> sits at the midpoint of A and B. Every - is tried as the separator; exactly one split must name two distinct contacts, both positioned. An unknown, n/a or ambiguous endpoint, or several possible splits, leave the channel unpositioned.

The midpoint is in the electrodes’ own coordinate system and units, and the distance between the two contacts is kept with it. Nothing is written back to electrodes.tsv, no contact or anatomy is invented, channel names are not changed and the signal is not re-referenced.

eegdash.dataset.channel_positions.BIPOLAR_MIDPOINT = 'bipolar_midpoint'

position_source of a channel placed at the midpoint of two contacts.

class eegdash.dataset.channel_positions.ChannelPosition(xyz: tuple[float, float, float], source: str, contacts: tuple[str, ...], contact_distance: float | None = None)[source]

Bases: object

Where a channel sits, and which contacts that position comes from.

contact_distance: float | None = None
contacts: tuple[str, ...]
provenance() → dict[str, Any][source]

position_source, contacts and, for a pair, contact_distance.

source: str
xyz: tuple[float, float, float]
eegdash.dataset.channel_positions.ELECTRODE_ALIAS = 'electrode_alias'

position_source of a channel that names one contact under the lookup rules.

eegdash.dataset.channel_positions.MEASURED = 'measured'

position_source of a channel named exactly like a contact.

eegdash.dataset.channel_positions.derived_positions(ch_names: Sequence[str], electrodes: Mapping[str, Any]) → dict[str, ChannelPosition][source]

resolve_channel_positions() without the exactly named (measured) channels.

eegdash.dataset.channel_positions.fill_missing_positions(raw, electrodes_tsv: Path, coordsystem_json: Path, datatype: str = 'ieeg') → dict[str, dict[str, Any]][source]

Give channels without a position the one the module rules derive.

Only channels with no position are touched; a measured position is never replaced. The contacts are placed as MNE-BIDS places them (same frame, units and transform). The provenance of every filled channel goes to raw.info["temp"]["derived_positions"].

Returns:

Channel name to its provenance, for the channels this filled.

Return type:

dict

eegdash.dataset.channel_positions.read_electrode_table(path: Path, encoding: str | None = None) → dict[str, tuple[float, float, float] | None][source]

Every name of an electrodes.tsv with its (x, y, z), None when n/a.

A name listed twice has no single position, so it maps to None. A file without name/x/y/z columns gives {}.

eegdash.dataset.channel_positions.resolve_channel_positions(ch_names: Sequence[str], electrodes: Mapping[str, Any]) → dict[str, ChannelPosition][source]

Position of every channel the module rules place, keyed by channel name.

Parameters:
  • ch_names (sequence of str) – Channel names as the data file names them.

  • electrodes (mapping) – Every electrodes.tsv name to its (x, y, z), or to None for an n/a row. Unpositioned names still count when telling whether a name is ambiguous.

Returns:

Channel name to ChannelPosition; channels left out have no position.

Return type:

dict