Note
Go to the end to download the full example code
ECoG Movement decoding example#
This example notebook read openly accessible data from the publication Electrocorticography is superior to subthalamic local field potentials for movement decoding in Parkinson’s disease (Merk et al. 2022 <https://elifesciences.org/articles/75126>_). The dataset is available here.
For simplicity one example subject is automatically shipped within this repo at the py_neuromodulation/data folder, stored in iEEG BIDS format.
from sklearn import metrics, model_selection, linear_model
import matplotlib.pyplot as plt
import py_neuromodulation as nm
from py_neuromodulation import (
nm_analysis,
nm_decode,
nm_define_nmchannels,
nm_IO,
nm_plots,
nm_settings,
)
Let’s read the example using mne_bids. The resulting raw object is of type mne.RawArray. We can use the properties such as sampling frequency, channel names, channel types all from the mne array and create the nm_channels DataFrame:
(
RUN_NAME,
PATH_RUN,
PATH_BIDS,
PATH_OUT,
datatype,
) = nm_IO.get_paths_example_data()
(
raw,
data,
sfreq,
line_noise,
coord_list,
coord_names,
) = nm_IO.read_BIDS_data(
PATH_RUN=PATH_RUN, BIDS_PATH=PATH_BIDS, datatype=datatype
)
nm_channels = nm_define_nmchannels.set_channels(
ch_names=raw.ch_names,
ch_types=raw.get_channel_types(),
reference="default",
bads=raw.info["bads"],
new_names="default",
used_types=("ecog", "dbs", "seeg"),
target_keywords=["MOV_RIGHT"],
)
nm_channels
Extracting parameters from /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr...
Setting channel info structure...
Reading channel info from /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv.
Reading electrode coords from /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv.
This example contains the grip force movement traces, we’ll use the MOV_RIGHT channel as a decoding target channel. Let’s check some of the raw feature and time series traces:
plt.figure(figsize=(12, 4), dpi=300)
plt.subplot(121)
plt.plot(raw.times, data[-1, :])
plt.xlabel("Time [s]")
plt.ylabel("a.u.")
plt.title("Movement label")
plt.xlim(0, 20)
plt.subplot(122)
for idx, ch_name in enumerate(nm_channels.query("used == 1").name):
plt.plot(raw.times, data[idx, :] + idx * 300, label=ch_name)
plt.legend(bbox_to_anchor=(1, 0.5), loc="center left")
plt.title("ECoG + STN-LFP time series")
plt.xlabel("Time [s]")
plt.ylabel("Voltage a.u.")
plt.xlim(0, 20)
(0.0, 20.0)
settings = nm_settings.get_default_settings()
settings = nm_settings.set_settings_fast_compute(settings)
settings["features"]["welch"] = True
settings["features"]["fft"] = True
settings["features"]["bursts"] = True
settings["features"]["sharpwave_analysis"] = True
settings["features"]["coherence"] = True
settings["coherence"]["channels"] = [["LFP_RIGHT_0", "ECOG_RIGHT_0"]]
settings["coherence"]["frequency_bands"] = ["high beta", "low gamma"]
settings["sharpwave_analysis_settings"]["estimator"]["mean"] = []
for sw_feature in list(
settings["sharpwave_analysis_settings"]["sharpwave_features"].keys()
):
settings["sharpwave_analysis_settings"]["sharpwave_features"][
sw_feature
] = True
settings["sharpwave_analysis_settings"]["estimator"]["mean"].append(
sw_feature
)
stream = nm.Stream(
sfreq=sfreq,
nm_channels=nm_channels,
settings=settings,
line_noise=line_noise,
coord_list=coord_list,
coord_names=coord_names,
verbose=True,
)
Last batch took: 0.09 seconds
1.0 seconds of data processed
Last batch took: 0.09 seconds
1.1 seconds of data processed
Last batch took: 0.1 seconds
1.2 seconds of data processed
Last batch took: 0.1 seconds
1.3 seconds of data processed
Last batch took: 0.1 seconds
1.4 seconds of data processed
Last batch took: 0.1 seconds
1.5 seconds of data processed
Last batch took: 0.1 seconds
1.6 seconds of data processed
Last batch took: 0.1 seconds
1.7 seconds of data processed
Last batch took: 0.1 seconds
1.8 seconds of data processed
Last batch took: 0.1 seconds
1.9 seconds of data processed
Last batch took: 0.1 seconds
2.0 seconds of data processed
Last batch took: 0.1 seconds
2.1 seconds of data processed
Last batch took: 0.1 seconds
2.2 seconds of data processed
Last batch took: 0.1 seconds
2.3 seconds of data processed
Last batch took: 0.1 seconds
2.4 seconds of data processed
Last batch took: 0.1 seconds
2.5 seconds of data processed
Last batch took: 0.1 seconds
2.6 seconds of data processed
Last batch took: 0.1 seconds
2.7 seconds of data processed
Last batch took: 0.11 seconds
2.8 seconds of data processed
Last batch took: 0.11 seconds
2.9 seconds of data processed
Last batch took: 0.11 seconds
3.0 seconds of data processed
Last batch took: 0.11 seconds
3.1 seconds of data processed
Last batch took: 0.11 seconds
3.2 seconds of data processed
Last batch took: 0.12 seconds
3.3 seconds of data processed
Last batch took: 0.12 seconds
3.4 seconds of data processed
Last batch took: 0.12 seconds
3.5 seconds of data processed
Last batch took: 0.12 seconds
3.6 seconds of data processed
Last batch took: 0.12 seconds
3.7 seconds of data processed
Last batch took: 0.12 seconds
3.8 seconds of data processed
Last batch took: 0.12 seconds
3.9 seconds of data processed
Last batch took: 0.12 seconds
4.0 seconds of data processed
Last batch took: 0.12 seconds
4.1 seconds of data processed
Last batch took: 0.12 seconds
4.2 seconds of data processed
Last batch took: 0.11 seconds
4.3 seconds of data processed
Last batch took: 0.11 seconds
4.4 seconds of data processed
Last batch took: 0.11 seconds
4.5 seconds of data processed
Last batch took: 0.11 seconds
4.6 seconds of data processed
Last batch took: 0.11 seconds
4.7 seconds of data processed
Last batch took: 0.11 seconds
4.8 seconds of data processed
Last batch took: 0.11 seconds
4.9 seconds of data processed
Last batch took: 0.11 seconds
5.0 seconds of data processed
Last batch took: 0.1 seconds
5.1 seconds of data processed
Last batch took: 0.1 seconds
5.2 seconds of data processed
Last batch took: 0.1 seconds
5.3 seconds of data processed
Last batch took: 0.1 seconds
5.4 seconds of data processed
Last batch took: 0.1 seconds
5.5 seconds of data processed
Last batch took: 0.1 seconds
5.6 seconds of data processed
Last batch took: 0.1 seconds
5.7 seconds of data processed
Last batch took: 0.1 seconds
5.8 seconds of data processed
Last batch took: 0.1 seconds
5.9 seconds of data processed
Last batch took: 0.1 seconds
6.0 seconds of data processed
Last batch took: 0.1 seconds
6.1 seconds of data processed
Last batch took: 0.11 seconds
6.2 seconds of data processed
Last batch took: 0.1 seconds
6.3 seconds of data processed
Last batch took: 0.11 seconds
6.4 seconds of data processed
Last batch took: 0.11 seconds
6.5 seconds of data processed
Last batch took: 0.11 seconds
6.6 seconds of data processed
Last batch took: 0.1 seconds
6.7 seconds of data processed
Last batch took: 0.1 seconds
6.8 seconds of data processed
Last batch took: 0.1 seconds
6.9 seconds of data processed
Last batch took: 0.1 seconds
7.0 seconds of data processed
Last batch took: 0.1 seconds
7.1 seconds of data processed
Last batch took: 0.1 seconds
7.2 seconds of data processed
Last batch took: 0.1 seconds
7.3 seconds of data processed
Last batch took: 0.1 seconds
7.4 seconds of data processed
Last batch took: 0.1 seconds
7.5 seconds of data processed
Last batch took: 0.1 seconds
7.6 seconds of data processed
Last batch took: 0.1 seconds
7.7 seconds of data processed
Last batch took: 0.1 seconds
7.8 seconds of data processed
Last batch took: 0.1 seconds
7.9 seconds of data processed
Last batch took: 0.11 seconds
8.0 seconds of data processed
Last batch took: 0.11 seconds
8.1 seconds of data processed
Last batch took: 0.1 seconds
8.2 seconds of data processed
Last batch took: 0.11 seconds
8.3 seconds of data processed
Last batch took: 0.11 seconds
8.4 seconds of data processed
Last batch took: 0.11 seconds
8.5 seconds of data processed
Last batch took: 0.11 seconds
8.6 seconds of data processed
Last batch took: 0.11 seconds
8.7 seconds of data processed
Last batch took: 0.11 seconds
8.8 seconds of data processed
Last batch took: 0.11 seconds
8.9 seconds of data processed
Last batch took: 0.11 seconds
9.0 seconds of data processed
Last batch took: 0.11 seconds
9.1 seconds of data processed
Last batch took: 0.11 seconds
9.2 seconds of data processed
Last batch took: 0.11 seconds
9.3 seconds of data processed
Last batch took: 0.11 seconds
9.4 seconds of data processed
Last batch took: 0.11 seconds
9.5 seconds of data processed
Last batch took: 0.11 seconds
9.6 seconds of data processed
Last batch took: 0.11 seconds
9.7 seconds of data processed
Last batch took: 0.11 seconds
9.8 seconds of data processed
Last batch took: 0.11 seconds
9.9 seconds of data processed
Last batch took: 0.11 seconds
10.0 seconds of data processed
Last batch took: 0.11 seconds
10.1 seconds of data processed
Last batch took: 0.11 seconds
10.2 seconds of data processed
Last batch took: 0.11 seconds
10.3 seconds of data processed
Last batch took: 0.11 seconds
10.4 seconds of data processed
Last batch took: 0.11 seconds
10.5 seconds of data processed
Last batch took: 0.11 seconds
10.6 seconds of data processed
Last batch took: 0.11 seconds
10.7 seconds of data processed
Last batch took: 0.11 seconds
10.8 seconds of data processed
Last batch took: 0.11 seconds
10.9 seconds of data processed
Last batch took: 0.11 seconds
11.0 seconds of data processed
Last batch took: 0.11 seconds
11.1 seconds of data processed
Last batch took: 0.11 seconds
11.2 seconds of data processed
Last batch took: 0.11 seconds
11.3 seconds of data processed
Last batch took: 0.11 seconds
11.4 seconds of data processed
Last batch took: 0.11 seconds
11.5 seconds of data processed
Last batch took: 0.11 seconds
11.6 seconds of data processed
Last batch took: 0.11 seconds
11.7 seconds of data processed
Last batch took: 0.11 seconds
11.8 seconds of data processed
Last batch took: 0.11 seconds
11.9 seconds of data processed
Last batch took: 0.11 seconds
12.0 seconds of data processed
Last batch took: 0.11 seconds
12.1 seconds of data processed
Last batch took: 0.11 seconds
12.2 seconds of data processed
Last batch took: 0.11 seconds
12.3 seconds of data processed
Last batch took: 0.1 seconds
12.4 seconds of data processed
Last batch took: 0.1 seconds
12.5 seconds of data processed
Last batch took: 0.1 seconds
12.6 seconds of data processed
Last batch took: 0.1 seconds
12.7 seconds of data processed
Last batch took: 0.1 seconds
12.8 seconds of data processed
Last batch took: 0.1 seconds
12.9 seconds of data processed
Last batch took: 0.1 seconds
13.0 seconds of data processed
Last batch took: 0.1 seconds
13.1 seconds of data processed
Last batch took: 0.1 seconds
13.2 seconds of data processed
Last batch took: 0.1 seconds
13.3 seconds of data processed
Last batch took: 0.1 seconds
13.4 seconds of data processed
Last batch took: 0.1 seconds
13.5 seconds of data processed
Last batch took: 0.1 seconds
13.6 seconds of data processed
Last batch took: 0.1 seconds
13.7 seconds of data processed
Last batch took: 0.1 seconds
13.8 seconds of data processed
Last batch took: 0.1 seconds
13.9 seconds of data processed
Last batch took: 0.1 seconds
14.0 seconds of data processed
Last batch took: 0.1 seconds
14.1 seconds of data processed
Last batch took: 0.1 seconds
14.2 seconds of data processed
Last batch took: 0.11 seconds
14.3 seconds of data processed
Last batch took: 0.1 seconds
14.4 seconds of data processed
Last batch took: 0.1 seconds
14.5 seconds of data processed
Last batch took: 0.11 seconds
14.6 seconds of data processed
Last batch took: 0.11 seconds
14.7 seconds of data processed
Last batch took: 0.12 seconds
14.8 seconds of data processed
Last batch took: 0.13 seconds
14.9 seconds of data processed
Last batch took: 0.11 seconds
15.0 seconds of data processed
Last batch took: 0.11 seconds
15.1 seconds of data processed
Last batch took: 0.11 seconds
15.2 seconds of data processed
Last batch took: 0.11 seconds
15.3 seconds of data processed
Last batch took: 0.11 seconds
15.4 seconds of data processed
Last batch took: 0.11 seconds
15.5 seconds of data processed
Last batch took: 0.11 seconds
15.6 seconds of data processed
Last batch took: 0.11 seconds
15.7 seconds of data processed
Last batch took: 0.11 seconds
15.8 seconds of data processed
Last batch took: 0.11 seconds
15.9 seconds of data processed
Last batch took: 0.11 seconds
16.0 seconds of data processed
Last batch took: 0.11 seconds
16.1 seconds of data processed
Last batch took: 0.11 seconds
16.2 seconds of data processed
Last batch took: 0.11 seconds
16.3 seconds of data processed
Last batch took: 0.11 seconds
16.4 seconds of data processed
Last batch took: 0.11 seconds
16.5 seconds of data processed
Last batch took: 0.11 seconds
16.6 seconds of data processed
Last batch took: 0.11 seconds
16.7 seconds of data processed
Last batch took: 0.11 seconds
16.8 seconds of data processed
Last batch took: 0.1 seconds
16.9 seconds of data processed
Last batch took: 0.1 seconds
17.0 seconds of data processed
Last batch took: 0.1 seconds
17.1 seconds of data processed
Last batch took: 0.1 seconds
17.2 seconds of data processed
Last batch took: 0.1 seconds
17.3 seconds of data processed
Last batch took: 0.1 seconds
17.4 seconds of data processed
Last batch took: 0.1 seconds
17.5 seconds of data processed
Last batch took: 0.1 seconds
17.6 seconds of data processed
Last batch took: 0.1 seconds
17.7 seconds of data processed
Last batch took: 0.1 seconds
17.8 seconds of data processed
Last batch took: 0.1 seconds
17.9 seconds of data processed
Last batch took: 0.1 seconds
18.0 seconds of data processed
Last batch took: 0.1 seconds
18.1 seconds of data processed
Last batch took: 0.1 seconds
18.2 seconds of data processed
Last batch took: 0.1 seconds
18.3 seconds of data processed
Last batch took: 0.1 seconds
18.4 seconds of data processed
Last batch took: 0.1 seconds
18.5 seconds of data processed
Last batch took: 0.11 seconds
18.6 seconds of data processed
Last batch took: 0.11 seconds
18.7 seconds of data processed
Last batch took: 0.11 seconds
18.8 seconds of data processed
Last batch took: 0.11 seconds
18.9 seconds of data processed
Last batch took: 0.11 seconds
19.0 seconds of data processed
_SIDECAR.json saved to /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json
FEATURES.csv saved to /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv
settings.json saved to /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json
nm_channels.csv saved to /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv
Feature Analysis Movement#
The obtained performances can now be read and visualized using the nm_analysis.Feature_Reader
.
# initialize analyzer
feature_reader = nm_analysis.Feature_Reader(
feature_dir=PATH_OUT,
feature_file=RUN_NAME,
)
feature_reader.label_name = "MOV_RIGHT"
feature_reader.label = feature_reader.feature_arr["MOV_RIGHT"]
feature_reader.feature_arr.iloc[100:108, -6:]
(181, 552)
feature_reader._get_target_ch()
'MOV_RIGHT'
feature_reader.plot_target_averaged_channel(
ch="ECOG_RIGHT_0",
list_feature_keywords=None,
epoch_len=4,
threshold=0.5,
ytick_labelsize=7,
figsize_x=12,
figsize_y=12,
)
Feature epoch average figure saved to: /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png
feature_reader.plot_all_features(
ytick_labelsize=6,
clim_low=-2,
clim_high=2,
ch_used="ECOG_RIGHT_0",
time_limit_low_s=0,
time_limit_high_s=20,
normalize=True,
save=True,
)
nm_plots.plot_corr_matrix(
feature=feature_reader.feature_arr.filter(regex="ECOG_RIGHT_0"),
ch_name="ECOG_RIGHT_0-avgref",
feature_names=feature_reader.feature_arr.filter(
regex="ECOG_RIGHT_0-avgref"
).columns,
feature_file=feature_reader.feature_file,
show_plot=True,
figsize=(15, 15),
)
<Axes: title={'center': 'Correlation matrix features channel: ECOG_RIGHT_0-avgref'}>
Decoding#
The main focus of the py_neuromodulation pipeline is feature estimation. Nevertheless, the user can also use the pipeline for machine learning decoding. It can be used for regression and classification problems and also dimensionality reduction such as PCA and CCA.
Here, we show an example using the XGBOOST classifier. The used labels came from a continuous grip force movement target, named “MOV_RIGHT”.
First we initialize the Decoder
class, which the specified validation method, here being a simple 3-fold cross validation,
the evaluation metric, used machine learning model, and the channels we want to evaluate performances for.
There are many more implemented methods, but we will here limit it to the ones presented.
model = linear_model.LinearRegression()
feature_reader.decoder = nm_decode.Decoder(
features=feature_reader.feature_arr,
label=feature_reader.label,
label_name=feature_reader.label_name,
used_chs=feature_reader.used_chs,
model=model,
eval_method=metrics.r2_score,
cv_method=model_selection.KFold(n_splits=3, shuffle=True),
)
performances = feature_reader.run_ML_model(
estimate_channels=True,
estimate_gridpoints=False,
estimate_all_channels_combined=True,
save_results=True,
)
model being saved to: /home/docs/checkouts/readthedocs.org/user_builds/py-neuromodulation/envs/latest/lib/python3.11/site-packages/py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p
The performances are a dictionary that can be transformed into a DataFrame:
df_per = feature_reader.get_dataframe_performances(performances)
df_per
ax = nm_plots.plot_df_subjects(
df_per,
x_col="sub",
y_col="performance_test",
hue="ch_type",
PATH_SAVE=PATH_OUT / RUN_NAME / (RUN_NAME + "_decoding_performance.png"),
figsize_tuple=(8, 5),
)
ax.set_ylabel(r"$R^2$ Correlation")
ax.set_xlabel("Subject 000")
ax.set_title("Performance comparison Movement decoding")
plt.tight_layout()
Total running time of the script: (0 minutes 27.049 seconds)