I have found the issue. During creating the 2D custom layout, the API mne.channels.generate_2d_layout
expects the channel names to be a continuous strings without a space. For example, if the channel name is MAG 000
instead of MAG000
; plot_topo
doesn’t work (creates an empty figure).
This is the test code snippets! This can be run with MNE example data.
"""
Test case: create custom layout and plot topoplot of evoked data
@author: diptyajit das <bmedasdiptyajit@gmail.com>
Created on june 13th, 2023
"""
# modules
from pathlib import Path
import os
import numpy as np
import mne
# Create some dummy magnetometers data
n_channels = 102
sampling_freq = 1000 # in Hertz
info = mne.create_info(n_channels, sfreq=sampling_freq)
ch_names = [f"MAG{n:03}" for n in range(n_channels)]
ch_types = ["mag"] * n_channels
info = mne.create_info(ch_names, ch_types=ch_types, sfreq=sampling_freq)
info["description"] = "custom data"
print('info directory', info)
# add some random data to Raw array
times = np.linspace(0, 1, sampling_freq, endpoint=False)
data = np.random.rand(n_channels, len(times)) * 1e-12 # mag channels scale
custom_raw = mne.io.RawArray(data, info)
custom_raw.plot(block=True)
lout_ori = mne.channels.find_layout(custom_raw.info)
print('default custom layout', lout_ori)
# create dummy evoked
epochs = mne.make_fixed_length_epochs(custom_raw, duration=.5, preload=True)
evoked = epochs.average()
evoked = evoked.filter(None, 40.)
evoked.plot()
print('channel locations', evoked.info['chs']) # at the moment, no locations for MEG channel is present
# define meg sample data path
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, "MEG", "sample", "sample_audvis_raw.fif")
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.load_data()
raw = raw.copy().pick_types(meg='mag')
# add channel locations to custom evoked data from MNE sample data
for itype, ch in enumerate(evoked.info['ch_names']):
pos_mag = evoked.info['ch_names'].index(ch)
evoked.info['chs'][pos_mag]['loc'] = raw.info['chs'][pos_mag]['loc'] # add location
print('new channel locations', evoked.info['chs'])
evoked.plot()
# create a dummy custom layout, we will first extract xy coordinates from evoked data
info = evoked.info
chs = info["chs"]
pos = np.empty((len(chs), 2))
for ci, ch in enumerate(chs):
pos[ci] = ch["loc"][:2]
picks = mne.pick_channels(ch_names=info["ch_names"], include=[]).tolist()
# modify channel names for layout
mod_ch_names1 = [f"MAG {n:03}_v" for n in range(n_channels)] # error occurs
mod_ch_names2 = [f"MAG {n:03}" for n in range(n_channels)] # error occurs
# test cases
types = [ch_names, mod_ch_names1, mod_ch_names2]
for type in types:
dmaglout = mne.channels.generate_2d_layout(xy=pos, ch_names=type, ch_indices=picks,
name='custom_mag_layout', normalize=True)
# replace channel position with original layout ~ 2D custom layout doesn't match nicely with original layout,
# so we will be strict to original one for now.
# read the original layout
layout_dir = Path(mne.__file__).parent / "channels" / "data" / "layouts"
layouts = sorted(path.name for path in layout_dir.iterdir())
print("\n" "BUILT-IN LAYOUTS\n" "================")
print("\n".join(layouts))
Vectorview_layout = mne.channels.read_layout("Vectorview-mag")
dmaglout.pos = Vectorview_layout.pos[:n_channels]
print('modified custom layout', dmaglout)
dmaglout.plot()
# now we will try to plot topomap for evoked with custom layout with changed channel names
evoked.plot_topo(layout=dmaglout)
@larsoner Is it possible to add a warning massage or change the doc strings to recommend a specific naming for the channels?