Add example for Python/R interoperability using mass univariate t-test#13729
Add example for Python/R interoperability using mass univariate t-test#13729aman-coder03 wants to merge 17 commits intomne-tools:mainfrom
Conversation
|
|
|
Being fixed in #13728 |
55cfb78 to
fd55fd3
Compare
|
CircleCI failure is real I think you need to add |
|
@larsoner the failure is because |
|
Can you try updating the CI to have R installed? Would be doable with an |
|
towncrier fragments errors about "Famous Raj Bhat" are pre existing and unrelated to this PR.. @larsoner |
Hi @aman-coder03 , Can you specify where you are getting an error. Would Love to co-ordinate with you to fix the issue. |
No need, I fixed it in |
|
thanks @larsoner !! |
tools/circleci_dependencies.sh
Outdated
| @@ -1,5 +1,7 @@ | |||
| #!/bin/bash -ef | |||
|
|
|||
| sudo apt-get install -y r-base libtirpc-dev | |||
There was a problem hiding this comment.
This isn't the best place, tools/circleci_bash_env.sh would make more sense
drammock
left a comment
There was a problem hiding this comment.
In the long run, I think it would be better / more interesting to showcase an example of something where R/rpy2 actually makes it easier to achieve the desired result (i.e., demonstrate something that is hard to do in Python but easy in R). But for now, however, let's stick with the t-test (subject to the modifications I propose in the below comments)
examples/preprocessing/r_interop.py
Outdated
There was a problem hiding this comment.
file is in the wrong folder. This should be in stats folder, not preprocessing
doc/changes/dev/13729.newfeature.rst
Outdated
| @@ -0,0 +1 @@ | |||
| Add example showing how to run a mass-univariate t-test in both Python and R using ``rpy2``, demonstrating Python–R interoperability with :class:`~mne.Epochs` data by `Aman Srivastava`_. (:gh:`13729`) No newline at end of file | |||
There was a problem hiding this comment.
| Add example showing how to run a mass-univariate t-test in both Python and R using ``rpy2``, demonstrating Python–R interoperability with :class:`~mne.Epochs` data by `Aman Srivastava`_. (:gh:`13729`) | |
| Add example showing how to interoperate with R using ``rpy2``, by `Aman Srivastava`_. |
PR number will get added automatically, and the details (epochs, t-test) are not really the point.
examples/preprocessing/r_interop.py
Outdated
| # We average over channels and time to get one value per epoch, then run | ||
| # a 2-sample t-test comparing the two conditions. |
There was a problem hiding this comment.
this is... not something anyone would ever do in practice. What we show can be much simpler than what most real neuroscience studies do, but it should at least be plausible. Probably the shortest path to something plausible would be:
- pick a few sensors as an "ROI" (ones that should differ between auditory L vs R conditions)
- pick a typical ERP time window
- average over just those channels and those times
- do the t-test (1 observation per epoch)
Probably worthwhile to do a plot_compare_evokeds or similar, as a way to motivate what the stats are testing.
examples/preprocessing/r_interop.py
Outdated
| with localconverter(default_converter + numpy2ri.converter): | ||
| r_left = ro.FloatVector(epochs_left) | ||
| r_right = ro.FloatVector(epochs_right) | ||
|
|
||
| r_ttest = ro.r["t.test"] | ||
| result = r_ttest(r_left, r_right, **{"var.equal": True}) | ||
|
|
||
| t_r = float(result.rx2("statistic")[0]) | ||
| p_r = float(result.rx2("p.value")[0]) |
There was a problem hiding this comment.
these steps are the important "new learning" from this, so there should be more explanation/comments here. E.g., things like
rpy2.robjects(imported here asro) has an attributerthat provides access to the R namespace, through a dictionary-like interface- Unlike Python,
Rfunctions can have parameter names that contain a., so you may need to pass function arguments as a dict and unpack them with** - what is
FloatVectorand why do we need it - what is the
rx2attribute of the result, and why do we need the[0]after it
|
moved example to |
| # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| # | ||
| # We first plot the evoked responses to motivate the statistical test. | ||
| # Auditory left vs right stimuli should differ over lateral temporal sensors. |
There was a problem hiding this comment.
EEG059 is not really lateral/temporal. Use MEG 1323
| # and a typical N1 time window (80–120 ms). This gives one value per epoch, | ||
| # which is a plausible neuroscience analysis. | ||
|
|
||
| roi_channels = ["EEG 059", "EEG 058", "EEG 057"] |
There was a problem hiding this comment.
| roi_channels = ["EEG 059", "EEG 058", "EEG 057"] | |
| roi_channels = ["MEG 1323"] |
| .crop(tmin_roi, tmax_roi) | ||
| .get_data(picks=roi_channels) | ||
| .mean(axis=(1, 2)) | ||
| ) |
There was a problem hiding this comment.
unnecessary copying. something this should be fine:
epochs.crop(tmin, tmax).pick(roi_chs)
epo_left = epochs["auditory/left"].get_data().mean(axis=(1, 2))
epo_right = epochs["auditory/right"].get_data().mean(axis=(1, 2))|
|
||
| mne.viz.plot_compare_evokeds( | ||
| {"auditory/left": evoked_left, "auditory/right": evoked_right}, | ||
| picks="EEG 059", |
There was a problem hiding this comment.
| picks="EEG 059", | |
| picks="MEG 1323", |
| .. _r-interop: | ||
| ============================================== | ||
| Mass-univariate t-test: Python and R compared |
There was a problem hiding this comment.
Let's make the point of the tutorial clearer in the title
| Mass-univariate t-test: Python and R compared | |
| Integrating with R via rpy2 |
Reference issue (if any)
closes #13721
What does this implement/fix?
adds a tutorial example that runs a 2 sample mass univariate t-test on Epochs data,.., first in python using
scipy.statsthen the same test in R viarpy2showing that both give identical results. The goal is to show the users how to get data into R, run stats there, and then bring the results back into Python, as starting point for more complex R based workflowsAdditional information
follows the approach suggested by @larsoner in the issue ...keeping it simple and focused on one clear use case rather than trying to build general purpose utility