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
ENH: dict features for Epochs objects #229
Conversation
Related to mne-tools#133 -- please don't merge. I wanted to expose one possible approach to this and would like to get some feedback before proceeding.
- parameter event_id now takes either int or dict. Internalla a dict of event_label : event_id mappings will be created which are exposed to the user. - On init the events passed are reduced to the events available in the event_id dict. The dict is then used to subset the available events - for this purpose I added a private function ._get_events() that is about compliant with Python dictionary .get --- returns None, if not in event_ids, else it returns the events associated with the key. - I found instead of passing keys to average / std_error, etc. It is less testing affordant and cleaner to extend .__get_item__. It now checks, whether the key is an int or str. if int, as before subsettig by epoch number, if str and in self.event_ids an Epochs subset associated with the key is returned. To get what @agramfort initially wanted he now can say epochs['audio'].average() A simple test case is: ```Python run examples/plot_read_and_write_raw_data.py import mne events = mne.find_events(raw) epochs = mne.Epochs(raw, events, dict(a=1, v=2, button=32), tmin=-0.2, tmax=0.5) epochs['button'] epochs['b'] epochs['a'].average().plot() ``` Let me know what you think about this take befor I get into further details D
ok done ... |
@@ -105,6 +108,9 @@ class Epochs(object): | |||
info: dict | |||
Measurement info | |||
|
|||
event_ids : dict | |||
Labels mapped to event_ids. |
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.
Names of conditions corresponding to event_ids.
Labels as a particular meaning.
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.
right, would mix up things with stc..
On 05.12.2012, at 21:53, Alexandre Gramfort notifications@github.com wrote:
In mne/epochs.py:
@@ -105,6 +108,9 @@ class Epochs(object):
info: dict
Measurement info
- event_ids : dict
Names of conditions corresponding to event_ids.Labels mapped to event_ids.
Labels as a particular meaning.
—
Reply to this email directly or view it on GitHub.
can you add tests and example ? |
sure, let me run home first ;-) On 05.12.2012, at 21:56, Alexandre Gramfort notifications@github.com wrote:
|
Do you think this deserves an individual example or should we just rewrite the examples in which conditions are being compared? what about deprecation? On 05.12.2012, at 21:56, Alexandre Gramfort notifications@github.com wrote:
|
What I have in mind is topo plot of the ERP/ERF in two conditions mne.viz.plot_topo_evoked([epochs['visual'].average(), that would show a difference in the ERPs. We could even do a 2 sample T-test topo using the standard deviation
you should not need to deprecate anything. You did not break anything |
On 05.12.2012, at 22:10, Alexandre Gramfort notifications@github.com wrote:
exactly! as we catch up with misc support all this could take place in ICA space --> select responding sources. also, related to #133 the architecture proposed is easily extendable without breaking things. one could pass tuples to get combinations of conditions. do you by chance know whether formula objects are hashable? one more point. the pandas exporter -- after some edits-- could now export proper dataframes you can meaningfully pass to patsy / statsmodels / R.
|
On 05.12.2012, at 22:10, Alexandre Gramfort notifications@github.com wrote:
and once more this calls for support of repeated measures anova...
|
…mple @agramfort : if you're fine with this let's merge it soon and get back to the fancy stats / 2-sample t-tests / on the next iteration. I think for 0.5 this is already nice whereas other parts of mne need to be polished for 0.5.
So for the moment I think we're done with this. Let's merge this soon and go deeper as the other pending construction sites are supplied with polish and care. |
|
||
epochs = cp.copy(self) # XXX : should use deepcopy but breaks ... | ||
epochs.events = np.atleast_2d(self.events[key]) | ||
epochs = self.copy() |
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 I'm not mistaken this produces a deepcopy of all the data even before indexing, which would not be very efficient. Also, should users modify the Epochs' ._data? If not, why not let numpy decide when to create deep copies of the data (and internally only deepcopy before actually modifying the 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.
you're right. But otherwise it means doing the copy manually which is more work to do right.
it might work with a trick like:
data = self._data
del self._data
epochs = self.copy()
self._data = data
etc.
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.
And hope that self.copy()
never crashes :)
Eventually it might be cleaner to have an Epochs class that can be initialized with its attributes... and that might also be useful for the result of data transformations such as time-frequency?
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.
Maybe but it's a huge API break and I think we can live with that now don't you think?
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, hence "eventually" :) If you have a NewEpochs class that initializes with data, then the current Epochs could be transformed into a function that create such objects to keep the api backwards compatible (or subclass to make type-checking work as well).
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.
But wait, all is fine. If not preloaded no copy of self._data because it
doesn't exist.
else it in fact gets sliced to the events selected by the key. Where is the
issue?
self.copy() makes a deep copy of self, including self._data. For consistency with numpy this seems a good idea. However, when indexing this entails a lot of unnecessary copying (especially since you're going to discard most of the copied data anyways when you index).
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.
Maybe temporarily remove self._data in getitem before calling self.copy()?
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 see your point, but I'm still a bit reluctant to add something like an
index argument to copy. Or do you see a way to do it without passing an
index argument?
On Thu, Dec 6, 2012 at 5:37 PM, Christian Brodbeck <notifications@github.com
wrote:
In mne/epochs.py:
@@ -463,19 +493,30 @@ def repr(self):
def getitem(self, key):
"""Return an Epochs object with a subset of epochs
"""
if not self._bad_dropped:
warnings.warn("Bad epochs have not been dropped, indexing will be "
- "inaccurate. Use drop_bad_epochs() or preload=True")
epochs = cp.copy(self) # XXX : should use deepcopy but breaks ...
epochs.events = np.atleast_2d(self.events[key])
epochs = self.copy()
But wait, all is fine. If not preloaded no copy of self._data because it
doesn't exist.
else it in fact gets sliced to the events selected by the key. Where is the
issue?self.copy() makes a deep copy of self, including self._data. For
consistency with numpy this seems a good idea. However, when indexing this
entails a lot of unnecessary copying (especially since you're going to
discard most of the copied data anyways when you index).—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2337289.
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 sounds reasonable. More than having a private ._copy with index arg...
Or what do you / the others think?
On Thu, Dec 6, 2012 at 5:40 PM, Christian Brodbeck <notifications@github.com
wrote:
In mne/epochs.py:
@@ -463,19 +493,30 @@ def repr(self):
def getitem(self, key):
"""Return an Epochs object with a subset of epochs
"""
if not self._bad_dropped:
warnings.warn("Bad epochs have not been dropped, indexing will be "
- "inaccurate. Use drop_bad_epochs() or preload=True")
epochs = cp.copy(self) # XXX : should use deepcopy but breaks ...
epochs.events = np.atleast_2d(self.events[key])
epochs = self.copy()
Maybe temporarily remove self._data in getitem before calling
self.copy()?—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2337336.
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.
Added the version you proposed plus fixed a missing assignment-statement.
The epochs tests seem to run smoothly.
On Thu, Dec 6, 2012 at 5:41 PM, Denis-Alexander Engemann <
denis.engemann@gmail.com> wrote:
Yes sounds reasonable. More than having a private ._copy with index arg...
Or what do you / the others think?On Thu, Dec 6, 2012 at 5:40 PM, Christian Brodbeck <
notifications@github.com> wrote:In mne/epochs.py:
@@ -463,19 +493,30 @@ def repr(self):
def getitem(self, key):
"""Return an Epochs object with a subset of epochs
"""
if not self._bad_dropped:
warnings.warn("Bad epochs have not been dropped, indexing will be "
- "inaccurate. Use drop_bad_epochs() or preload=True")
epochs = cp.copy(self) # XXX : should use deepcopy but breaks ...
epochs.events = np.atleast_2d(self.events[key])
epochs = self.copy()
Maybe temporarily remove self._data in getitem before calling
self.copy()?—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2337336.
wait, one more iteration... found some more things to fix. |
@@ -30,6 +30,7 @@ | |||
from .filter import resample | |||
from .event import _read_events_fif | |||
from . import verbose | |||
from .fixes import _in1d |
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 in1d so if np.in1d is present use 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.
Yes, check for it -- was a bit late yesterday ;-)
Wait one more moment, I now have a failing test in test_epochs --- test_bootstrap() -- let me see what's going on there... |
- made sure the API does not get changed, went back to epochs.event_id as attribute instead of .event_ids. - restored support for epochs with event_id=None, in this case the internal event id dict is just the event_id with str(event_id) as key - for the sake of consistency and to make the read-write test work again, the default key for event_id=int is str(event_id) -- as above. - replaced cp.deepcopy with self.copy whereever it was possible. Bootstrapping did not work anymore..
Ok, looks good now. Feel free to review / merge! |
it would be nice to illustrate this new features in the python_tutorial.rst or/and in at least one example. Do you feel brave enough to do the mne.viz.plot_topo_evoked to illustrate it? |
Already pushed it yesterday evening, isin't it up? On Thu, Dec 6, 2012 at 3:16 PM, Alexandre Gramfort <notifications@github.com
|
Ok, now it's up On Thu, Dec 6, 2012 at 3:21 PM, Denis-Alexander Engemann <
|
if self.preload: | ||
epochs._data = epochs._data[epochs.events[:, 0]] | ||
epochs.name = (key if epochs.name == 'Unknown' | ||
else 'epochs_%s' % key) |
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.
copying looks good to me now.
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.
Great, thanks heaps for the detailed feedback.
On Thu, Dec 6, 2012 at 6:21 PM, Christian Brodbeck <notifications@github.com
wrote:
In mne/epochs.py:
)epochs.events = np.atleast_2d(self.events[key]
+if self.preload:
if isinstance(key, slice):
epochs._data = self._data[key]
else:
key = np.atleast_1d(key)
epochs._data = self._data[key]
elif key not in self.event_id:
raise KeyError('Event "%s" is not in Epochs.' % key)
else:
epochs.events = self._get_events(key)
if self.preload:
epochs._data = epochs._data[epochs.events[:, 0]]
epochs.name = (key if epochs.name == 'Unknown'
else 'epochs_%s' % key)
copying looks good to me now.
—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2337924.
And COSMITS in example
include=include, exclude=exclude) | ||
|
||
# Create epochs including different events | ||
epochs = mne.Epochs(raw, events, dict(audio_l=1, visual_r=3), tmin, tmax, 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.
pep8 :) line too long
LGTM besides the detail on the example plot. |
I'll see how to make use of the new cool color function. On Thu, Dec 6, 2012 at 7:27 PM, Alexandre Gramfort <notifications@github.com
|
- fixing cosmits - drafting list support for plot_topo (DON'T merge yet)
+ updating the compare conditions example @agramfort, hopefully I left some tiny nits to pick for you. Feel free to edit if something is fishy; otherwise please merge!
Btw. @mluessi (and @agramfort ; can you confirm this?) --- as much as I love the most recent call back function --- one thing which is annoying is that for some reason apple track pad gestures by some low level mechanism get translated to clicks, so if you happen to swipe in proximity to the topo plot it may happen that n_channels tiny figures would pop up. Is this somehow avoidable? I think we should catch this before the upcoming release. |
@mluessi @agramfort JTLYK what I'm talking about: 222 with one swipe ;-) |
I can't confirm it fortunately I don't have to use a Mac :-). If it is really a problem we could disable it for OS-X or add an option.. |
pl.close('all') | ||
pl.figure() | ||
title = 'MNE sample data - left auditory and visual' | ||
plot_topo(evokeds, layout, color=['y', 'g'], title=title) |
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.
apparently there is a bug if you don't give color then only one evoked is 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.
I'm addings some cases + guessing.
If colors is str, always use that colour,
if None, cycle through list of colors.
Is there btw. a prefered order? 'w' 'y' 'g' 'r' 'b' ? if not I would just
take the COLORS list specified in viz and prepend 'w'
On Fri, Dec 7, 2012 at 8:54 AM, Alexandre Gramfort <notifications@github.com
wrote:
In examples/plot_topo_compare_conditions.py:
+# Create epochs including different events
+epochs = mne.Epochs(raw, events, dict(audio_l=1, visual_r=3), tmin, tmax,
picks=picks, baseline=(None, 0), reject=reject)
+# Generate list of evoked objects from conditions names
+evokeds = [epochs[name].average() for name in 'audio_l', 'visual_r']
+
+###############################################################################
+# Show topography for two different conditions
+
+layout = read_layout('Vectorview-all.lout')
+
+pl.close('all')
+pl.figure()
+title = 'MNE sample data - left auditory and visual'
+plot_topo(evokeds, layout, color=['y', 'g'], title=title)apparently there is a bug if you don't give color then only one evoked is
plotted.—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2345728.
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 addings some cases + guessing. If colors is str, always use that colour, if None, cycle through list of colors. Is there btw. a prefered order? 'w' 'y' 'g' 'r' 'b' ? if not I would just take the COLORS list specified in viz and prepend 'w'
itertools.cycle on ['w' 'y' 'g' 'r' 'b'] is good to me.
indeed. I can reproduce the swiping… darn...
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 recent commit
On Fri, Dec 7, 2012 at 10:26 AM, Alexandre Gramfort <
notifications@github.com> wrote:
In examples/plot_topo_compare_conditions.py:
+# Create epochs including different events
+epochs = mne.Epochs(raw, events, dict(audio_l=1, visual_r=3), tmin, tmax,
picks=picks, baseline=(None, 0), reject=reject)
+# Generate list of evoked objects from conditions names
+evokeds = [epochs[name].average() for name in 'audio_l', 'visual_r']
+
+###############################################################################
+# Show topography for two different conditions
+
+layout = read_layout('Vectorview-all.lout')
+
+pl.close('all')
+pl.figure()
+title = 'MNE sample data - left auditory and visual'
+plot_topo(evokeds, layout, color=['y', 'g'], title=title)I'm addings some cases + guessing. If colors is str, always use that
colour, if None, cycle through list of colors. Is there btw. a prefered
order? 'w' 'y' 'g' 'r' 'b' ? if not I would just take the COLORS list
specified in viz and prepend 'w'
itertools.cycle on ['w' 'y' 'g' 'r' 'b'] is good to me. indeed. I can
reproduce the swiping… darn...—
Reply to this email directly or view it on GitHubhttps://github.com//pull/229/files#r2346337.
besides my last commnets LGTM and everything works fine on my mac with EPD.
|
.... but can you reproduce the swiping and opening hundreds of of plots On Fri, Dec 7, 2012 at 8:58 AM, Alexandre Gramfort <notifications@github.com
|
merged by rebase but we have a tiny problem when clicking and the line is white as we don't see it with a white background.... |
thanks ! I almost forgot :) |
To get what @agramfort initially wanted he now can say epochs['audio'].average()
A simple test case is:
Let me know what you think about this take befor I get into further details
D