-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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] plot_ica_properties #3275
Conversation
Looks pretty good. |
@@ -3106,3 +3106,32 @@ def average_movements(epochs, head_pos=None, orig_sfreq=None, picks=None, | |||
_remove_meg_projs(evoked) # remove MEG projectors, they won't apply now | |||
logger.info('Created Evoked dataset from %s epochs' % (count,)) | |||
return (evoked, mapping) if return_mapping else evoked | |||
|
|||
|
|||
def segment_raw(raw, segment_length=1., verbose=True): |
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.
Should this be exposed or private?
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 have a strong opinion here, I quite often use segmentation but maybe it is not common enough to expose it. I can add the _
prefix if you prefer.
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 make it private
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 use something like this ever so often too, actually. But I'm okay with either.
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.
yes please make it private and don't set verbose to True by default. See how we use the verbose decorator elsewhere
Consider using this for the topoplots: #3165 |
axes[2].plot(1e3 * evoked.times, evoked.data[comp_idx].ravel()) | ||
# spectrum | ||
axes[3].plot(freqs, psds_mean, color='k') | ||
axes[3].fill_between(freqs, psds_mean - neg_sd, psds_mean + pos_sd, |
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.
Another option would be to use descriptive names (erp_axes, topomap_axes, ...).
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 am definitelly going to add labels to the axes (will be useful for interactive stuff), but I can change the variable names here too.
Oh, and please also make it work for regular Epochs :) (like EEGLAB) |
I thought it does... or maybe you mean something else by "regular Epochs"? |
raise ValueError('inst should be an instance of Raw or Epochs,' | ||
' got %s instead.' % type(inst)) | ||
if isinstance(picks, int): | ||
picks = [picks] |
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.
You're doing this twice.
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 mean channel data. |
Ah, right, I was thinking about another PR, but I can do it here if you insist :) |
evoked = src.average(picks) | ||
# smooth_data is kept in a separate variable for future | ||
# interactive changing of sigma | ||
smooth_data = ndimage.gaussian_filter1d(ica_data[comp_idx], |
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.
Have you thought about hacking plot_epochs_image
so that its individual axes are somehow accessible instead?
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.
Yes, I thought it might be too much work but it might be useful if we want to have topo_kwargs
and epochs_image_kwargs
for example...
plot_epochs_image
creates a separate figure so I would need to make it accept axes
keyword for example.
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.
Most of the plot functions have axes params so I think this might be good.
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.
Also we're big into DRY so I think it should be done somehow.
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.
Ok, I'll try to look into it today/tomorrow.
No, it's okay, just keep it in mind - don't make it impossible to add that functionality later. |
seg_len = np.arange(0.5, 10., 0.5) | ||
nice_len = inst._data.shape[1] / (250. * inst.info['sfreq']) | ||
seg_len = seg_len[np.argmin(np.abs(seg_len - nice_len))] | ||
inst = segment_raw(inst, segment_length=seg_len, verbose=False) |
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.
don't like this very much. 1s is arbitrary + all the various Epochs params. I suggest you do not show any raster plot for Raw data. Just when looking at properties of Epochs instances. I means though that figure layout will be different for Raw and Epochs.
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.
This is what EEGLAB does and it's actually very useful. It's a very good way of showing what and where particular artefacts are, what kind of activity a channel captures.
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 also like what eeglab does for continuous files in pop_prop
. It is not as informative as for epoched data but you get some "birds eye view" instead of nothing.
BTW - I do not set segmentation to 1s - the code above looks for a value from range np.arange(0.5, 10., 0.5)
that is closest to segmet length that divides that data into 250 chunks. 250 is arbitrary 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.
IMO least surprise would be using EEGLAB's default.
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.
Ok you win :)
Make the segment function private though please
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.
@jona-sassenhagen by EEGLAB's default you mean 1 second length segments?
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.
@jona-sassenhagen by EEGLAB's default you mean 1 second length segments?
I think EEGLAB uses 2 seconds?
Predictability is probably better than a smart/'smart' auto magic here.
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.
Not perfectly sure here because it can mean that something looks predictably worse :), but I didn't check it thoroughly anyway so the simple 2s behavior should be ok.
|
|
||
def _segment_raw(raw, segment_length=1., verbose=True): | ||
"""Divide continuous raw data into equal-sized | ||
consecutive chunks. |
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.
chunks -> epochs
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.
don't forget this one
@mmagnuski let us know when travis is happy and you're done with docstrings etc. |
Probably a different PR?
I'd prefer to keep it all in the image_kwargs dict actually. I think the choice of sigma in plot_epochs_image is often unfortunate, but that should be handled there, not here. Haven't gone over the code, but the results look good. |
Is 120 Hz a good default for the max freq? Wouldn't something like 80 or so be better to more clearly see alpha vs. beta vs. theta vs. no low freq peaks? |
@jona-sassenhagen @Eric89GXL @agramfort
Then Epochs and Raw would have Currently |
I'm not 100% sure if this works without conflict, API wise - picks usually does something different. For example, maybe you want to only look at the MEG channels for your ICA, so you pick the MEG channels. Then you need to further supply the component to be plotted. Do you get what I mean? Rest I either agree, or am indifferent on. |
@jona-sassenhagen Yeah, I understand, I was following the way |
Should stay consistent with that somehow, too. I'm not sure. I guess just following the |
Stick to ICA objects only for now otherwise PR will take much longer to be merged and don't be too creative ;) |
Yes, good idea.
I didn't think of setting the parameter in if sigma is not None:
image_kwargs.update(sigma=sigma) I just expect
There is no default currently, all freqs are plotted. Maybe an additional argument?
Yes, I was planning to stick to ICA objs now - I was just trying to plan ahead and sketch my thoughts to see if you agree. I will try tro restrict my creativity ;) |
is_correct_type = np.array([isinstance(x, mpl.axes._axes.Axes) | ||
for x in axes]) | ||
if not np.all(is_correct_type): | ||
first_bad = np.where(is_correct_type == False)[0][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.
flake doesn't like this :(
it doesn't notice it is numpy array and tells me:
comparison to False should be if cond is False:
or if not cond:
Pretty weird, but I'll change to numpy.logical_not
and remove the comparison
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.
63119d0
to
e859433
Compare
@agramfort |
thanks @mmagnuski for the hard work and the patience @espressofiend feel free to complain if it's not good enough for your needs. |
I love this, great work @mmagnuski. |
* 'master' of git://github.com/mne-tools/mne-python: [MRG] plot_ica_properties (mne-tools#3275) [MRG] Example link (mne-tools#3346) GUI (coregistration): scale step +/- instead of multiplicative (mne-tools#3345) Fix bug in set_bipolar_reference (mne-tools#3343) fixed spelling mistake (mne-tools#3341) Fixed spelling mistake (mne-tools#3339)
Thanks! And thanks for your helpful comments! |
Thanks @mmagnuski for doing this! I did not have time to look during the last weeks unfortunately. This is really great work! |
Adressess #3269, #2747 by adding
plot_ica_properties
(and probably a method forICA
class etc.).Example
An example of
plot_ica_properties
(name is open for discussion of course) in inline mode (ep
is an instance ofmne.Epochs
here):
It is definitelly more cluttered than when plotted in a separate window. I will work on this (probably just change figure size). ## Discussion@jona-sassenhagen @agramfort @espressofiend @Eric89GXL @jaeilepp
I will need your input on several issues:
topo_kwargs
to allow the user to control how the topoplot is made?). Some useful candidates (with rather temporary names):dB
- to plot spectrum in dBcmap
- to control topoplot and epochs image colormapinteractive
- to run in interactive mode or not (if we generate non-interactive image, we do not need buttons etc., but see also point 2)PropertiesExplorer
(or some other name) class for future PR adding interactivity. I know you prefer not to add too many classes to mne-python, but I think it would be useful in this case. If you are ok with adding such a class I could start working on it in this PR. Some reasons for why I think it would be useful:it is easier to write/maintain code for interactive visualizations if all the data and plot refresh methods are kept in one object and not in partial functions
it is also easier to extend the interactive plotter (from a user and developer perspective) either by subclassing it or attaching callbacks to the figure/axis that take the plotter object as one of the arguments. If all relevant data is kept in partial functions it is much harder to use it without rewriting the original mne code.
Example: if I wanted to extend the properties plotter to show the unmixing matrix weights as a bar plot when I click on the topo map I could just do:
The discussion on the class can wait until my next PR if you prefer.
importlib.reload
but it didn't seem to work... How do you reloadmne
to test incremental changes?mne
handles MEG sensorsTODOs
plot_properties
argumentseeg
(I will need help here)segment
method for_BaseRaw
psd_kws
orfreq_kws
picks
be allowed a list of values - creating multiple figures ?sigma
param?RuntimeWarning: Mean of empty slice.
ica.plot_properties
tests: