Giter VIP home page Giter VIP logo

Comments (54)

larsoner avatar larsoner commented on July 24, 2024

Sounds easy enough. I can do it while I'm digging around in there anyway.

Speaking of which, I'd like to add support for saving the standard errors, too, if it's not already in there. Did I just miss it somewhere?

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

something like epochs.average(comment='auditory', stderr=True/False) would be nice

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

very nice idea +1 for that
maybe with this we could revisit my epochs-response matching example (reading correct buttonpresses from epochs).

On 05.10.2012, at 19:08, Eric89GXL [email protected] wrote:

Sounds easy enough. I can do it while I'm digging around in there anyway.

Speaking of which, I'd like to add support for saving the standard errors, too, if it's not already in there. Did I just miss it somewhere?


Reply to this email directly or view it on GitHub.

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

glad you both like this idea.

Regarding capturing the standard error what you want is a logger

see for inspiration (or not if too complex) this PR on scikit-learn :

scikit-learn/scikit-learn#1171

but we could do something simpler...

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

I'm not sure what you mean by standard error---does it have something to do with the subject making errors? I just meant the standard deviation across epochs divided by the sqrt of the number of epochs for each point in time (to complement the average at each point in time for plotting purposes, generally)...

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

i.e., the data that gets put in the evoked files by putting "stderr" in the .ave files in the C code. As you can see I'm a little hung up on the C code functionality :)

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

#LOL

by the way you probably know it's possible to compute the std in one pass:

http://www.strchr.com/standard_deviation_in_one_pass

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

I did not know that. I wonder which one numpy.std uses.

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

I did not know that. I wonder which one numpy.std uses.

you can read the code :

https://github.com/numpy/numpy

:)

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

lol too :-)

no it was just related to a discussion alex and i had about smartening up the epochs objects.
letting the epochs know about the type of events via a dict points into that direction.
i was refering to some code i wrote for creating epochs based on the subjects behavioral responses.

btw. -- somewhat related -- i'm about to finalize my ica WIP-PR soon. I was thinking about allowing to include the mixing / unmixing matrixes in the raw object similar like projs, so that you could toggle the raw data between ica and channel space. does it make sense to you?

On 05.10.2012, at 19:22, Alexandre Gramfort [email protected] wrote:

#LOL

by the way you probably know it's possible to compute the std in one pass:

http://www.strchr.com/standard_deviation_in_one_pass

Reply to this email directly or view it on GitHub.

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

I would suggest enhancing epochs with an ultimate goal of also capturing single trial properties.

I have been working on some classes to represent events, which would provide a large range of functionalities. If people like this idea these classes could be used in one way or another to manage events.

For the classes, look at an example or the documentation I have made so far.

I have made a branch where I used these objects to implement basically the functionality you are asking for (there is an example script but you would have to have my package installed). The branch adds a model attribute to Epochs which has all events. This model makes it easy to add additional functionality. For instance, the Epochs.model also keeps the epoch index based on the original event order, so that after epochs are rejected individual epochs can still be identified (although this works only with preload=True, bc with preload=False get_data() does not modify the Epochs object).

I think such an event representation would be even more useful for loading events in the first place. This would make it easier to assign more than one label to each event, and specify e.g. multifactorial designs. Each experimental paradigm then would only need a single label_events function that would add labels based on IDs (example). The whole selection of events could then be made using labels rather than IDs. E.g.::

>>> import loader
>>> ds = loader.load_evts('xxxxx/MEG/sample/sample_audvis_raw.fif')
    Read a total of 3 projection items:
        PCA-v1 (1 x 102)  idle
        PCA-v2 (1 x 102)  idle
        PCA-v3 (1 x 102)  idle
Adding average EEG reference projection.
Created an SSP operator (subspace dimension = 4)
4 projection items activated
>>> print ds[:10]
eventID   i_start   condition   side   modality
-----------------------------------------------
2         27977     RA          R      A       
3         28345     LV          L      V       
1         28771     LA          L      A       
4         29219     RV          R      V       
2         29652     RA          R      A       
3         30025     LV          L      V       
1         30450     LA          L      A       
4         30839     RV          R      V       
2         31240     RA          R      A       
3         31665     LV          L      V       
>>> ds = ds.subset(ds['modality'] == 'A')
>>> print ds[:10]
eventID   i_start   condition   side   modality
-----------------------------------------------
2         27977     RA          R      A       
1         28771     LA          L      A       
2         29652     RA          R      A       
1         30450     LA          L      A       
2         31240     RA          R      A       
1         32101     LA          L      A       
2         32935     RA          R      A       
1         33712     LA          L      A       
2         34532     RA          R      A       
1         35428     LA          L      A       
>>> 

And then epochs could be loaded just for the selected events.

Minor comment: the Epochs.average() method already has a kwarg so Epochs.average(event_label) would break backwards compatibility.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

While we're on the topic of re-coding events, it came to my attention a bit ago that having different numbers of trials in two conditions A and B will cause a bias when you estimate the difference in the magnitude of the activities in the two conditions (e.g., when using the magnitude of the dSPM or current values in the two conditions). For an intuitive sense, of this consider that your noise will go down as sqrt(N) with N trials, so if there are 4x the number of trials in condition A versus condition B, then you'll have half the noise amplitude in condition A---once you take the magnitude, if noise used to be zero-mean Gaussian it now has a folded normal distribution with a non-zero mean that biases condition B more than condition A.

In any case, if we can build in a way to do trial-count equalization at some level to this code, it would also be great. Right now we do it all offline with list file I/O...

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

wow, that sounds really interesting.
did you actually come across patsy?
just for not reduplicating work:

http://patsy.readthedocs.org/en/latest/index.html

maybe one could create some kind of interface to the stats-world via your approach / patsy / pandas.
we just have to make sure that the api doesnt get cluttered.
i will upload an example demonstrating my event-evaluation function tomorrow / the next days. I would be happy to know what you think on it
(it gets key-depression-latencies / durations and labels epochs as valid vd invalid depending on criteria)
i also did some explorations on how to most smoothly draw pandas dataframes from epochs.
i stopped this at some point because it felt somewhat 'unnatural' if you know what i mean.
(tabular strucures seem to me more natural for natural for behavioral / second-level / lower-dimensional data
but i might be mistaken)
so my conclusion was that the main classes shouldnt be oover-doped too much
but an IO module might be it.
wdyt? hope this makes sense.

D

On 05.10.2012, at 23:13, Christian Brodbeck [email protected] wrote:

I would suggest enhancing epochs with an ultimate goal of also capturing single trial properties.

I have been working on some classes to represent events, which would provide a large range of functionalities. If people like this idea these classes could be used in one way or another to manage events.

For the classes, look at an example or the documentation I have made so far.

I have made a branch where I used these objects to implement basically the functionality you are asking for (there is an example script but you would have to have my package installed). The branch adds a model attribute to Epochs which has all events. This model makes it easy to add additional functionality. For instance, the Epochs.model also keeps the epoch index based on the original event order, so that after epochs are rejected individual epochs can still be identified (although this works only with preload=True, bc with preload=False get_data() does not modify the Epochs object).

I think such an event representation would be even more useful for loading events in the first place. This would make it easier to assign more than one label to each event, and specify e.g. multifactorial designs. Each experimental paradigm then would only need a single label_events function that would add labels based on IDs (example). The whole selection of events could then be made using labels rather than IDs. E.g.::

import loader
ds = loader.load_evts('xxxxx/MEG/sample/sample_audvis_raw.fif')
Read a total of 3 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Adding average EEG reference projection.
Created an SSP operator (subspace dimension = 4)
4 projection items activated
print ds[:10]

eventID i_start condition side modality

2 27977 RA R A
3 28345 LV L V
1 28771 LA L A
4 29219 RV R V
2 29652 RA R A
3 30025 LV L V
1 30450 LA L A
4 30839 RV R V
2 31240 RA R A
3 31665 LV L V
ds = ds.subset(ds['modality'] == 'A')
print ds[:10]

eventID i_start condition side modality

2 27977 RA R A
1 28771 LA L A
2 29652 RA R A
1 30450 LA L A
2 31240 RA R A
1 32101 LA L A
2 32935 RA R A
1 33712 LA L A
2 34532 RA R A
1 35428 LA L A

And then epochs could be loaded just for the selected events.

Minor comment: the Epochs.average() method already has a kwarg so Epochs.average(event_label) would break backwards compatibility.


Reply to this email directly or view it on GitHub.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

good that you name it.
i was puzzled about that too.

On 05.10.2012, at 23:34, Eric89GXL [email protected] wrote:

While we're on the topic of re-coding events, it came to my attention a bit ago that having different numbers of trials in two conditions A and B will cause a bias when you estimate the difference in the magnitude of the activities in the two conditions (e.g., when using the magnitude of the dSPM or current values in the two conditions). For an intuitive sense, of this consider that your noise will go down as sqrt(N) with N trials, so if there are 4x the number of trials in condition A versus condition B, then you'll have half the noise amplitude in condition A---once you take the magnitude, if noise used to be zero-mean Gaussian it now has a folded normal distribution with a non-zero mean that biases condition B more than condition A.

In any case, if we can build in a way to do trial-count equalization at some level to this code, it would also be great. Right now we do it all offline with list file I/O...


Reply to this email directly or view it on GitHub.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

I'm working on adding functionality to save stderr as well as means in evoked (including from epochs.average()). Looks like the best way to do it is to modify the guts of the Evoked object to support multiple data sets, i.e. make evoked.data a list. This most closely mirrors how the files are stored. While we could do multiple evoked objects per condition (or mean / stderr) you wanted to have, that makes for a bunch of unnecessary copies of channel data, etc. that we'd have to add conditions for, so I lean against that solution. From the bit of work I've done, it looks like we'd have to make the following things lists:

evoked.data
evoked.nave
evoked.aspect_kind (mean or stderr)
evoked.comment

Other than that, we should be able to leave the structures as-is. Unfortunately this would break backward compatibility with people reading these fields directly, but it seems like the cleanest from a coding standpoint. What do people think?

If backward compatibility is critical, I can instead work on instead improving read_evoked and write_evoked. Instead of just calling the Evoked() and evoked.save() functions, respectively, I can try to get them to do something intelligent to combine (or split) these calls when reading or writing the evoked files. That would get us closer to what the C code did, storing every item in one place, at least.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

In any case, it might make sense for now just to extend the functionality of read/write_evoked to allow reading and writing multiple event codes to FIF files in order to maintain backward compatibility. That's what I've implemented in PR 135, let me know what you think.

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

In any case, it might make sense for now just to extend the functionality
of read/write_evoked to allow reading and writing multiple event codes to
FIF files in order to maintain backward compatibility. That's what I've
implemented in PR 135, let me know what you think.

+1 for that

and +1 for denis' suggestion to find a way to expose epochs as objects that
pandas or statsmodels or patsy understands for simple stats like
anovas and GLMs.

df = epochs.as_dataframe()

?

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

and +1 for adding a method summary to Epochs to print things like

eventID i_start condition

2 27977 RA
1 28771 LA
2 29652 RA
1 30450 LA
2 31240 RA

I am really open to suggestions.

and print also in seconds rather that index numbers

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

since a trigger is always one integer it would be better to do:

event_id = {1:['left', 'auditory'], 2:['right', 'auditory'], 3:['left', 'visual'], 4:['right', 'visual']}

@christianmbrodbeck would that suite your needs better?

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

@ Data frames

Actually i did something like that, it would looks like that (inside Epochs):

def to_data_frame(self, frame=True):
     """Get the epochs as Pandas panel of data frames

     Parameters
     ----------

     frame : boolean
        If frame, data frame will be returned with a hierarchical
        epochs * time-slices index, else a panel object of
        channels * time-slices data frames for each epoch.

     Returns
     -------
     out : depending on arguments
        data frame object or panel object

     """
     import pandas as pa
     data = self.get_data()
     epoch_ids = ["Epoch %i" % (i + 1) for i in np.arange(data.shape[0])]

     ret = pa.Panel(data=data, items=epoch_ids, major_axis=self.ch_names)
     if frame:
         ret = ret.swapaxes(0, 1).to_frame()
         ret.index.names = ["epochs", "tsl"]
         return ret
     else:
         ret.swapaxes(1, 2)

And here a minimum usage example:

import mne
import numpy as np
from mne.fiff import Raw
from mne.datasets import sample
from pandas.stats.api import rolling_mean

from mne.datasets import sample
data_path = sample.data_path('examples/')
raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
raw = Raw(raw_fname)
events = mne.find_events(raw, stim_channel='STI 014')
exclude = raw.info['bads'] + ['MEG 2443', 'EEG 053']
picks = mne.fiff.pick_types(raw.info, meg=True, eeg=True, eog=True, stim=False, exclude=exclude)

event_id = 1
tmin = -0.2
tmax = 0.5
baseline = (None, 0)
reject = dict(grad=4000e-13, mag=4e-12, eog=150e-6)

epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks,
                    baseline=baseline, preload=False, reject=reject)

epochs_df = epochs.to_data_frame()

meg_chs = [c for c in epochs.ch_names if c.startswith("MEG")]

#display some channels
epochs_df.ix[:, :10].head(20)

# split timeslices
grouped_tsl = epochs_df[meg_chs].groupby(level='tsl')

# then create a quick average plot
grouped_tsl.mean().plot(legend=0)

# or a trellis plot on a few channels 
grouped_tsl.mean()[meg_chs[:10]].plot(subplots=1)

# use median instead
grouped_tsl.median().plot(legend=0)

# use custom numpy function
grouped_tsl.agg(np.std).plot(legend=0)

# average and then smooth using a rolling mean and finally plot in one sinfle line!
grouped_tsl.apply(lambda x: rolling_mean(x.mean(), 10)).plot(legend=0)

# apply different functio for channels
grouped_tsl.agg({"MEG 0113": np.mean, "MEG 0213": np.median})

# investigate epochs and create string table for dumping into file
grouped_epochs = epochs_df[meg_chs].groupby(level='epochs')

result_table = (grouped_epochs.max().ix[:, 1:3] * 1e15).to_string()

# investigate a specific channel's std across epochs
grouped_epochs.std()["MEG 0113"].plot()

grouped_tsl.agg(np.std).plot(legend=0)

What do you think on that, worth a PR?

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

@dengemann no I haven't come across patsy, thanks for pointing it out! I will have to have a closer look at it. I have come across Pandas, but I understood it's not made for higher dimensional data.

For simplifying sensor analysis I've been using an additional "ndvar" class which I have not documented externally yet; The main idea is to have a numpy array that "knows" about its dimensions, which can be used in indexing and plotting.

  • The object behaves like factor and var objects for indexing and can be managed in a dataset in the same way
  • ndvars represents for each case not a single value or label, but instead an n-dimensional array (e.g., time for a simple time series; time by sensor for an epoch; …)
  • for each dimension, the object can store meaningful values, e.g. sensor positions, which can then then be used by plotting functions
  • indexing with dimensions allows fast extraction of univariate dependent variables such as peak values for analysis in other packages (e.g., indexing a (trial X sensor X time) ndvar object with a single sensor returns a (trial X time) ndvar for that sensor). Once you reach a univariate variable it can be transferred into another stats package

Here are some examples with random data, and here is an example with the mne sample data.

If that's of any use it would be great to integrate it with other packages. A limitation is representing data of from different types (e.g. gradiometer and magnetometer) together. However, I think source estimates could well be represented with ndvars, the relevant dimension being a source space.

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

@agramfort

since a trigger is always one integer it would be better to do:
event_id = {1:['left', 'auditory'], 2:['right', 'auditory'], 3:['left', 'visual'], 4:['right', 'visual']}

That would be somewhat more flexible but might get somewhat complicated with multiple factors. Also, what I mean by trial information is something like e.g. lexical frequency for words, which often cannot be encoded in the triggers because there are not enough values. Rather we'd have an external list with those values and would have to use that information together with the data. Having a proper data model for the epochs would provide a natural way to interact with that information.

However, especially because of the .fif file format limitations (?), maybe it would make more sense to store the data model externally? The epochs object could restrict itself to representing the case index in some way (so that after rejecting epochs the corresponding to the model can be reconstructed) but epochs could of course also represent simple label codes for situations where a more complex label is unnecessary? I.e. someone with a simple model would not have to worry about the model representation?

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

@christianmbrodbeck

Hi chris,

that looks very appealing, nice!
probably the ndvar approach would be suitible for internal usage, however increasingly so on the long run (api changes, complexity).

the pandas folks are working on nd-dataframes. however, in fact the data frame already supports higher dimensional structures via hierarchical or multi index, that is a tuple based index.

http://pandas.pydata.org/pandas-docs/stable/indexing.html

also see the recent WIP #137

you can easily achieve what we are looking with pandas, e.g. subjects x conditions x trials x timeslices indices.
and its fast (cython) and is tested.

so as for now pandas might be a good choice for doing analysis-related restructuring. we then could iself-pacedly catch up with the api step by step and see where to set the internal / foreign fuctionality border.

does that make sense?

D

On 07.10.2012, at 17:55, Christian Brodbeck [email protected] wrote:

@dengemann no I haven't come across patsy, thanks for pointing it out! I will have to have a closer look at it. I have come across Pandas, but I understood it's not made for higher dimensional data.

For simplifying sensor analysis I've been using an additional "ndvar" class which I have not documented externally yet; The main idea is to have a numpy array that "knows" about its dimensions, which can be used in indexing and plotting.

The object behaves like factor and var objects for indexing and can be managed in a dataset in the same way
ndvars represents for each case not a single value or label, but instead an n-dimensional array (e.g., time for a simple time series; time by sensor for an epoch; …)
for each dimension, the object can store meaningful values, e.g. sensor positions, which can then then be used by plotting functions
indexing with dimensions allows fast extraction of univariate dependent variables such as peak values for analysis in other packages (e.g., indexing a (trial X sensor X time) ndvar object with a single sensor returns a (trial X time) ndvar for that sensor). Once you reach a univariate variable it can be transferred into another stats package
Here are some examples with random data, and here is an example with the mne sample data.

If that's of any use it would be great to integrate it with other packages. A limitation is representing data of from different types (e.g. gradiometer and magnetometer) together. However, I think source estimates could well be represented with ndvars, the relevant dimension being a source space.


Reply to this email directly or view it on GitHub.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

... Hi @agramfort @Eric89GXL @mluessi @christianmbrodbeck --- what did actually happen to this, thinking about future directions I 'sexing' / 'smartening' up the Epochs still is a nice one. Where are we at with this?

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

not far... I guess we need a dedicated contributor to embrace the project ;)

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

You damn pointer ;-)

On Wed, Dec 5, 2012 at 2:46 PM, Alexandre Gramfort <[email protected]

wrote:

not far... I guess we need a dedicated contributor to embrace the project
;)


Reply to this email directly or view it on GitHubhttps://github.com//issues/133#issuecomment-11041754.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

So let me look into it --- yes @agramfort ! -- but let's first see what we have.

  1. first -- still pending -- the initial dict - int proposal
  2. the comment field -- already added by @Eric89GXL and @agramfort
  3. the data_frame export which I'm about to refactor to be smoother / more self-evident
  4. impressive stuff by @christianmbrodbeck -- lost the discussion somewhere. But what actually about this?
  5. finally I did something related to my epochs in order to be abled to measure response latencies from my data, generate reports on it and select epochs based on the correctness of the behavioral response, see a gist, to give you an idea: https://gist.github.com/4215754 --- you would call it like:
STIM_CH, RESP_CH = 'STI 014', 'STI 015'

L_CORRECT = [... event ids where left response are expected]
RCORRECT = [... same for right button presses]

 res, ev_correct = evaluate_responses(raw, evs, ev_id, rtmin, rtmax, STIM_CH, RESP_CH,
                                          L_CORRECT, R_CORRECT, resp_events=False)

You would then get a result table and the corrected events where you could chose to be on stimulus or button press onset.

I would then do 1) and we can see what can be easily added from 4) and 5).
Makes sense? Did I miss anything?

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

Finally, as 1), 4) and 5) seem to be rather independent features It's safe to start with 1) and then see what to add next.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

JTLYK I'm drafting this now.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

So to promote this issue i made a PR (#229) that covers part of out discussion. That is at least 1). From there it should be easier to get the rest into Epochs. I will leave this issue open however until we're happy with it.

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

About my stuff, I am happy to adapt it or parts of it to be included in mne-python. Let me know what parts you are thinking about. I understood some people were reluctant to add too many new classes, like the factors for modeling events.

I recently implemented representing source estimates as ndvars, and can describe that in some detail. If something like the ndvar object would be a candidate for representing data I can also add some documentation on it to have a better basis for discussion.

Also, next semester I am going to have fewer classes and hopefully more time :)

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

although we are more and more developers I am reluctant to add to much new code that we'll have the maintain. We have to be careful with the scope of mne-python. It's better to do one thing very well rather than embracing too many projects. As a good friend always tells me "choose your battles" ! We cannot afford to be a standard stats packages. The best we can do is make mne-python as usable as possible with standard stats projects. At least until we are not 30 core developers ;)

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

I really like the new dict support for epochs instances. They will allow me to potentially code something in mne-python that I've been doing manually thus far, which is trial-count-matching conditions and combining conditions, with minimal changes to mne-python.

For example, let's say you have an experiment with auditory and visual stimuli, coming from the left and right (2x2 design). You may want to contrast A-V (thus collapsing L&R). In this case, it would be ideal (and possibly important) to equalize the trial counts in AL, AR, VL, and VR conditions before combining AL+AR and VL+VR to get A and V.

I propose the following changes (or something analogous) to facilitate this:

  1. We already have the function mne.equalize_epoch_counts, but this operates on multiple Epochs instances to equalize trial counts. Now that we have support for multiple markers in one Epochs instance, it should be very straightforward to add this functionality to Epochs instances themselves, as:
    epochs.equalize_counts(types_1, types_2, ...)
    This function would pool all trials of type types_1 together, all trials of type types_2 together, etc., and equalize the trial counts of these groups by dropping an appropriate number of trials from each groups. In the example above, this would be something like:
    epochs.equalize_counts(['AudL'], ['AudR'], ['VisL'], ['VisR'])
    Following this operation, epochs would have the same number of 'AudL' and 'VisR' trials and so on. If someone didn't really care about whether or not auditory had more L trials than visual, but just wanted to compare them (obviously not optimal in this example but there are situations where it would make sense), they could do:
    epochs.equalize_counts(['AudL', 'AudR'], ['VisL', 'VisR'])

  2. Add the method epochs.collapse_trial_types(old_types, new_type). This function would simply re-label all events in a given set of types to be a new label. In the example, this would be:

epochs.collapse_types(['AudL', 'AudR'], 'Aud'])
epochs.collapse_types(['VisL', 'VisR'], 'Vis'])

What do people think? I think the changes to mne-python would be fairly minimal, and I think this is functionality that should be present. It's a necessary first step for any analysis performed in our lab, since we typically have designs where there are multiple conditions, and they must be trial-count-equalized to avoid bias...

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

i can only second that, each sentence.
would you like to issue a PR?

On 07.12.2012, at 23:49, Eric89GXL [email protected] wrote:

I really like the new dict support for epochs instances. They will allow me to potentially code something in mne-python that I've been doing manually thus far, which is trial-count-matching conditions and combining conditions, with minimal changes to mne-python.

For example, let's say you have an experiment with auditory and visual stimuli, coming from the left and right (2x2 design). You may want to contrast A-V (thus collapsing L&R). In this case, it would be ideal (and possibly important) to equalize the trial counts in AL, AR, VL, and VR conditions before combining AL+AR and VL+VR to get A and V.

I propose the following changes (or something analogous) to facilitate this:

  1. We already have the function mne.equalize_epoch_counts, but this operates on multiple Epochs instances to equalize trial counts. Now that we have support for multiple markers in one Epochs instance, it should be very straightforward to add this functionality to Epochs instances themselves, as:
    epochs.equalize_counts(types_1, types_2, ...)
    This function would pool all trials of type types_1 together, all trials of type types_2 together, etc., and equalize the trial counts of these groups by dropping an appropriate number of trials from each groups. In the example above, this would be something like:
    epochs.equalize_counts(['AudL'], ['AudR'], ['VisL'], ['VisR'])
    Following this operation, epochs would have the same number of 'AudL' and 'VisR' trials and so on. If someone didn't really care about whether or not auditory had more L trials than visual, but just wanted to compare them (obviously not optimal in this example but there are situations where it would make sense), they could do:
    epochs.equalize_counts(['AudL', 'AudR'], ['VisL', 'VisR'])

  2. Add the method epochs.collapse_trial_types(old_types, new_type). This function would simply re-label all events in a given set of types to be a new label. In the example, this would be:

epochs.collapse_types(['AudL', 'AudR'], 'Aud'])
epochs.collapse_types(['VisL', 'VisR'], 'Vis'])
What do people think? I think the changes to mne-python would be fairly minimal, and I think this is functionality that should be present. It's a necessary first step for any analysis performed in our lab, since we typically have designs where there are multiple conditions, and they must be trial-count-equalized to avoid bias...


Reply to this email directly or view it on GitHub.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

Sure. @agramfort, @christianmbrodbeck, @mluessi, speak now or forever hold your peace...

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

Just to add a few thoughts, or just one, basically. Before the 0.5 release i am planning to refactor the pandas export. I think the dict features will make it straight forward to export proper design-tables to pandas / R -- so you could easily feedback stats to an mne session at a critical point, the epochs processing. the equalisation features would be nice to obtain equal cells expected by most procedures.

On 07.12.2012, at 23:49, Eric89GXL [email protected] wrote:

I really like the new dict support for epochs instances. They will allow me to potentially code something in mne-python that I've been doing manually thus far, which is trial-count-matching conditions and combining conditions, with minimal changes to mne-python.

For example, let's say you have an experiment with auditory and visual stimuli, coming from the left and right (2x2 design). You may want to contrast A-V (thus collapsing L&R). In this case, it would be ideal (and possibly important) to equalize the trial counts in AL, AR, VL, and VR conditions before combining AL+AR and VL+VR to get A and V.

I propose the following changes (or something analogous) to facilitate this:

  1. We already have the function mne.equalize_epoch_counts, but this operates on multiple Epochs instances to equalize trial counts. Now that we have support for multiple markers in one Epochs instance, it should be very straightforward to add this functionality to Epochs instances themselves, as:
    epochs.equalize_counts(types_1, types_2, ...)
    This function would pool all trials of type types_1 together, all trials of type types_2 together, etc., and equalize the trial counts of these groups by dropping an appropriate number of trials from each groups. In the example above, this would be something like:
    epochs.equalize_counts(['AudL'], ['AudR'], ['VisL'], ['VisR'])
    Following this operation, epochs would have the same number of 'AudL' and 'VisR' trials and so on. If someone didn't really care about whether or not auditory had more L trials than visual, but just wanted to compare them (obviously not optimal in this example but there are situations where it would make sense), they could do:
    epochs.equalize_counts(['AudL', 'AudR'], ['VisL', 'VisR'])

  2. Add the method epochs.collapse_trial_types(old_types, new_type). This function would simply re-label all events in a given set of types to be a new label. In the example, this would be:

epochs.collapse_types(['AudL', 'AudR'], 'Aud'])
epochs.collapse_types(['VisL', 'VisR'], 'Vis'])
What do people think? I think the changes to mne-python would be fairly minimal, and I think this is functionality that should be present. It's a necessary first step for any analysis performed in our lab, since we typically have designs where there are multiple conditions, and they must be trial-count-equalized to avoid bias...


Reply to this email directly or view it on GitHub.

from mne-python.

mluessi avatar mluessi commented on July 24, 2024

I like the idea.. but I think we should try not to include a bunch new features right before the 0.5 release. Given that the idea is to have a release in less than 2 weeks, features that we include now will only receive minimal testing and we may end up having a release that is buggy.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

that's true too. so let's first make sure the new exciting dict feature does what we want and also there are still other thıngs pending / waiting...
but ++1 for this direction

On 08.12.2012, at 00:11, Martin Luessi [email protected] wrote:

I like the idea.. but I think we should try not to include a bunch new features right before the 0.5 release. Given that the idea is to have a release in less than 2 weeks, features that we include now will only receive minimal testing and we may end up having a release that is buggy.


Reply to this email directly or view it on GitHub.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

Makes sense. Are we going to split development between stable 0.5 (patching bugs and the like) and development (0.6dev) versions? That would prevent delays on 0.6 feature development. But it is possible just hold off on merging changes if that's easier to manage.

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

@Eric89GXL Sounds useful, and makes me think of my earlier proposal to integrate my factors to manage events: with those factors you could just use an interaction (I used the % operator) to specify the sets of trials you want to equalize

epochs.equalize_counts(modality % side)

internally you could then use those factors:

>>> i = modality % side # interaction effect
>>> i.cells
[('A', 'L'), ('A', 'None'), ('A', 'R'), ('None', 'L'), ('None', 'None'), ('None', 'R'), ('V', 'L'), ('V', 'None'), ('V', 'R')]
>>> for cell in i.cells:
...     index = i == cell
...     print index.sum()
...     
72
0
73
0
31
0
73
0
71

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

@christianmbrodbeck that would be cool, too. I think supporting both modes would be convienent, like a "simple" versus "advanced" mode.

Sorry for being dense, but what is the ('None', 'None') in that example...?

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

i think that would be brilliant.
but martin is certainly right, let's get the 0.5 done -- without bugs and meanwhile keep up #133

On 08.12.2012, at 00:18, Christian Brodbeck [email protected] wrote:

@Eric89GXL Sounds useful, and makes me think of my earlier proposal to integrate my factors to manage events: with those factors you could just use an interaction (I used the % operator) to specify the sets of trials you want to equalize

epochs.equalize_counts(modality % side)
internally you could then use those factors:

i = modality % side # interaction effect
i.cells
[('A', 'L'), ('A', 'None'), ('A', 'R'), ('None', 'L'), ('None', 'None'), ('None', 'R'), ('V', 'L'), ('V', 'None'), ('V', 'R')]
for cell in i.cells:
... index = i == cell
... print index.sum()
...
72
0
73
0
31
0
73
0
71

Reply to this email directly or view it on GitHub.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

@mluessi I realized it might be as simple as sending my pull request for this design into a 0.6dev fork (which would also get the commits from 0.5 as they roll in). Is that right?

from mne-python.

christianbrodbeck avatar christianbrodbeck commented on July 24, 2024

@Eric89GXL

Sorry for being dense, but what is the ('None', 'None') in that example...?

On 'smiley' and 'button' trials, modality and side are = 'None' :), so

>>> interesting_trials = condition.isnot('smiley', 'button')
>>> i = i[interesting_trials]
>>> i.cells
[('A', 'L'), ('A', 'R'), ('V', 'L'), ('V', 'R')]

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

@Eric89GXL I am answering to your long text above

+1 for 1/

for 2/ this is somehow related with the mne.merge_events functions. I am wondering if this logic should be done in Epochs or a priori working with the events array.

just a remark: we should be careful not to have too clever objects. Think about how you would explain it to a new user.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

On 08.12.2012, at 10:34, Alexandre Gramfort [email protected] wrote:

@Eric89GXL I am answering to your long text above

+1 for 1/

for 2/ this is somehow related with the mne.merge_events functions. I am wondering if this logic should be done in Epochs or a priori working with the events array.

right, forgot about merge events. so as it is now you could at any time merge events before you create a new epoch.
2 would just involve an instance method that does this / remaps the event_id.
Or you simply would create a new events array and update the event_id dict without the need for a new smart instance method that confuses new users already totally overwhelmed by mne-python.
We could also think about a function that does this as kind of a compromise. say mne.merge_epochs_ids(epochs, ['aud_l', 'aud_'], 'aud') or so.

just a remark: we should be careful not to have too clever objects. Think about how you would explain it to a new user.


Reply to this email directly or view it on GitHub.

from mne-python.

agramfort avatar agramfort commented on July 24, 2024

Maybe it's 5 lines of code so yes but it gets messy lets avoid too much complexity.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

@agramfort I do expect it only to be about 5 lines of code. I'll draft a PR (for 0.6) and you can see what you think. The advantage to having the object be smarter is that, the way I've talked about, you can load all the data exactly once (and save the epochs FIF), and then take a subset of them as required by the analysis. It should be substantially faster than having to re-load from raw every time.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

as i proposed, if the reluctance is about the fear to pollute our Epochs object we could write function that lives in mne just like merge_events

On 08.12.2012, at 17:25, Eric89GXL [email protected] wrote:

@agramfort I do expect it only to be about 5 lines of code. I'll draft a PR (for 0.6) and you can see what you think. The advantage to having the object be smarter is that, the way I've talked about, you can load all the data exactly once (and save the epochs FIF), and then take a subset of them as required by the analysis. It should be substantially faster than having to re-load from raw every time.


Reply to this email directly or view it on GitHub.

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

this function then could do the remapping in a flexible fashion to also support factors, patsy and the other smart things from #133

so the object may remain a bit less smart and we can eat and have our cakes...

On 08.12.2012, at 17:25, Eric89GXL [email protected] wrote:

@agramfort I do expect it only to be about 5 lines of code. I'll draft a PR (for 0.6) and you can see what you think. The advantage to having the object be smarter is that, the way I've talked about, you can load all the data exactly once (and save the epochs FIF), and then take a subset of them as required by the analysis. It should be substantially faster than having to re-load from raw every time.


Reply to this email directly or view it on GitHub.

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

Ahh, I see what you mean. I'll do that!

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

Great! Let's think about how we do it in a way that is extendible, either
via smart args / type checking (dict, list, pandas whatever, R formula), or
a private function that does the remapping but with different public
interfaces that handle the specification from different, e.g. your proposed
merge_epochs_ids, that would take list + string, and then for example
gen/make/create/set_espochs_design which would process formulas or whatever
and so forth and whatever we will draw from #133 in the future. Makes
sense?

from mne-python.

larsoner avatar larsoner commented on July 24, 2024

@dengemann, @agramfort, @christianmbrodbeck now that we've made some progress on this, my vote is to close this issue, and start a new, more clearly titled issue when someone formulates the next good potential extension (for 0.6, presumably).

from mne-python.

dengemann avatar dengemann commented on July 24, 2024

Good bye #133 and thanks for the fruitful as well as nice discussion we had. Let's continue this in subsequent issues and PRs.

from mne-python.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.