From d41c9727e1524e9a44657fcc5bfd5ba5e3ee4fcc Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 7 Aug 2020 11:18:11 -0400 Subject: [PATCH] MAINT: Go back to OrderedDict --- mne/annotations.py | 3 +- mne/channels/_standard_montage_utils.py | 15 ++++----- mne/channels/montage.py | 41 +++++++++++++------------ mne/datasets/utils.py | 3 +- mne/io/what.py | 4 ++- mne/tests/test_annotations.py | 3 +- mne/utils/mixin.py | 4 +-- 7 files changed, 40 insertions(+), 33 deletions(-) diff --git a/mne/annotations.py b/mne/annotations.py index 94d63e2c9dd..9fb4fa014a0 100644 --- a/mne/annotations.py +++ b/mne/annotations.py @@ -2,6 +2,7 @@ # # License: BSD (3-clause) +from collections import OrderedDict from datetime import datetime, timedelta, timezone import os.path as op import re @@ -249,7 +250,7 @@ def __getitem__(self, key): out_keys = ('onset', 'duration', 'description', 'orig_time') out_vals = (self.onset[key], self.duration[key], self.description[key], self.orig_time) - return dict(zip(out_keys, out_vals)) + return OrderedDict(zip(out_keys, out_vals)) else: key = list(key) if isinstance(key, tuple) else key return Annotations(onset=self.onset[key], diff --git a/mne/channels/_standard_montage_utils.py b/mne/channels/_standard_montage_utils.py index af5e129f846..5997886fc95 100644 --- a/mne/channels/_standard_montage_utils.py +++ b/mne/channels/_standard_montage_utils.py @@ -2,6 +2,7 @@ # Alexandre Gramfort # # License: BSD (3-clause) +from collections import OrderedDict import os.path as op import numpy as np @@ -90,7 +91,7 @@ def _mgh_or_standard(basename, head_size): ch_names_.append(line.strip(' ').strip('\n')) pos = np.array(pos) - ch_pos = dict(zip(ch_names_, pos)) + ch_pos = OrderedDict(zip(ch_names_, pos)) nasion, lpa, rpa = [ch_pos.pop(n) for n in fid_names] scale = head_size / np.median(np.linalg.norm(pos, axis=1)) for value in ch_pos.values(): @@ -151,7 +152,7 @@ def _read_sfp(fname, head_size): ch_names, xs, ys, zs = _safe_np_loadtxt(fname, **options) pos = np.stack([xs, ys, zs], axis=-1) - ch_pos = dict(zip(ch_names, pos)) + ch_pos = OrderedDict(zip(ch_names, pos)) # no one grants that fid names are there. nasion, lpa, rpa = [ch_pos.pop(n, None) for n in fid_names] @@ -177,7 +178,7 @@ def _read_csd(fname, head_size): if head_size is not None: pos *= head_size / np.median(np.linalg.norm(pos, axis=1)) - return make_dig_montage(ch_pos=dict(zip(ch_names, pos))) + return make_dig_montage(ch_pos=OrderedDict(zip(ch_names, pos))) def _read_elc(fname, head_size): @@ -225,7 +226,7 @@ def _read_elc(fname, head_size): if head_size is not None: pos *= head_size / np.median(np.linalg.norm(pos, axis=1)) - ch_pos = dict(zip(ch_names_, pos)) + ch_pos = OrderedDict(zip(ch_names_, pos)) nasion, lpa, rpa = [ch_pos.pop(n, None) for n in fid_names] return make_dig_montage(ch_pos=ch_pos, coord_frame='unknown', @@ -251,7 +252,7 @@ def _read_theta_phi_in_degrees(fname, head_size, fid_names=None, radii = np.full(len(phi), head_size) pos = _sph_to_cart(np.array([radii, np.deg2rad(phi), np.deg2rad(theta)]).T) - ch_pos = dict(zip(ch_names, pos)) + ch_pos = OrderedDict(zip(ch_names, pos)) nasion, lpa, rpa = None, None, None if fid_names is not None: @@ -282,7 +283,7 @@ def _read_elp_besa(fname, head_size): if head_size is not None: pos *= head_size / np.median(np.linalg.norm(pos, axis=1)) - ch_pos = dict(zip(ch_names, pos)) + ch_pos = OrderedDict(zip(ch_names, pos)) fid_names = ('Nz', 'LPA', 'RPA') # No one grants that the fid names actually exist. @@ -310,4 +311,4 @@ def _read_brainvision(fname, head_size): if head_size is not None: pos *= head_size / np.median(np.linalg.norm(pos, axis=1)) - return make_dig_montage(ch_pos=dict(zip(ch_names, pos))) + return make_dig_montage(ch_pos=OrderedDict(zip(ch_names, pos))) diff --git a/mne/channels/montage.py b/mne/channels/montage.py index 01fd18cc934..de766f01812 100644 --- a/mne/channels/montage.py +++ b/mne/channels/montage.py @@ -11,10 +11,11 @@ # # License: Simplified BSD -import os.path as op -import re +from collections import OrderedDict from copy import deepcopy from functools import partial +import os.path as op +import re import numpy as np @@ -289,7 +290,7 @@ def __add__(self, other): def _get_ch_pos(self): pos = [d['r'] for d in _get_dig_eeg(self.dig)] assert len(self.ch_names) == len(pos) - return dict(zip(self.ch_names, pos)) + return OrderedDict(zip(self.ch_names, pos)) def _get_dig_names(self): NAMED_KIND = (FIFF.FIFFV_POINT_EEG,) @@ -302,9 +303,12 @@ def _get_dig_names(self): return dig_names -def _check_unit_and_get_scaling(unit, valid_scales): - _check_option('unit', unit, list(valid_scales.keys())) - return valid_scales[unit] +VALID_SCALES = dict(mm=1e-3, cm=1e-2, m=1) + + +def _check_unit_and_get_scaling(unit): + _check_option('unit', unit, sorted(VALID_SCALES.keys())) + return VALID_SCALES[unit] def transform_to_head(montage): @@ -514,8 +518,7 @@ def read_dig_hpts(fname, unit='mm'): """ from ._standard_montage_utils import _str_names, _str - VALID_SCALES = dict(mm=1e-3, cm=1e-2, m=1) - _scale = _check_unit_and_get_scaling(unit, VALID_SCALES) + _scale = _check_unit_and_get_scaling(unit) out = np.genfromtxt(fname, comments='#', dtype=(_str, _str, 'f8', 'f8', 'f8')) @@ -705,7 +708,8 @@ def _backcompat_value(pos, ref_pos): info_names_use = info_names dig_names_use = dig_names else: - ch_pos_use = {name.lower(): pos for name, pos in ch_pos.items()} + ch_pos_use = OrderedDict( + (name.lower(), pos) for name, pos in ch_pos.items()) info_names_use = [name.lower() for name in info_names] dig_names_use = [name.lower() if name is not None else name for name in dig_names] @@ -738,15 +742,14 @@ def _backcompat_value(pos, ref_pos): logger.info(missing_coord_msg) # set ch coordinates and names from digmontage or nan coords - _ch_pos_use = dict(ch_pos_use) # make a copy - for name in info_names: - if name not in ch_pos_use: - _ch_pos_use[name] = [np.nan, np.nan, np.nan] - ch_pos_use = _ch_pos_use + ch_pos_use = dict( + (name, ch_pos_use.get(name, [np.nan] * 3)) + for name in info_names) # order does not matter here for name, use in zip(info_names, info_names_use): _loc_view = info['chs'][info['ch_names'].index(name)]['loc'] _loc_view[:6] = _backcompat_value(ch_pos_use[use], eeg_ref_pos) + del ch_pos_use # XXX this is probably wrong as it uses the order from the montage # rather than the order of our info['ch_names'] ... @@ -868,8 +871,7 @@ def read_dig_polhemus_isotrak(fname, ch_names=None, unit='m'): read_dig_fif """ VALID_FILE_EXT = ('.hsp', '.elp', '.eeg') - VALID_SCALES = dict(mm=1e-3, cm=1e-2, m=1) - _scale = _check_unit_and_get_scaling(unit, VALID_SCALES) + _scale = _check_unit_and_get_scaling(unit) _, ext = op.splitext(fname) _check_option('fname', ext, VALID_FILE_EXT) @@ -892,7 +894,7 @@ def read_dig_polhemus_isotrak(fname, ch_names=None, unit='m'): else: points = data.pop('points') if points.shape[0] == len(ch_names): - data['ch_pos'] = dict(zip(ch_names, points)) + data['ch_pos'] = OrderedDict(zip(ch_names, points)) else: raise ValueError(( "Length of ``ch_names`` does not match the number of points" @@ -938,8 +940,7 @@ def read_polhemus_fastscan(fname, unit='mm'): make_dig_montage """ VALID_FILE_EXT = ['.txt'] - VALID_SCALES = dict(mm=1e-3, cm=1e-2, m=1) - _scale = _check_unit_and_get_scaling(unit, VALID_SCALES) + _scale = _check_unit_and_get_scaling(unit) _, ext = op.splitext(fname) _check_option('fname', ext, VALID_FILE_EXT) @@ -1031,7 +1032,7 @@ def read_custom_montage(fname, head_size=HEAD_SIZE_DEFAULT, coord_frame=None): pos *= scale montage = make_dig_montage( - ch_pos=dict(zip(ch_names, pos)), + ch_pos=OrderedDict(zip(ch_names, pos)), coord_frame='head', ) diff --git a/mne/datasets/utils.py b/mne/datasets/utils.py index 5e74c10e912..2908601a553 100644 --- a/mne/datasets/utils.py +++ b/mne/datasets/utils.py @@ -5,6 +5,7 @@ # Stefan Appelhoff # License: BSD Style. +from collections import OrderedDict import os import os.path as op import shutil @@ -701,7 +702,7 @@ def fetch_hcp_mmp_parcellation(subjects_dir=None, combine=True, verbose=None): return # otherwise, let's make them logger.info('Creating combined labels') - groups = dict([ + groups = OrderedDict([ ('Primary Visual Cortex (V1)', ('V1',)), ('Early Visual Cortex', diff --git a/mne/io/what.py b/mne/io/what.py index 2fa02ce5474..5f9efbb0698 100644 --- a/mne/io/what.py +++ b/mne/io/what.py @@ -3,6 +3,8 @@ # # License: BSD (3-clause) +from collections import OrderedDict + from ..fixes import _get_args from ..utils import _check_fname, logger @@ -38,7 +40,7 @@ def what(fname): from ..proj import read_proj from .meas_info import read_fiducials _check_fname(fname, overwrite='read', must_exist=True) - checks = dict() + checks = OrderedDict() checks['raw'] = read_raw_fif checks['ica'] = read_ica checks['epochs'] = read_epochs diff --git a/mne/tests/test_annotations.py b/mne/tests/test_annotations.py index 0c35b0cd146..5d2791a02ea 100644 --- a/mne/tests/test_annotations.py +++ b/mne/tests/test_annotations.py @@ -2,6 +2,7 @@ # # License: BSD 3 clause +from collections import OrderedDict from datetime import datetime, timezone from itertools import repeat import sys @@ -967,7 +968,7 @@ def test_annotations_simple_iteration(): orig_time=None) for ii, elements in enumerate(annot[:2]): - assert isinstance(elements, dict) + assert isinstance(elements, OrderedDict) expected_values = (ii, ii, str(ii)) for elem, expected_type, expected_value in zip(elements.values(), EXPECTED_ELEMENTS_TYPE, diff --git a/mne/utils/mixin.py b/mne/utils/mixin.py index 05e2cf8eb33..ebc416e94d0 100644 --- a/mne/utils/mixin.py +++ b/mne/utils/mixin.py @@ -4,7 +4,7 @@ # # License: BSD (3-clause) - +from collections import OrderedDict from copy import deepcopy import logging import json @@ -413,7 +413,7 @@ def _prepare_read_metadata(metadata): pd = _check_pandas_installed(strict=False) # use json.loads because this preserves ordering # (which is necessary for round-trip equivalence) - metadata = json.loads(metadata, object_pairs_hook=dict) + metadata = json.loads(metadata, object_pairs_hook=OrderedDict) assert isinstance(metadata, list) if pd: metadata = pd.DataFrame.from_records(metadata)