-
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: Faster topomaps #4224
MRG: Faster topomaps #4224
Conversation
I'm still profiling, sorry ... |
Okay, well if you get anything useful let me know. In the meantime feel free to try this branch, too. |
Codecov Report
@@ Coverage Diff @@
## master #4224 +/- ##
==========================================
- Coverage 86.23% 86.19% -0.05%
==========================================
Files 357 358 +1
Lines 64558 65004 +446
Branches 9831 9911 +80
==========================================
+ Hits 55673 56030 +357
- Misses 6182 6242 +60
- Partials 2703 2732 +29 |
Looks like it successfully cut down testing time by about 5 minutes. The longest https://travis-ci.org/mne-tools/mne-python/jobs/225709024#L3282 |
Awesome! With a bit of luck I'll be able to shave off another second or two :D |
@jaeilepp I'm not going to work on this until tomorrow sometime, so if you feel like messing with the PatchCollection selection stuff in the meantime, feel free. |
from ..viz import plot_tfr_topomap | ||
if abs(eclick.x - erelease.x) < .1 or abs(eclick.y - erelease.y) < .1: | ||
return | ||
plt.ion() # turn interactive mode on |
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 think we should mess with matplotlib interactive status. Messing with global states is not a nice thing for a package to do.
It would be better to document any functions that need interactivity, telling people to activate interactive mode (maybe with docs somewhere about how to do it).
@kingjr feel free to try this to see if it works any better for you. If not, let me know which call(s) are slow for you. |
mne/viz/topomap.py
Outdated
for x, y in zip(pos_x, pos_y): | ||
ax.add_artist(Circle(xy=(x, y), radius=0.003, color='k')) | ||
patches += [Circle(xy=(x, y), radius=0.003)] |
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 not
patches = [Circle(xy=(x, y), radius=0.003) for x, y in zip(pos_x, pos_y)]
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.
Yeah that's better, just bad translation by me. Actually I'm not sure if this is really any better than ax.plot()
, I should probably try that, too. (Need to check and see if the subset of selected points is also accessible there, though, just like with PatchCollection, though.)
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 should probably benchmark all 3(+?) methods we're using:
ax.plot
patches
ax.scatter
mne/viz/topomap.py
Outdated
for x, y in zip(pos_x, pos_y): | ||
ax.add_artist(Circle(xy=(x, y), radius=0.003, color='k')) | ||
patches += [Circle(xy=(x, y), radius=0.003)] | ||
ax.add_collection(PatchCollection(patches, color='k')) |
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.
@jaeilepp it's probably suboptimal that the legend for spatial_colors and this one use different code ...
spatial_colors doing ax.scatter(pos_x, pos_y, color=colors, s=25, marker='.', zorder=1)
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.
My guess is that it has to do with selection. These sensors need to be Lasso-able. The existing code makes it easy. But I think it should be possible at least with PatchCollection if not also whatever scatter
returns with a bit of extra logic at the selection end.
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.
... although we'd have to check to see if you can do point-by-point color with scatter
like you can with PatchCollection
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'm not sure I understand you correctly, but the (colored) legend for plot_joint is currently done with scatter
.
Checking the history, your guess seems correct: I submitted this with add_artist patches and @jaeilepp turned it into scatter when he added interactivity.
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 think you must have this backward, because right now it's add_artist
, so you must have done scatter
and @jaeilepp did add_artist
...
But in any case, we should figure out which is fastest among ax.plot
, ax.scatter
, and PatchCollection
. Then we can try from fastest to slowest to see if sub-selection can be done. Do you have time to do some profiling?
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 think you must have this backward, because right now it's add_artis
I meant this:
https://github.com/mne-tools/mne-python/blob/master/mne/viz/evoked.py#L167
Do you have time to do some profiling?
Not today, I got to sleep :) I can set up a preliminary benchmark, but nothing thorough.
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.
Well if you have time tomorrow morning feel free, otherwise I'll give it a shot
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.
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 think the scatter option might give another decent boost.
Though most of the test comes from creating (and perhaps closing) the new axes, not the actual plot (it obscures the differences between the actual sensor plotting: I think scatter is actually ~2x as fast as the patches, not just a bit.)
So then it's possible we could save a lot with the axes divider pattern ..?
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.
See also matplotlib/matplotlib#6664. If it were faster to make axes I wouldn't have had to do crazy refactoring of the topo
code to use a single Axes, either. But in the meantime, yeah using a single axes instance could help. But let's do it in another PR.
So |
No, I haven't really touched the topomap code too much. |
Seems like a x2 gain for me. Have you tried on the slider? There the gain should be noticeable. |
Is that 2x enough to be happy, be acceptable, or be better but still crappy?
Is there an example or tutorial with the slider?
|
yes, it's a good start http://mne-tools.github.io/dev/auto_examples/visualization/make_report.html?highlight=slider |
Okay now we use The example you listed @kingjr now rapidly pops up figures for me so I think the overhead is now mostly in figure/axis creation. Can you see if it got any faster for you? Now I think I just need to make the selection bits work now that we use |
@Eric89GXL I never use interactive functions, can't say anything to these. In the notebook, I'm seeing a sizeable 30-50% speedup. |
Okay, selection works now. Ready for review/merge from my end. |
""" | ||
|
||
def __init__(self, pos): | ||
from scipy.spatial.qhull import Delaunay |
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.
Uhhhh Delaunay again, I recognize this now! This is like a second math ed to me!
for x, y in zip(pos_x, pos_y): | ||
ax.add_artist(Circle(xy=(x, y), radius=0.003, color='k')) | ||
ax.scatter(pos_x, pos_y, s=0.25, marker='o', | ||
edgecolor=['k'] * len(pos_x), facecolor='none') |
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.
edgecolor='k'
?
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 prefer this explicit list-mode because we set them independently elsewhere later. (Also, it's possible that this might help mpl optimize for such a state we expect could come later...)
seems to work fine for me !
@Eric89GXL <https://github.com/Eric89GXL> I am just wondering about the
change of behavior on the edges of the head when spatial sampling is poor
(eg with small number of EEG sensors)
|
ok +1 for merge for me then.
|
@jaeilepp all yours then
|
Thanks @Eric89GXL |
I also don't like how channels are re-scaled to match the whole head - are there plans to change it? |
How do you like outlines="skirt"?
|
This is an effort to make our topomap plotting faster interactively and in testing. cc @jona-sassenhagen can you push your changes to the tests?
Still WIP because I need to modify the picking to use
PatchCollection
(which I think should be possible) instead of iteration overCircle
objects. @jaeilepp did you ever try this approach?