Similarity & RSA
Representational similarity analysis has two halves: turn brain patterns into a representational
dissimilarity matrix (RDM), and compare that RDM to a model RDM.
BrainData.distance does the first and
returns an Adjacency, a square matrix stored as its upper triangle.
Adjacency.similarity does the second.
Two kwargs are easy to mix up. metric= is the correlation type used to compare the two matrices
('spearman' by default, or 'pearson' / 'kendall'). method= is the permutation scheme:
'2d' shuffles rows and columns together (the Mantel test, the right null for a symmetric RDM),
'1d' shuffles the vectorized entries, and None skips the test entirely.
| Goal | Use | Notes |
|---|---|---|
| Brain RDM | BrainData.distance(metric='correlation') |
Any scipy metric; 'euclidean' is the default |
| Per-ROI or per-searchlight RDMs | distance(..., spatial_scale='roi', roi_mask=) or 'searchlight', radius= |
Returns an ordinary stack in explicit ROI or voxel order |
| Model RDM | Adjacency(square_matrix, matrix_type='distance') |
'similarity' and 'directed' are the other types |
| Compare two RDMs | Adjacency.similarity(other, metric=, method='2d') |
Returns {'correlation', 'p', ...}; a stack returns a list |
| Paint a stack's result on the brain | roi_to_brain_from_atlas(...) or nilearn.masking.unmask(...) |
Retain the aligned atlas/ROI order or source-mask voxel order |
| Compare two raw matrices | matrix_permutation_test |
The Mantel test on plain arrays; include_diag=False by default |
| Row-wise pattern similarity | compute_similarity |
One image against many; metric='correlation', 'spearman', 'cosine', 'dot_product' |
| Average correlations | fisher_r_to_z / fisher_z_to_r |
Also Adjacency.r_to_z / .z_to_r, returning independent copies |
| Show two RDMs together | similarity(plot=True) |
Brain RDM above the diagonal, model RDM below |
| Cluster structure | plot_mds, plot_silhouette, plot_label_distance |
All take labels= |
Brain RDM vs model RDM¶
from nltools.data import Adjacency
brain_rdm = brain.distance(metric="correlation")
model_rdm = Adjacency(
np.abs(levels[:, None] - levels[None, :]), matrix_type="distance"
)
stats = brain_rdm.similarity(
model_rdm, metric="spearman", n_permute=1000, random_state=0
)
stats["correlation"], stats["p"]
plot=True draws the picture that goes with that number: one matrix above the diagonal and the
other below on a shared scale, so you can see where the two agree, not just how much.
Both matrices are rescaled before stacking, which matters whenever the two are on different scales, say a correlation-distance brain RDM against a model RDM in stimulus units.
Per-ROI RSA, painted back on the brain¶
Give distance a labeled parcellation and it returns one RDM per parcel.
Align the atlas to the brain mask first and keep the sorted nonzero labels
present within that mask. This is the stack order used by distance:
from nilearn.image import resample_to_img
from nilearn.masking import apply_mask
from nltools.mask import roi_to_brain_from_atlas
aligned_atlas = resample_to_img(
atlas, brain.mask, interpolation="nearest", force_resample=True, copy_header=True,
)
roi_labels = np.unique(apply_mask(aligned_atlas, brain.mask).astype(int))
roi_labels = roi_labels[roi_labels != 0]
rdms = brain.distance(metric="correlation", spatial_scale="roi", roi_mask=aligned_atlas)
scores = rdms.similarity(model_rdm, metric="spearman", method=None)
brain_map = roi_to_brain_from_atlas(
np.array([score["correlation"] for score in scores]),
atlas=aligned_atlas, source_mask=brain.mask, roi_labels=roi_labels,
)
similarity returns one result dict per parcel. If you select or reorder matrices,
apply the same selection to roi_labels before painting their values.
spatial_scale="searchlight" returns one matrix per masked voxel in source-mask
order. Map its per-center scores with nilearn.masking.unmask(values, brain.mask);
for a selected subset, restore the selected positions in a full mask-length
vector first. Adjacency carries no spatial mapping state.
Raw arrays¶
Every method above has a function underneath that takes plain numpy:
from nltools.algorithms import compute_similarity, fisher_r_to_z, matrix_permutation_test
r = compute_similarity(brain.data[0], brain.data[1:], metric="correlation")
fisher_r_to_z(r)
matrix_permutation_test(
brain_rdm.squareform(), model_rdm.squareform(),
n_permute=1000, metric="spearman", random_state=0,
)
Next: Statistics & inference, or the MVPA tutorial, which runs RSA at all three spatial scales.