-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
ENH: Add function to plot statistical clusters on brain surface #13366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Comments rearranged Docstring updated Code cleanup
Thanks for the contribution! For a big change like this, it's usually best to open an issue first to discuss the API. We'll discuss this one at our next maintainer meeting and report back. Meanwhile: if I view the last image in the rendered tutorial (https://output.circle-artifacts.com/output/job/77fa0002-c504-4110-a95d-0a57460ffe05/artifacts/0/html/auto_tutorials/stats-source-space/20_cluster_1samp_spatiotemporal.html#visualize-the-clusters), there doesn't appear to be any magenta-colored boundary on the brain? So not clear that this function is actually working as intended. |
Apologies. I will open issue next time. :)
I have updated this part with a new cluster index, the boundary is visible now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good start! Now we need the unit test to test different scenarios:
- Explicitly check that the proper vertices are included in the label
- Plotting multiple clusters and checking the auto-generated names
- Selecting different time points and checking that the cluster boundaries change
doc/changes/dev/13366.newfeature.rst
Outdated
@@ -0,0 +1 @@ | |||
Make :func:`~mne.viz.plot_stat_cluster` that plots spatial extent of a cluster on top of a brain by `Shristi Baral`_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make :func:`~mne.viz.plot_stat_cluster` that plots spatial extent of a cluster on top of a brain by `Shristi Baral`_. | |
Add :func:`~mne.viz.plot_stat_cluster` that plots the spatial extent of a cluster on top of a brain by `Shristi Baral`_. |
"A cluster is a tuple of two elements, a list time indices " | ||
"and list of vertex indices." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"A cluster is a tuple of two elements, a list time indices " | |
"and list of vertex indices." | |
"A cluster is a tuple of two elements: an array of time indices " | |
"and an array of vertex indices." |
mne/viz/_3d.py
Outdated
# Let's create an anatomical label containing these vertex indices. | ||
# Problem 1): a label must be defined for either the left or right hemisphere. It | ||
# cannot span both hemispheres. So we must filter the vertices based on their | ||
# hemisphere. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Let's create an anatomical label containing these vertex indices. | |
# Problem 1): a label must be defined for either the left or right hemisphere. It | |
# cannot span both hemispheres. So we must filter the vertices based on their | |
# hemisphere. | |
# Create the anatomical label containing the vertex indices belonging to the cluster. | |
# A label cannot span both hemispheres. So we must filter the vertices based on their | |
# hemisphere. |
mne/viz/_3d.py
Outdated
# Problem 2): We have vertex *indices* that need to be transformed into proper | ||
# vertex numbers. Not every vertex in the original high-resolution brain mesh is a | ||
# source point in the source estimate. Do draw nice smooth curves, we need to | ||
# interpolate the vertex indices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Problem 2): We have vertex *indices* that need to be transformed into proper | |
# vertex numbers. Not every vertex in the original high-resolution brain mesh is a | |
# source point in the source estimate. Do draw nice smooth curves, we need to | |
# interpolate the vertex indices. | |
# Transform vertex indices into proper vertex numbers. | |
# Not every vertex in the original high-resolution brain mesh is a | |
# source point in the source estimate. Do draw nice smooth curves, we need to | |
# interpolate the vertex indices. |
mne/viz/tests/test_3d.py
Outdated
@testing.requires_testing_data | ||
def test_plot_stat_cluster(renderer_interactive): | ||
"""Test plotting clusters on brain in static and interactive mode.""" | ||
pytest.importorskip("nibabel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is nibabel necessary?
mne/viz/tests/test_3d.py
Outdated
for key in ("lh", "rh"): | ||
for attr, desc in [ | ||
("labels", "brain.labels"), | ||
("_hemis", "brain._hemis"), | ||
("_layered_meshes", "brain._layered_meshes"), | ||
]: | ||
if key not in getattr(brain, attr): | ||
missing.append(f"{key} is missing from '{desc}'") | ||
if not brain._subject: | ||
missing.append("Subject name is missing from brain._subject") | ||
if not brain._subjects_dir: | ||
missing.append("Subject directory path is missing from brain._subjects_dir") | ||
if brain._times is None or brain._times.size == 0: | ||
missing.append("Time is missing from brain._times") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for key in ("lh", "rh"): | |
for attr, desc in [ | |
("labels", "brain.labels"), | |
("_hemis", "brain._hemis"), | |
("_layered_meshes", "brain._layered_meshes"), | |
]: | |
if key not in getattr(brain, attr): | |
missing.append(f"{key} is missing from '{desc}'") | |
if not brain._subject: | |
missing.append("Subject name is missing from brain._subject") | |
if not brain._subjects_dir: | |
missing.append("Subject directory path is missing from brain._subjects_dir") | |
if brain._times is None or brain._times.size == 0: | |
missing.append("Time is missing from brain._times") | |
# Check that the proper anatomical label has been constructed. | |
assert len(brain.labels["lh"] == 1 | |
assert len(brain.labels["rh"] == 0 | |
assert brain.labels["lh"][0].name == "cluster-0" |
In unit tests, it is not necessary to produce user-friendly error messages and you can just assert
. Also, I think we can get away with just verifying the label is there and not bother with checking other Brain
related functionality such as the presence of _hemis
and _layered_meshes
as those things are tested in other unit tests.
n_time = 5 | ||
n_verts = sum(len(v) for v in vertices) | ||
|
||
# simulate stc data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this test, I don't think it's actually needed to have STC data, we could just do:
brain = Brain("sample", subjects_dir=subjects_dir, surface="white")
# Alternatively, you may wish to observe clusters are considered statistically | ||
# significant under the permutation distribution with resect all the source estimates. | ||
# This can easily be done by plotting the cluster boundary on top of the source | ||
# estimates using the code snippet below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Alternatively, you may wish to observe clusters are considered statistically | |
# significant under the permutation distribution with resect all the source estimates. | |
# This can easily be done by plotting the cluster boundary on top of the source | |
# estimates using the code snippet below. | |
# Alternatively, you may wish to observe the spatial and temporal extent of | |
# single clusters. The code below demonstrates how to plot the cluster | |
# boundary on top of an existing source estimate. |
initial_time=0.1, | ||
) | ||
|
||
# We are plotting only one clusters here for illustration purpose. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# We are plotting only one clusters here for illustration purpose. | |
# Plot one cluster at the time of maximal spatial extent of that cluster. |
good_clusters[2], src, difference_plot, time="max-extent", color="magenta", width=1 | ||
) | ||
|
||
# Plotting the same cluster on the interactive mode for illustration purpose. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Plotting the same cluster on the interactive mode for illustration purpose. | |
# %% | |
Plotting the cluster in interactive mode allows scrolling through time. | |
Why wait? You can do so now :) |
WIP |
What does this implement/fix?
I improved the existing statistical cluster plot by adding a new function that plots the cluster boundary to its spatial extent on top of the brain.