Skip to content

add initial demo of iterative sphere coloring #11281

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

Merged
merged 18 commits into from
Nov 18, 2022

Conversation

timonmerk
Copy link
Contributor

Adresses #11214

What does this implement/fix?

This PR adds the sensor_colors keyword to the following functions:

  • mne.viz _3d.py plot_alignment()
  • mne.viz _3d.py _plot_sensors()

Within _plot_sensors() the function _plot_glyphs() is iteratively called to plot and color individually each sphere.

Additional information

An example is demonstrated in tutorials/clinical/30.ecog.py to color individual spheres.

I think there might be potential issues coming up with the actors list in the _3d.py _plot_sensors() function. I appended for each type a list of lists containing each actor. This might be problematic and break code for other scripts that I could not check. _plot_sensors() returns the actor dictionary, but it's not used in plot_alignment(). It would be great to get feedback on that.

Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's some initial feedback. Please also run make flake locally in the repo root, there are some style errors that I didn't comment on that should be fixed.

mne/viz/_3d.py Outdated
@@ -451,7 +451,7 @@ def plot_alignment(info=None, trans=None, subject=None, subjects_dir=None,
meg=None, eeg='original', fwd=None,
dig=False, ecog=True, src=None, mri_fiducials=False,
bem=None, seeg=True, fnirs=True, show_axes=False, dbs=True,
fig=None, interaction='terrain', verbose=None):
fig=None, interaction='terrain', sensor_colors=None, verbose=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason not to use colors instead of sensor_colors? Seems simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colors is already used in this function for plotting the surfaces, e.g. line 758, so I wanted to distinguish it from the other keyword.

mne/viz/_3d.py Outdated
Comment on lines 540 to 541
sensor_colors: np.array
Array with shape (3, num_sensors), default None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this should support any Matplotlib-compatible color, which means we shouldn't require shape (3, n_sensors). It should be possible to pass, e.g., ['red', 'blue', 'red', 'red', 'green', 'black', ...]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, I don't think this docstring matches what you're doing in the code... it looks like you expect shape (n_sensors, 3) not (3, n_sensors)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that comment, I will fix it in the next commit

mne/viz/_3d.py Outdated
@@ -1178,7 +1180,7 @@ def _plot_sensors(renderer, info, to_cf_t, picks, meg, eeg, fnirs,
warn_meg, head_surf, units, sensor_opacity=0.8,
orient_glyphs=False, scale_by_distance=False,
project_points=False, surf=None, check_inside=None,
nearest=None):
nearest=None, sensor_colors: np.array = None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not add type hints for just one param of one function. If we're going to start annotating we should first agree on a policy and approach. That approach might end up being "add them one param at a time as they get touched" but so far no concrete decision / plan is in place so let's not force our hand here.

@@ -115,9 +115,11 @@
# (along with xy positions of each electrode in the image), so that later
# we can plot frequency band power on top of it.

sensor_colors = np.random.random([len(raw.info["ch_names"]), 3])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding random colors is not really appropriate for a tutorial (which should generally show best practices, or at least sensible choices that most users would likely want to make). Can you instead demonstrate highlighting one or a few sensors in one or a few colors in order to make a particular point about the data?

@drammock
Copy link
Member

oops, sorry, I missed the fact that this was a draft PR and reviewed early! (Thanks for taking this on, by the way 🚀)

@timonmerk
Copy link
Contributor Author

@drammock Thanks a lot for the code review. I adapted the changes and added a small demo using the evoked gamma power as an example use for coloring the sensor spheres in 30_ecog.py

@timonmerk timonmerk marked this pull request as ready for review October 26, 2022 02:16
Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice improvements! A few more comments, then let's focus on getting the tests to pass. LMK if you need a refresher on how to run the tests locally and debug.

mne/viz/_3d.py Outdated
else:
color = sensor_colors[idx_sen, :]
actor, _ = _plot_glyphs(renderer=renderer,
loc=(sens_loc * scalar)[idx_sen, :],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some tests are failing with IndexError on this line. Can you figure out why? Offhand guess is that you're not accounting for bad channels, but that's just a guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drammock It would be helpful to know how to run the specific test. E.g. I see that the test test_notebook_alignment() in mne.viz._brain.tests fails due to an index error. How can I run this single test?

And is there optimally also an option for debugging within this specific test? When I put a breakpoint in VSCode in the specific line and try to debug, it won't jump to the breakpoint and does not seem to debug it correctly.

I tried to run pytest mne/viz/_brain/tests/test_notebook.py but it just skipped the test since it "Requires testing data".

Sorry for not being so proficient about using the testing framework. But I am very happy to learn more about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I read the documentation and installed the testing data. It is for me nevertheless confusing that I could not debug within the test:
image

So I can run the tests now, but VSCode does not stop at the breakpoint.
I tried to set the launch.json accordingly https://stackoverflow.com/a/60678160 and read through a related issue microsoft/vscode-python#693 but was not able to fix it.

Debugging would be probably quite necessary for this type of issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to run just one test and automatically enter debugger on error you can do pytest --pdb path/to/test/file.py -k name_of_test_function

timonmerk and others added 2 commits October 26, 2022 18:34
Co-authored-by: Daniel McCloy <[email protected]>
Co-authored-by: Daniel McCloy <[email protected]>
@larsoner
Copy link
Member

To run tests and have them debug on failure you can do from the command-line pytest mne/tests/test_whatever.py -k whatever_name --pdb for example. And then to stop at some specific point, you can do 1/0 or raise RuntimeError or whatever. It's not as clean as debug points directly in vscode but it works

@timonmerk
Copy link
Contributor Author

@drammock I had a look at the failing tests, and changed the code in such way that if the sensor_colors keyword is None, the code run's as before. In case it is not None I am iteratively plotting each sensor.
The tests should now pass except for the docstring test. I am not exactly sure how that one can be fixed though.

@drammock
Copy link
Member

drammock commented Nov 5, 2022 via email

mne/viz/_3d.py Outdated
else:
actor_list = []
for idx_sen in range(sens_loc.shape[0]):
if isinstance(sensor_colors, list):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is still fragile / too stringent. a tuple of color strings will fail this check, and will then error out on the else clause. I suggest something like this:

sensor_colors = np.asarray(sensor_colors)
if sensor_colors.ndim not in (1, 2) or sensor_colors.shape[0] != sens_loc.shape[0]:
    raise ValueError('insert error message about expected shape and actual shape we got')
color = sensor_colors[idx_sen]
# ...then the call to _plot_glyphs()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I improved the error message in my most recent commit.

@drammock
Copy link
Member

@timonmerk can you merge in current main here? There are some CI failures here that I think have already been fixed in main.

Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! just two nitpicks about the error message, and about avoiding backslash-line-continuation pattern (we prefer parentheses instead).

timonmerk and others added 3 commits November 16, 2022 22:48
@drammock
Copy link
Member

@timonmerk FYI the failing -pre tests are the result of matplotlib/matplotlib#24455, waiting to hear back from them on a recommended workaround.

@timonmerk
Copy link
Contributor Author

@drammock Thanks for following up on that issue! I was also unsure if that is something we should work on by building a workaround in MNE

@drammock
Copy link
Member

ok, only remaining CI failures are the MPL-related -pre CIs, so in it goes! Thanks @timonmerk

@drammock drammock merged commit ad4c99c into mne-tools:main Nov 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants