-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[MRG] PCA flip for volumetric source estimates #13092
base: main
Are you sure you want to change the base?
Conversation
Extension of PCA flip in volumetric setting. Underlying logic is pretty much identical to cortical case with respect to flip vector creation and PCA itself Added a check in PCA flip in case the flip is None or number of vertices is below 2, in which case PCA will return a trivial estimate of the signal Complete with its own unit test case
I belive this PR is ready for review :) (although I do not know why pip pre is failing on Windows) |
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.
Found some cruft, can you take a look at the diff
on GitHub and make sure I found all of it?
Also with enhancements it's nice to see the effect in some example. Maybe you could modify this:
https://mne.tools/1.8/auto_examples/inverse/compute_mne_inverse_epochs_in_label.html
which comes from examples/inverse/compute_mne_inverse_epochs_in_label.py
could be modified to add a volume example to show its flip modes as well? Or maybe some other example like tutorials/inverse/60_visualize_stc.py
or examples/inverse/label_source_activations.py
?
# logger.info("Selected mode: " + mode) | ||
# print("Entering _prepare_label_extraction") | ||
# print("Selected mode: " + mode) |
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.
Cruft, should be removed. But really to help next time:
# logger.info("Selected mode: " + mode) | |
# print("Entering _prepare_label_extraction") | |
# print("Selected mode: " + mode) | |
logger.debug(f"Selected mode: {mode}") |
then if you run with verbose='debug'
you will see the statement
@@ -3445,6 +3456,7 @@ def _prepare_label_extraction(stc, labels, src, mode, allow_empty, use_sparse): | |||
|
|||
bad_labels = list() | |||
for li, label in enumerate(labels): | |||
# print("Mode: " + mode + " li: " + str(li) + " label: " + str(label)) |
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.
# print("Mode: " + mode + " li: " + str(li) + " label: " + str(label)) |
# print(f"src: {src[:2]}") | ||
# print(f"len(src): {len(src[:2])}") | ||
|
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.
# print(f"src: {src[:2]}") | |
# print(f"len(src): {len(src[:2])}") |
@@ -1460,22 +1460,47 @@ def label_sign_flip(label, src): | |||
flip : array | |||
Sign flip vector (contains 1 or -1). | |||
""" | |||
if len(src) != 2: | |||
raise ValueError("Only source spaces with 2 hemisphers are accepted") | |||
if len(src) > 2 or len(src) == 0: |
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 better / more modern check would be something like:
_validate_type(src, SourceSpaces, "src")
_check_option("source space kind", src.kind, ("volume", "surface"))
if src.kind == "volume" and len(src) != 1:
raise ValueError("Only single-segment volumes, are supported, got labelized volume source space")
And incidentally I think eventually we could add support for segmented volume source spaces, as well as mixed source spaces (once surface + volume are fully supported, mixed isn't too bad after that). But probably not as part of this PR!
if isbi_hemi: | ||
lh_id = 0 | ||
rh_id = 1 | ||
lh_vertno = src[0]["vertno"] | ||
rh_vertno = src[1]["vertno"] | ||
elif label.hemi == "lh": | ||
lh_vertno = src[0]["vertno"] | ||
elif label.hemi == "rh": | ||
rh_id = 0 | ||
rh_vertno = src[0]["vertno"] | ||
else: | ||
raise Exception(f'Unknown hemisphere type "{label.hemi}"') |
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.
I don't totally follow this logic. It's a surface source space if and only if it's bihemi, and it's volume source space if and only if it's a single-element list. I think it would be cleaner to triage based on source space type probably...
# if flip is None: # Happens if fewer than 2 vertices in the label | ||
# if this_data.shape[] | ||
# label_tc[i] = 0 | ||
# else: |
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.
# if flip is None: # Happens if fewer than 2 vertices in the label | |
# if this_data.shape[] | |
# label_tc[i] = 0 | |
# else: |
@@ -1469,7 +1613,7 @@ def objective(x): | |||
assert_allclose(directions, want_nn, atol=2e-6) | |||
|
|||
|
|||
@testing.requires_testing_data | |||
# @testing.requires_testing_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.
# @testing.requires_testing_data | |
@testing.requires_testing_data |
modes = ("mean", "max") if vector else ("mean", "max") | ||
for mode in modes: |
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.
Revert?
What does this implement/fix?
This PR implements PCA flip in volumetric source estimates.
To do so, it relies on using the already existing _pca_flip method. _pca_flip has been slighly modified to check if at least two vertices are available within a label. If that is not the case, it returns trivially the original signal, as PCA is ill-defined in this case.
Additional information
Feel free to have a look and - of course - double-check for correctness!