-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[MRG] Fix compare evokeds #4755
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
Conversation
mne/viz/evoked.py
Outdated
| ylim=dict(), invert_y=False, show_sensors=None, | ||
| show_legend=True, axes=None, title=None, show=True): | ||
| def _combine_grad(evoked, picks): | ||
| """Creates a new instance of Evoked with combined gradiometers (RMSE)""" |
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.
Uuuuh, that's good to have.
mne/viz/evoked.py
Outdated
|
|
||
|
|
||
| def _check_loc_legal(loc, what='your choice'): | ||
| """Check if a loc is a legal for MPL.""" |
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.
"""Check if loc is a legal location for MPL subordinate axes."""
(or something like that, it's missing a noun)
mne/viz/evoked.py
Outdated
|
|
||
|
|
||
| def _format_evokeds_colors(evokeds, cmap, colors): | ||
| # set up labels and instances to have evokeds as a dict |
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.
Sorry, what? :) I don't get what this sentence means.
mne/viz/evoked.py
Outdated
| channels using global field power (GFP) computation, else it is taking | ||
| a plain mean. | ||
| This function is useful for comparing ER[P/F]s at a specific location. |
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 function is useful for comparing multiple ER[P/F]s - e.g., for multiple conditions - at a specific location.
mne/viz/evoked.py
Outdated
| It can plot a simple Evoked object or if supplied with a list or a dict | ||
| of lists of evoked instances, it will compute the average of the mean or | ||
| GFP across datasets. It can also compute some confidence intervals | ||
| using for example bootstrap estimates. |
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.
It plots 1. a simple evoked object, 2. a list or dict of evoked objects (e.g., for multiple conditions), 3. a list or dict of lists of evokeds (e.g., for multiple subjects in multiple conditions). In the last case, it can show a confidence interval (across e.g. subjects) using parametric or bootstrap estimation.
mne/viz/evoked.py
Outdated
| show_legend : bool | int | ||
| If not False, show a legend. If int, the position of the axes | ||
| show_legend : bool | str | int | ||
| If not False, show a legend. If int or str, the position of the 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.
the position of the legend
or
the position of the legend axes
| show_sensors: bool | int | str | None | ||
| If not False, channel locations are plotted on a small head circle. | ||
| If an int, the position of the axes (forwarded to | ||
| If int or str, the position of the axes (forwarded to |
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.
The position of the head circle
or
The position of the head circle axes
mne/viz/evoked.py
Outdated
| # dealing with continuous colors | ||
| the_colors = None | ||
| color_conds = None | ||
| color_order = 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.
the_colors, color_conds, color_order = None, None, None
mne/evoked.py
Outdated
| def _check_evokeds_ch_names_times(all_evoked): | ||
| evoked = all_evoked[0] | ||
| ch_names = evoked.ch_names | ||
| for e in all_evoked[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.
single-char var name ...
tutorials/plot_metadata_epochs.py
Outdated
| metadata = epochs.metadata | ||
| is_concrete = metadata["Concreteness"] > metadata["Concreteness"].median() | ||
| metadata["is_concrete"] = np.where(is_concrete, 'Concrete', 'Abstract') | ||
| is_concrete = metadata["NumberOfLetters"] > 5 |
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.
is_long
|
Thanks Alex, that looks very clean. |
| gradiometers are combined with RMSE. It means for example that for | ||
| a vectorview system with 204 gradiometers it will be transformed to | ||
| a 102 channels helmet. | ||
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 example/E.g., data from a Vectorview system with 204 gradiometers will be transformed to 102 channels.
| gradiometer corresponding pairs with be combined. | ||
| If None, it defaults to all data channels, in which case the global | ||
| field power will be plotted for all channel type available. | ||
| gfp : bool |
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 the selected channels are gradiometers, the signal from corresponding (gradiometer) pairs will be combined. If None, it defaults to all data channels, in which case the global field power will be plotted for all available channel types.
mne/viz/evoked.py
Outdated
| vlines : "auto" | list of float | ||
| A list in seconds at which to plot dashed vertical lines. | ||
| If "auto" and 0. is in the time interval of interest, it is set to [0.] | ||
| so a vertical bar is plotted at time 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.
If "auto" and 0. ms is the time point of interest, it is set to [0.] and a vertical bar is plotted at time 0.
mne/viz/evoked.py
Outdated
| fig : Figure | list of Figures | ||
| The figure(s) in which the plot is drawn. | ||
| The figure(s) in which the plot is drawn. A list of figures | ||
| is returned only one multiple channel types are plotted. |
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.
When plotting multiple channel types, a list of figures, one for each channel type is returned.
|
Thanks @agramfort |
mne/viz/evoked.py
Outdated
| When multiple channels are passed, this function combines them all, to | ||
| get one time course for each condition. If gfp is True it combines | ||
| channels using global field power (GFP) computation, else it is taking | ||
| a plain mean. |
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 preamble is 3 paragraphs, can we put this into Notes instead?
mne/viz/evoked.py
Outdated
| GFP across datasets. It can also compute some confidence intervals | ||
| using for example bootstrap estimates. | ||
| When passed with more than one planar gradiometers, the planar |
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.
When ``picks`` includes more than one planar gradiometer, ...
mne/viz/evoked.py
Outdated
| When passed with more than one planar gradiometers, the planar | ||
| gradiometers are combined with RMSE. It means for example that for | ||
| a vectorview system with 204 gradiometers it will be transformed to | ||
| a 102 channels helmet. |
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.
102-channel helmet
tutorials/plot_visualize_evoked.py
Outdated
| # The plot is styled with dictionary arguments, again using "/"-separated tags. | ||
| # We plot a MEG channel with a strong auditory response. | ||
| # | ||
| # For move advanced plotting using :func:`mne.viz.plot_compare_evokeds` |
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
mne/viz/evoked.py
Outdated
| This function is useful for comparing ER[P/F]s at a specific location. | ||
| It can plot a simple Evoked object or if supplied with a list or a dict |
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.
:class:`mne.Evoked`
|
Seems to fix the problem for me. It would be good to add this test (I came up with this to help @ktavabi test his PR): |
|
@agramfort want me to directly commit to the branch? |
Codecov Report
@@ Coverage Diff @@
## master #4755 +/- ##
==========================================
+ Coverage 87.86% 87.87% +0.01%
==========================================
Files 349 349
Lines 65819 66079 +260
Branches 11319 11383 +64
==========================================
+ Hits 57829 58070 +241
- Misses 5117 5130 +13
- Partials 2873 2879 +6 |
|
I started to address some comments.
please take over if you have a bit of time over the next couple of days
|
|
Great, I'll try to get a bit of work done! |
|
Pushed a few lines directly to your branch, I will continue tomorrow. |
|
@larsoner I added your test. |
|
Green ... |
mne/viz/evoked.py
Outdated
| raise ValueError('If evokeds is a dict and a cmap is passed, ' | ||
| 'you must specify the colors.') | ||
| # XXX : I am a bit concerned about the duplication of | ||
| # the colors and cmap parameters. |
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 what do you think about this?
we don't use colors anywhere in doc and examples and it has a non-trivial interaction with cmap.
thoughts?
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.
Hm? Maybe I misunderstand, but colors is all over the example.
colors specifies the mapping from conditions to line colors relatively to the color map, cmap specifies the colormap (and the name for the colorbar). I see two alternatives, both of which we ruled out:
- use magic to read relative line color from the condition names (my original idea, which you ruled out)
- demand
colorsvalues to be RGB or RGBA (possible, but requires more user work)
Essentially, option two is viable, but would require the user do something like
import seaborn as sns
from sklearn.preprocessing import QuantileTransformer
n_bins = 10
qt = QuantileTransformer(n_bins).fit(epochs.metadata[cond])
colorscale = sns.color_palette("viridis", n_bins)
colors = dict()
for ... in ...:
colors[xxx] = colorscale[qt.transform(...)]
every time they run the function.
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 ok. forget it.
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 the comment be removed, then?
|
good to go from my end. |
larsoner
left a comment
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.
Otherwise LGTM
mne/evoked.py
Outdated
| ch_names = evoked.ch_names | ||
| for ev in all_evoked[1:]: | ||
| assert ev.ch_names == ch_names, ValueError( | ||
| "%s and %s do not contain " "the same channels" % (evoked, ev)) |
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.
assert statements are skipped with python -O, if this is meant to actually prevent some user error (rather than internal consistency check) this needs to be done the standard way
mne/viz/evoked.py
Outdated
| from numbers import Integral | ||
|
|
||
| import numpy as np | ||
| import matplotlib.lines as mlines |
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.
it probably doesn't matter for the backend choosing because you didn't import pyplot, but let's nest this one, too, to keep mne import times down
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.
Ah I see, you told me to put it at the top of the function, not the file. So that's what I'll do.
mne/viz/evoked.py
Outdated
| raise ValueError('If evokeds is a dict and a cmap is passed, ' | ||
| 'you must specify the colors.') | ||
| # XXX : I am a bit concerned about the duplication of | ||
| # the colors and cmap parameters. |
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 the comment be removed, then?
mne/viz/evoked.py
Outdated
| - a list or dict of :class:`mne.Evoked` objects (e.g., for multiple | ||
| conditions), | ||
| - a list or dict of lists of :class:`mne.Evoked` (e.g., for multiple | ||
| subjects in multiple conditions). |
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.
no need to indent, doing so turns it from a standard bullet list to a quoted bullet list
mne/viz/evoked.py
Outdated
| if (tmin >= 0 or tmax <= 0) and vlines == [0.]: | ||
| vlines = list() | ||
|
|
||
| if vlines is "auto" and (tmin < 0 and tmax > 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.
don't use is with string comparisons
mne/viz/evoked.py
Outdated
|
|
||
| if vlines is "auto" and (tmin < 0 and tmax > 0): | ||
| vlines = [0.] | ||
| assert isinstance(vlines, list) |
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.
From the code above I don't think you've validated that it should be a list at this point, so this should be a full if not isinstance(vlines, (list, tuple)): raise TypeError(...)
| if not isinstance(axes, list): | ||
| axes = [axes] | ||
| from .utils import _validate_if_list_of_axes | ||
| _validate_if_list_of_axes(axes, obligatory_len=len(ch_types)) |
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 axes = _validate_if_list_of_axes(...) and do the if not isinstance(...) in that function? It seems like part of the validation
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've decided against making that change here cause the function is somewhat widely used, mostly in contexts I don't know. I can do it in a separate PR if you still think it should be done (assign me if you want).
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 get it in a subsequent PR if we find time
mne/viz/evoked.py
Outdated
| # XXX: could possibly be refactored; plot_joint is doing a similar thing | ||
| if any([type_ not in _VALID_CHANNEL_TYPES for type_ in ch_types]): | ||
| raise ValueError("Non-data channel picked.") | ||
| 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.
Could do instead:
axes = [plt.subplots(figsize=(8, 6))[1] for _ in range(len(ch_types))]
mne/viz/evoked.py
Outdated
| # calculate the CI | ||
| ci_dict = dict() | ||
| data_dict = dict() | ||
| for cond, this_evokeds in evokeds.items(): |
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.
order here is not deterministic, probably better to do for cond in sorted(evokeds.keys()): this_evokeds = evokeds[key] ...
mne/viz/tests/test_evoked.py
Outdated
|
|
||
| warnings.simplefilter('always') # enable b/c these tests throw warnings | ||
|
|
||
| rng = np.random.RandomState(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.
better to nest this in the function that uses it, lest the next contributor add a function and think it's okay to use the same rng (it could break your test with a heisenbug)
|
Thanks Eric, I will take care of things.
On 20. Nov 2017, at 15:53, Eric Larson ***@***.***> wrote:
it probably doesn't matter for the backend choosing because you didn't import pyplot, but let's nest this one, too, to keep mne import times down
I’m pretty sure *you* told me to un-nest it :D
|
|
I suppose that means we've asymptoted to a good result :) Change it or not, then, up to you |
|
@jona-sassenhagen are you on it? |
|
Yes, I will try to get it done today. |
|
Green. |
|
@larsoner merge if you're happy |
larsoner
left a comment
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.
One small remaining problem then +1 for merge
mne/evoked.py
Outdated
| evoked = all_evoked[0] | ||
| ch_names = evoked.ch_names | ||
| for ev in all_evoked[1:]: | ||
| if not ev.ch_names == ch_names: |
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.
more easily expressed as ev.ch_names != ch_names but you can keep it this way
mne/viz/evoked.py
Outdated
| if vlines == "auto" and (tmin < 0 and tmax > 0): | ||
| vlines = [0.] | ||
| if not isinstance(vlines, (list, tuple)): | ||
| raise TypeError("vlines must be a list or tuple, not " + type(vlines)) |
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 must not be covered by a test because it will error, you should use % as we usually do:
In [1]: 'a' + type('b')
Traceback (most recent call last):
File "<ipython-input-1-03514d031c8f>", line 1, in <module>
'a' + type('b')
TypeError: must be str, not type
mne/viz/evoked.py
Outdated
| if axes is not None: | ||
| if not isinstance(axes, list): | ||
| axes = [axes] | ||
| from .utils import _validate_if_list_of_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.
this shouldn't need to be nested, in fact there is already a from .utils import ... line at the top
|
@ktavabi you are happy too? |
|
I've been happy since the takeover. Thanks. Just waiting to get back on master :) |
|
Thanks @agramfort @ktavabi @jona-sassenhagen |
|
Thanks guys! |
* [ENH] plot_compare_evoked improved line colouring * FIX plot_compared_evokeds grad ci estimation * Compute stats after computing RMS * reorg + simplify grad handling of plot_compare_evokeds * use auxilary functions * add exception and test for corner case with both cmap and colors * address comments * cosmit, docstrings * pep8 * add erics test for grad cis * docstring * comments * pep8 :| * comments
closes #4736 and #4526
@jona-sassenhagen @ktavabi I took over both of your PRs and simplify to refactor / simplify
please have a look