find_ch_adjacency() not accounting for bad channels?

Dear Forum,

I was trying to do cluster-based permutation tests on single-subject sensor-space epoched data, and encountered an error relating to the adjacency matrix saying that “adjacency (len X) must be of the correct size, i.e. equal to or evenly divide the number of tests (Y)”.

A quick inspection suggests that the adjacency matrix created by channels.find_ch_adjacency(, ch_type=‘grad’) was a 204x204 sparse matrix, despite that had only 199 good grad channels. Also X/Y=204/199.

Could any expert here tell me if this is the source of the size error? Is the function creating the adjacency matrix from fixed templates? If so, what is the best practice to fix the error–should I use a different method to create an adjacency matrix that only includes good channels, or should I include/interpolate bad channels in the epoched data? How should I deal with this issue when doing such tests at the group level?

Many thanks,

If I am not mistaken, the adjacency doesn’t take account the ‘bads’ electrode. If you want, for some reason, to definitely get rid of these electrodes you should call the method drop_channels from your mne instance you created (either if it’s a object or mne.Epochs object or other).
If you want to perform a group study, (for example if you want to call mne.concatenate_epochs to concatenate your mne.Epochs objects of different subjects) you need to have the same number of electrodes across subjects (MNE will indicate you that).
So yes you should interpolate the bad channels.


Thank you @Sam54000 for the quick response!

However, I tried dropping one arbitrary channel from my epoch instance and it didn’t change the dimension of the adjacency matrix created from the epochs. I vaguely remember that in Fieldtrip there’s a way to manually create an adjacency matrix using channel indices. Is there such a method in MNE python as well (I’m very new to Python)? Or is it better to interpolate bad channels (and why)?



I think the adjacency matrix for MEGIN system is read from a template, which is why dropping a random channel did not impact it (maybe it should?).

@drammock @richard Could one of you have a look to that question, else I will pick it up in mid-December.


1 Like

Hello @yaqing

It looks like the adjacency matrix is indeed loaded from a file. If you set the log level to INFO, find_ch_adjacency logs:

from mne import set_log_level
from mne.channels import find_ch_adjacency

adj, ch_names = find_ch_adjacency(, picks="grad")

Reading adjacency matrix for neuromag306planar.

You can list the builtin adjacency matrix with get_builtin_ch_adjacencies and load them with read_ch_adjacency directly, which allows a channel selection.

from mne import pick_types
from mne.channels import read_ch_adjacency

raw = ...["bads"] = ["MEG0632", "MEG0642", "MEG0643"]  # gradiometers
adj, ch_names = read_ch_adjacency("neuromag306planar")  # full, similar to find_ch_adjacency
assert adj.shape == (204, 204)
idx = pick_types(, meg="grad", exclude="bads")
adj, ch_names = read_ch_adjacency("neuromag306planar", picks=[ch for k, ch in enumerate(raw.ch_names) if k in idx])
assert adj.shape == (201, 201)
assert not any(ch in ch_names for ch in["bads"])

It’s not ideal, the description of the picks argument read_ch_adjacency is erroneous and find_ch_adjacency should probably have some parameters to allow pick/exclusion of bad channels.
Issue opened here: Channel adjacency improvements · Issue #12292 · mne-tools/mne-python · GitHub

That said, if in the end your analysis requires the same number of channels between all subjects, you should interpolate your bad sensors.


1 Like

It’s clear to me now, thank you!