Skip to content

Commit

Permalink
rename names to ch_names
Browse files Browse the repository at this point in the history
update manifest.in
some cleanup (s/layout/montage etc.)
  • Loading branch information
agramfort committed Oct 31, 2014
1 parent 9a186b4 commit d1e322d
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 52 deletions.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ recursive-include mne *.py
recursive-include mne/data *.dat
recursive-include mne/data *.sel
recursive-include mne/data *.fif.gz
recursive-include mne/montages *.elc
recursive-include mne/montages *.txt
recursive-include mne/montages *.csd
recursive-include mne/montages *.sfp
recursive-include mne/layouts *.lout
recursive-include mne/layouts *.lay
recursive-include mne/html *.js
Expand Down
4 changes: 2 additions & 2 deletions examples/plot_read_apply_eeg_montage.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""
==================================
Reading and applyting EEG Montages
Reading and applying EEG Montages
==================================
"""
# Author: Denis Engemann <dneis[email protected]>
# Author: Denis Engemann <denis[email protected]>
#
# License: BSD (3-clause)

Expand Down
4 changes: 3 additions & 1 deletion mne/montages/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from montage import read_montage, apply_montage
"""Module to handle EEG montages"""

from .montage import read_montage, apply_montage
97 changes: 52 additions & 45 deletions mne/montages/montage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Authors: Denis Engemann <[email protected]>
# Marijn van Vliet <[email protected]>
# Alexandre Gramfort <[email protected]>
#
# License: Simplified BSD

Expand All @@ -12,29 +14,31 @@


class Montage(object):
"""Sensor layouts
"""Montage for EEG cap
Montages are typically loaded from a file using read_montage. Only use this
class directly if you're constructing a new layout.
class directly if you're constructing a new montage.
Parameters
----------
pos : array, shape (n_channels, 3)
The positions of the channels in 3d.
names : list
The channel names
ch_names : list
The channel names.
kind : str
The type of Layout (e.g. 'standard_1005')
The type of montage (e.g. 'standard_1005').
selection : array of int
The indices of the selected channels in the montage file.
"""
def __init__(self, pos, names, kind, ids):
def __init__(self, pos, ch_names, kind, selection):
self.pos = pos
self.names = names
self.ch_names = ch_names
self.kind = kind
self.ids = ids
self.selection = selection

def __repr__(self):
s = '<Montage | %s - Channels: %s ...>' % (self.kind,
', '.join(self.names[:3]))
s = '<Montage | %s - %d Channels: %s ...>'
s %= self.kind, len(self.ch_names), ', '.join(self.ch_names[:3])
return s

def plot(self, scale_factor=1.5, show_names=False):
Expand All @@ -56,25 +60,26 @@ def plot(self, scale_factor=1.5, show_names=False):
show_names=show_names)


def read_montage(kind, names=None, path=None, scale=True):
"""Read layout from a file
def read_montage(kind, ch_names=None, path=None, scale=True):
"""Read montage from a file
Parameters
----------
kind : str
The name of the .lout file (e.g. kind='Vectorview-all' for
'Vectorview-all.lout'
names : list of str
The name of the montage file (e.g. kind='easycap-M10' for
'easycap-M10.txt'). Files with extensions '.elc', '.txt', '.csd'
or '.sfp' are supported.
ch_names : list of str
The names to read. If None, all names are returned.
path : str | None
The path of the folder containing the Layout file
The path of the folder containing the montage file
scale : bool
Apply useful scaling for out the box plotting using layout.pos
Apply useful scaling for out the box plotting using montage.pos
Returns
-------
layout : instance of Layout
The layout
montage : instance of Montage
The montage.
"""
if path is None:
path = op.dirname(__file__)
Expand All @@ -83,8 +88,8 @@ def read_montage(kind, names=None, path=None, scale=True):
montages = [op.splitext(f) for f in os.listdir(path)]
montages = [m for m in montages if m[1] in supported and kind == m[0]]
if len(montages) != 1:
raise ValueError('Could not find the montage. Please provide the'
'full path')
raise ValueError('Could not find the montage. Please provide the '
'full path.')
kind, ext = montages[0]
fname = op.join(path, kind + ext)
else:
Expand All @@ -96,10 +101,10 @@ def read_montage(kind, names=None, path=None, scale=True):
dtype = np.dtype('S4, f8, f8, f8')
data = np.loadtxt(fname, dtype=dtype)
pos = np.c_[data['f1'], data['f2'], data['f3']]
names_ = data['f0']
ch_names_ = data['f0']
elif ext == '.elc':
# 10-5 system
names_ = []
ch_names_ = []
pos = []
with open(fname) as fid:
for line in fid:
Expand All @@ -112,7 +117,7 @@ def read_montage(kind, names=None, path=None, scale=True):
for line in fid:
if not line or not set(line) - set([' ']):
break
names_.append(line.strip(' ').strip('\n'))
ch_names_.append(line.strip(' ').strip('\n'))
pos = np.loadtxt(BytesIO(''.join(pos)))
elif ext == '.txt':
# easycap
Expand All @@ -123,59 +128,61 @@ def read_montage(kind, names=None, path=None, scale=True):
y = 85. * np.sin(np.deg2rad(theta)) * np.sin(np.deg2rad(phi))
z = 85. * np.cos(np.deg2rad(theta))
pos = np.c_[x, y, z]
names_ = data['f0']
ch_names_ = data['f0']
elif ext == '.csd':
# CSD toolbox
dtype = [('label', 'S4'), ('theta', 'f8'), ('phi', 'f8'),
('radius', 'f8'), ('x', 'f8'), ('y', 'f8'), ('z', 'f8'),
('off_sph', 'f8')]
table = np.loadtxt(fname, skiprows=2, dtype=dtype)
pos = np.c_[table['x'], table['y'], table['z']]
names_ = table['label']
ch_names_ = table['label']
else:
raise ValueError('Currently the "%s" template is not supported.' %
kind)
ids = np.arange(len(pos))
if names is not None:
sel, names_ = zip(*[(i, e) for i, e in enumerate(names_)
if e in names])
selection = np.arange(len(pos))
if ch_names is not None:
sel, ch_names_ = zip(*[(i, e) for i, e in enumerate(ch_names_)
if e in ch_names])
sel = list(sel)
pos = pos[sel]
ids = ids[sel]
selection = selection[sel]
else:
names_ = list(names_)
ch_names_ = list(ch_names_)
kind = op.split(kind)[-1]
return Montage(pos=pos, names=names_, kind=kind, ids=ids)
return Montage(pos=pos, ch_names=ch_names_, kind=kind, selection=selection)


def apply_montage(info, montage):
"""Apply montage to EEG data.
This function will replace the eeg channel names and locations with
This function will replace the EEG channel names and locations with
the values specified for the particular montage.
Note. You have to rename your object to correclty map
Note: You have to rename your object to correclty map
the montage names.
Note. This function will change the info in place.
Note: This function will change the info variable in place.
Parameters
----------
inst : instance of Info
The info to update.
info : instance of Info
The measurement info to update.
montage : instance of Montage
The montage to apply.
"""
if not _contains_ch_type(info, 'eeg'):
raise ValueError('No eeg channels found')
raise ValueError('No EEG channels found.')

sensors_found = False
for pos, name in zip(montage.pos, montage.names):
if name not in info['ch_names']:
for pos, ch_name in zip(montage.pos, montage.ch_names):
if ch_name not in info['ch_names']:
continue

ch_idx = info['ch_names'].index(name)
info['ch_names'][ch_idx] = name
info['chs'][ch_idx]['eeg_loc'] = np.c_[pos, [0] * 3]
info['chs'][ch_idx]['loc'] = np.r_[pos, [0] * 9]
ch_idx = info['ch_names'].index(ch_name)
info['ch_names'][ch_idx] = ch_name
info['chs'][ch_idx]['eeg_loc'] = np.c_[pos, [0.] * 3]
info['chs'][ch_idx]['loc'] = np.r_[pos, [0.] * 9]
sensors_found = True

if not sensors_found:
Expand Down
8 changes: 4 additions & 4 deletions mne/montages/tests/test_montage.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ def test_montage():
with open(fname, 'w') as fid:
fid.write(text)
montage = read_montage(fname)
assert_equal(len(montage.names), 3)
assert_equal(len(montage.names), len(montage.pos))
assert_equal(len(montage.ch_names), 3)
assert_equal(len(montage.ch_names), len(montage.pos))
assert_equal(montage.pos.shape, (3, 3))
assert_equal(montage.kind, kind[:-4])
# test with last
info = create_info(montage.names, 1e3, ['eeg'] * len(montage.names))
info = create_info(montage.ch_names, 1e3, ['eeg'] * len(montage.ch_names))
apply_montage(info, montage)
pos2 = np.array([c['loc'][:3] for c in info['chs']])
pos3 = np.array([c['eeg_loc'][:, 0] for c in info['chs']])
assert_array_equal(pos2, montage.pos)
assert_array_equal(pos3, montage.pos)
assert_equal(montage.names, info['ch_names'])
assert_equal(montage.ch_names, info['ch_names'])

0 comments on commit d1e322d

Please sign in to comment.