-
Notifications
You must be signed in to change notification settings - Fork 34
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
Ransac Comparison Example #53
Open
yjmantilla
wants to merge
14
commits into
sappelhoff:main
Choose a base branch
from
yjmantilla:ransac_compare
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
354e2bc
first version of the comparison example
yjmantilla 000612a
add autoreject+reqs to requirements-dev
sappelhoff d9f35ef
add tqdm for autoreject
sappelhoff bcbae67
Apply suggestions from code review
yjmantilla 0314f94
correlation confusion corrected
yjmantilla 57043ac
1 epoch less bug corrected
yjmantilla 6cbe0fb
formatting fixes
yjmantilla debef8c
Merge branch 'master' into ransac_compare
sappelhoff 1d7e03f
try to adjust with master branch
sappelhoff ccd1e51
do some cleanup and add docs
sappelhoff 3cbfcf0
install autoreject from master
sappelhoff baac068
pass into to AR ransac until passing RandomState is possible
sappelhoff 0600524
Revert "pass into to AR ransac until passing RandomState is possible"
sappelhoff eff3d26
fix docs link, bump cache version
sappelhoff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
""" | ||
=============================================== | ||
RANSAC comparison between pyprep and autoreject | ||
=============================================== | ||
|
||
Next to the RANSAC implementation in ``pyprep``, | ||
there is another implementation that makes use of MNE-Python. | ||
That alternative RANSAC implementation can be found in the | ||
`"autoreject" package <https://github.com/autoreject/autoreject/>`_. | ||
|
||
In this example, we make a basic comparison between the two implementations. | ||
|
||
#. by running them on the same simulated data | ||
#. by running them on the same "real" data | ||
|
||
|
||
.. currentmodule:: pyprep | ||
""" | ||
|
||
# Authors: Yorguin Mantilla <[email protected]> | ||
# | ||
# License: MIT | ||
|
||
# %% | ||
# First we import what we need for this example. | ||
import numpy as np | ||
import mne | ||
from scipy import signal as signal | ||
from time import perf_counter | ||
from autoreject import Ransac | ||
import pyprep.ransac as ransac_pyprep | ||
|
||
|
||
# %% | ||
# Now let's make some arbitrary MNE raw object for demonstration purposes. | ||
# We will think of good channels as sine waves and bad channels correlated with | ||
# each other as sawtooths. The RANSAC will be biased towards sines in its | ||
# prediction (they are the majority) so it will identify the sawtooths as bad. | ||
|
||
# Set a random seed to make this example reproducible | ||
rng = np.random.RandomState(435656) | ||
|
||
# start defining some key aspects for our simulated data | ||
sfreq = 1000.0 | ||
montage = mne.channels.make_standard_montage("standard_1020") | ||
ch_names = montage.ch_names | ||
n_chans = len(ch_names) | ||
info = mne.create_info(ch_names=ch_names, sfreq=sfreq, ch_types=["eeg"] * n_chans) | ||
time = np.arange(0, 30, 1.0 / sfreq) # 30 seconds of recording | ||
|
||
# randomly pick some "bad" channels (sawtooths) | ||
n_bad_chans = 3 | ||
bad_channels = rng.choice(np.arange(n_chans), n_bad_chans, replace=False) | ||
bad_channels = [int(i) for i in bad_channels] | ||
bad_ch_names = [ch_names[i] for i in bad_channels] | ||
|
||
# The frequency components to use in the signal for good and bad channels | ||
freq_good = 20 | ||
freq_bad = 20 | ||
|
||
# Generate the data: sinewaves for "good", sawtooths for "bad" channels | ||
X = [ | ||
signal.sawtooth(2 * np.pi * freq_bad * time) | ||
if i in bad_channels | ||
else np.sin(2 * np.pi * freq_good * time) | ||
for i in range(n_chans) | ||
] | ||
|
||
# Scale the signal amplitude and add noise. | ||
X = 2e-5 * np.array(X) + 1e-5 * np.random.random((n_chans, time.shape[0])) | ||
|
||
# Finally, put it all together as an mne "Raw" object. | ||
raw = mne.io.RawArray(X, info) | ||
raw.set_montage(montage, verbose=False) | ||
|
||
# Print, which channels are simulated as "bad" | ||
print(bad_ch_names) | ||
|
||
# %% | ||
# Configure RANSAC parameters | ||
n_samples = 50 | ||
fraction_good = 0.25 | ||
corr_thresh = 0.75 | ||
fraction_bad = 0.4 | ||
corr_window_secs = 5.0 | ||
|
||
# %% | ||
# autoreject's RANSAC | ||
ransac_ar = Ransac( | ||
picks=None, | ||
n_resample=n_samples, | ||
min_channels=fraction_good, | ||
min_corr=corr_thresh, | ||
unbroken_time=fraction_bad, | ||
n_jobs=1, | ||
random_state=rng, | ||
) | ||
epochs = mne.make_fixed_length_epochs( | ||
raw, | ||
duration=corr_window_secs, | ||
preload=True, | ||
reject_by_annotation=False, | ||
verbose=None, | ||
) | ||
|
||
start_time = perf_counter() | ||
ransac_ar = ransac_ar.fit(epochs) | ||
print("--- %s seconds ---" % (perf_counter() - start_time)) | ||
|
||
corr_ar = ransac_ar.corr_ | ||
bad_by_ransac_ar = ransac_ar.bad_chs_ | ||
|
||
# Check channels that go bad together by RANSAC | ||
print("autoreject bad chs:", bad_by_ransac_ar) | ||
assert set(bad_ch_names) == set(bad_by_ransac_ar) | ||
|
||
# %% | ||
# pyprep's RANSAC | ||
|
||
start_time = perf_counter() | ||
bad_by_ransac_pyprep, corr_pyprep = ransac_pyprep.find_bad_by_ransac( | ||
data=raw._data.copy(), | ||
sample_rate=raw.info["sfreq"], | ||
complete_chn_labs=np.asarray(raw.info["ch_names"]), | ||
chn_pos=raw._get_channel_positions(), | ||
exclude=[], | ||
n_samples=n_samples, | ||
sample_prop=fraction_good, | ||
corr_thresh=corr_thresh, | ||
frac_bad=fraction_bad, | ||
corr_window_secs=corr_window_secs, | ||
channel_wise=False, | ||
random_state=rng, | ||
) | ||
print("--- %s seconds ---" % (perf_counter() - start_time)) | ||
|
||
# Check channels that go bad together by RANSAC | ||
print("pyprep bad chs:", bad_by_ransac_pyprep) | ||
assert set(bad_ch_names) == set(bad_by_ransac_pyprep) | ||
|
||
# %% | ||
# Now we test the algorithms on real EEG data. | ||
# Let's download some data for testing. | ||
data_paths = mne.datasets.eegbci.load_data(subject=4, runs=1, update_path=True) | ||
fname_test_file = data_paths[0] | ||
|
||
# %% | ||
# Load data and prepare it | ||
|
||
raw = mne.io.read_raw_edf(fname_test_file, preload=True) | ||
|
||
# The eegbci data has non-standard channel names. We need to rename them: | ||
mne.datasets.eegbci.standardize(raw) | ||
|
||
# Add a montage to the data | ||
montage_kind = "standard_1005" | ||
montage = mne.channels.make_standard_montage(montage_kind) | ||
raw.set_montage(montage) | ||
|
||
|
||
# %% | ||
# autoreject's RANSAC | ||
ransac_ar = Ransac( | ||
picks=None, | ||
n_resample=n_samples, | ||
min_channels=fraction_good, | ||
min_corr=corr_thresh, | ||
unbroken_time=fraction_bad, | ||
n_jobs=1, | ||
random_state=rng, | ||
) | ||
epochs = mne.make_fixed_length_epochs( | ||
raw, | ||
duration=corr_window_secs, | ||
preload=True, | ||
reject_by_annotation=False, | ||
verbose=None, | ||
) | ||
|
||
start_time = perf_counter() | ||
ransac_ar = ransac_ar.fit(epochs) | ||
print("--- %s seconds ---" % (perf_counter() - start_time)) | ||
|
||
corr_ar = ransac_ar.corr_ | ||
bad_by_ransac_ar = ransac_ar.bad_chs_ | ||
|
||
# Check channels that go bad together by RANSAC | ||
print("autoreject bad chs:", bad_by_ransac_ar) | ||
|
||
|
||
# %% | ||
# pyprep's RANSAC | ||
|
||
start_time = perf_counter() | ||
bad_by_ransac_pyprep, corr_pyprep = ransac_pyprep.find_bad_by_ransac( | ||
data=raw._data.copy(), | ||
sample_rate=raw.info["sfreq"], | ||
complete_chn_labs=np.asarray(raw.info["ch_names"]), | ||
chn_pos=raw._get_channel_positions(), | ||
exclude=[], | ||
n_samples=n_samples, | ||
sample_prop=fraction_good, | ||
corr_thresh=corr_thresh, | ||
frac_bad=fraction_bad, | ||
corr_window_secs=corr_window_secs, | ||
channel_wise=False, | ||
random_state=rng, | ||
) | ||
print("--- %s seconds ---" % (perf_counter() - start_time)) | ||
|
||
# Check channels that go bad together by RANSAC | ||
print("pyprep bad chs:", bad_by_ransac_pyprep) | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
git+git://github.com/autoreject/autoreject.git@master#egg=autoreject | ||
black | ||
check-manifest | ||
flake8 | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
we should add one more code block below to easily show: