py-neuromodulation 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +233 -0
- docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
- docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +239 -0
- docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
- docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
- docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +76 -0
- docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +97 -0
- docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +240 -0
- docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +233 -0
- docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +63 -0
- docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +210 -0
- docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +192 -0
- docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +219 -0
- docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +121 -0
- docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +68 -0
- docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
- docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +189 -0
- docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_0_first_demo.py +189 -0
- docs/source/auto_examples/plot_1_example_BIDS.py +240 -0
- docs/source/auto_examples/plot_2_example_add_feature.py +76 -0
- docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +219 -0
- docs/source/auto_examples/plot_4_example_gridPointProjection.py +210 -0
- docs/source/auto_examples/plot_5_example_rmap_computing.py +64 -0
- docs/source/auto_examples/plot_6_real_time_demo.py +121 -0
- docs/source/conf.py +105 -0
- examples/plot_0_first_demo.py +189 -0
- examples/plot_1_example_BIDS.py +240 -0
- examples/plot_2_example_add_feature.py +76 -0
- examples/plot_3_example_sharpwave_analysis.py +219 -0
- examples/plot_4_example_gridPointProjection.py +210 -0
- examples/plot_5_example_rmap_computing.py +64 -0
- examples/plot_6_real_time_demo.py +121 -0
- packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +4 -0
- packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +104 -0
- packages/realtime_decoding/build/lib/realtime_decoding/features.py +163 -0
- packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +15 -0
- packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +345 -0
- packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +54 -0
- packages/tmsi/build/lib/TMSiFileFormats/__init__.py +37 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +36 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +200 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +496 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +236 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +977 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +35 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +116 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +294 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +229 -0
- packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +102 -0
- packages/tmsi/build/lib/TMSiPlotters/__init__.py +2 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +39 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +234 -0
- packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +440 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +44 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +446 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +589 -0
- packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +1326 -0
- packages/tmsi/build/lib/TMSiSDK/__init__.py +54 -0
- packages/tmsi/build/lib/TMSiSDK/device.py +588 -0
- packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +1764 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +34 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +1366 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +520 -0
- packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +165 -0
- packages/tmsi/build/lib/TMSiSDK/error.py +95 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data.py +63 -0
- packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +99 -0
- packages/tmsi/build/lib/TMSiSDK/settings.py +45 -0
- packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +111 -0
- packages/tmsi/build/lib/__init__.py +4 -0
- packages/tmsi/build/lib/apex_sdk/__init__.py +34 -0
- packages/tmsi/build/lib/apex_sdk/device/__init__.py +41 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +1009 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +239 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +668 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +1611 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +38 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +44 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +36 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +48 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +108 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +39 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +77 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +150 -0
- packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +129 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +59 -0
- packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +57 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +83 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +201 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +103 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +43 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +50 -0
- packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +118 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +33 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +44 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +50 -0
- packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +136 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +126 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +113 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +134 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +60 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +42 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +72 -0
- packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +98 -0
- py_neuromodulation/{helper.py → _write_example_dataset_helper.py} +1 -1
- py_neuromodulation/nm_EpochStream.py +2 -3
- py_neuromodulation/nm_IO.py +43 -70
- py_neuromodulation/nm_RMAP.py +308 -11
- py_neuromodulation/nm_analysis.py +1 -1
- py_neuromodulation/nm_artifacts.py +25 -0
- py_neuromodulation/nm_bispectra.py +64 -29
- py_neuromodulation/nm_bursts.py +44 -30
- py_neuromodulation/nm_coherence.py +2 -1
- py_neuromodulation/nm_features.py +4 -2
- py_neuromodulation/nm_filter.py +63 -32
- py_neuromodulation/nm_filter_preprocessing.py +91 -0
- py_neuromodulation/nm_fooof.py +47 -29
- py_neuromodulation/nm_mne_connectivity.py +1 -1
- py_neuromodulation/nm_normalization.py +50 -74
- py_neuromodulation/nm_oscillatory.py +151 -31
- py_neuromodulation/nm_plots.py +13 -10
- py_neuromodulation/nm_rereference.py +10 -8
- py_neuromodulation/nm_run_analysis.py +28 -13
- py_neuromodulation/nm_sharpwaves.py +103 -136
- py_neuromodulation/nm_stats.py +44 -30
- py_neuromodulation/nm_stream_abc.py +18 -10
- py_neuromodulation/nm_stream_offline.py +181 -40
- py_neuromodulation/utils/_logging.py +24 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -142
- py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
- py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
- tests/__init__.py +0 -0
- tests/conftest.py +117 -0
- tests/test_all_examples.py +10 -0
- tests/test_all_features.py +63 -0
- tests/test_bispectra.py +70 -0
- tests/test_bursts.py +105 -0
- tests/test_feature_sampling_rates.py +143 -0
- tests/test_fooof.py +16 -0
- tests/test_initalization_offline_stream.py +41 -0
- tests/test_multiprocessing.py +58 -0
- tests/test_nan_values.py +29 -0
- tests/test_nm_filter.py +95 -0
- tests/test_nm_resample.py +63 -0
- tests/test_normalization_settings.py +146 -0
- tests/test_notch_filter.py +31 -0
- tests/test_osc_features.py +424 -0
- tests/test_preprocessing_filter.py +151 -0
- tests/test_rereference.py +171 -0
- tests/test_sampling.py +57 -0
- tests/test_settings_change_after_init.py +76 -0
- tests/test_sharpwave.py +165 -0
- tests/test_target_channel_add.py +100 -0
- tests/test_timing.py +80 -0
- py_neuromodulation/data/README +0 -6
- py_neuromodulation/data/dataset_description.json +0 -8
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_decoding_performance.png +0 -0
- py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv +0 -11
- py_neuromodulation/data/participants.json +0 -32
- py_neuromodulation/data/participants.tsv +0 -2
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
- py_neuromodulation/grid_cortex.tsv +0 -40
- py_neuromodulation/grid_subcortex.tsv +0 -1429
- py_neuromodulation/nm_settings.json +0 -290
- py_neuromodulation/plots/STN_surf.mat +0 -0
- py_neuromodulation/plots/Vertices.mat +0 -0
- py_neuromodulation/plots/faces.mat +0 -0
- py_neuromodulation/plots/grid.mat +0 -0
- py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
- py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
- py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
- py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
- py_neuromodulation-0.0.2.dist-info/RECORD +0 -73
- /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
- {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analyzing temporal features
|
|
3
|
+
===========================
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# %%
|
|
8
|
+
# Time series data can be characterized using oscillatory components, but assumptions of sinusoidality are for real data rarely fulfilled.
|
|
9
|
+
# See *"Brain Oscillations and the Importance of Waveform Shape"* `Cole et al 2017 <https://doi.org/10.1016/j.tics.2016.12.008>`_ for a great motivation.
|
|
10
|
+
# We implemented here temporal characteristics based on individual trough and peak relations,
|
|
11
|
+
# based on the :meth:~`scipy.signal.find_peaks` method. The function parameter *distance* can be specified in the *nm_settings.json*.
|
|
12
|
+
# Temporal features can be calculated twice for troughs and peaks. In the settings, this can be specified by setting *estimate* to true
|
|
13
|
+
# in *detect_troughs* and/or *detect_peaks*. A statistical measure (e.g. mean, max, median, var) can be defined as a resulting feature from the peak and
|
|
14
|
+
# trough estimates using the *apply_estimator_between_peaks_and_troughs* setting.
|
|
15
|
+
#
|
|
16
|
+
# In py_neuromodulation the following characteristics are implemented:
|
|
17
|
+
#
|
|
18
|
+
# .. note::
|
|
19
|
+
# The nomenclature is written here for sharpwave troughs, but detection of peak characteristics can be computed in the same way.
|
|
20
|
+
#
|
|
21
|
+
# - prominence:
|
|
22
|
+
# :math:`V_{prominence} = |\frac{V_{peak-left} + V_{peak-right}}{2}| - V_{trough}`
|
|
23
|
+
# - sharpness:
|
|
24
|
+
# :math:`V_{sharpnesss} = \frac{(V_{trough} - V_{trough-5 ms}) + (V_{trough} - V_{trough+5 ms})}{2}`
|
|
25
|
+
# - rise and decay rise time
|
|
26
|
+
# - rise and decay steepness
|
|
27
|
+
# - width (between left and right peaks)
|
|
28
|
+
# - interval (between troughs)
|
|
29
|
+
#
|
|
30
|
+
# Additionally, different filter ranges can be parametrized using the *filter_ranges_hz* setting.
|
|
31
|
+
# Filtering is necessary to remove high frequent signal fluctuations, but limits also the true estimation of sharpness and prominence due to signal smoothing.
|
|
32
|
+
|
|
33
|
+
import seaborn as sb
|
|
34
|
+
from matplotlib import pyplot as plt
|
|
35
|
+
from scipy import signal
|
|
36
|
+
import numpy as np
|
|
37
|
+
|
|
38
|
+
import py_neuromodulation as nm
|
|
39
|
+
from py_neuromodulation import (
|
|
40
|
+
nm_define_nmchannels,
|
|
41
|
+
nm_IO,
|
|
42
|
+
nm_settings,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# %%
|
|
47
|
+
# We will first read the example ECoG data and plot the identified features on the filtered time series.
|
|
48
|
+
|
|
49
|
+
RUN_NAME, PATH_RUN, PATH_BIDS, PATH_OUT, datatype = nm_IO.get_paths_example_data()
|
|
50
|
+
|
|
51
|
+
(
|
|
52
|
+
raw,
|
|
53
|
+
data,
|
|
54
|
+
sfreq,
|
|
55
|
+
line_noise,
|
|
56
|
+
coord_list,
|
|
57
|
+
coord_names,
|
|
58
|
+
) = nm_IO.read_BIDS_data(
|
|
59
|
+
PATH_RUN=PATH_RUN,
|
|
60
|
+
BIDS_PATH=PATH_BIDS, datatype=datatype
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# %%
|
|
64
|
+
settings = nm_settings.get_default_settings()
|
|
65
|
+
settings = nm_settings.set_settings_fast_compute(settings)
|
|
66
|
+
|
|
67
|
+
settings["features"]["fft"] = True
|
|
68
|
+
settings["features"]["bursts"] = False
|
|
69
|
+
settings["features"]["sharpwave_analysis"] = True
|
|
70
|
+
settings["features"]["coherence"] = False
|
|
71
|
+
|
|
72
|
+
settings["sharpwave_analysis_settings"]["estimator"]["mean"] = []
|
|
73
|
+
for sw_feature in list(
|
|
74
|
+
settings["sharpwave_analysis_settings"]["sharpwave_features"].keys()
|
|
75
|
+
):
|
|
76
|
+
settings["sharpwave_analysis_settings"]["sharpwave_features"][sw_feature] = True
|
|
77
|
+
settings["sharpwave_analysis_settings"]["estimator"]["mean"].append(sw_feature)
|
|
78
|
+
|
|
79
|
+
nm_channels = nm_define_nmchannels.set_channels(
|
|
80
|
+
ch_names=raw.ch_names,
|
|
81
|
+
ch_types=raw.get_channel_types(),
|
|
82
|
+
reference="default",
|
|
83
|
+
bads=raw.info["bads"],
|
|
84
|
+
new_names="default",
|
|
85
|
+
used_types=("ecog", "dbs", "seeg"),
|
|
86
|
+
target_keywords=["MOV_RIGHT"]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
stream = nm.Stream(
|
|
90
|
+
sfreq=sfreq,
|
|
91
|
+
nm_channels=nm_channels,
|
|
92
|
+
settings=settings,
|
|
93
|
+
line_noise=line_noise,
|
|
94
|
+
coord_list=coord_list,
|
|
95
|
+
coord_names=coord_names,
|
|
96
|
+
verbose=False,
|
|
97
|
+
)
|
|
98
|
+
sw_analyzer = stream.run_analysis.features.features[1]
|
|
99
|
+
|
|
100
|
+
# %%
|
|
101
|
+
# The plotted example time series, visualized on a short time scale, shows the relation of identified peaks, troughs, and estimated features:
|
|
102
|
+
data_plt = data[5, 1000:4000]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
sw_analyzer._initialize_sw_features()
|
|
106
|
+
filtered_dat = np.convolve(
|
|
107
|
+
data_plt,
|
|
108
|
+
sw_analyzer.list_filter[0][1],
|
|
109
|
+
mode="same"
|
|
110
|
+
)
|
|
111
|
+
#filtered_dat = filtered_dat[500:-500]
|
|
112
|
+
|
|
113
|
+
troughs = signal.find_peaks(-filtered_dat, distance=10)[0]
|
|
114
|
+
peaks = signal.find_peaks(filtered_dat, distance=5)[0]
|
|
115
|
+
|
|
116
|
+
sw_analyzer.data_process_sw = filtered_dat
|
|
117
|
+
sw_analyzer.analyze_waveform()
|
|
118
|
+
|
|
119
|
+
WIDTH = BAR_WIDTH = 4
|
|
120
|
+
BAR_OFFSET = 50
|
|
121
|
+
OFFSET_TIME_SERIES = -100
|
|
122
|
+
SCALE_TIMESERIES = 1
|
|
123
|
+
|
|
124
|
+
hue_colors = sb.color_palette("viridis_r", 6)
|
|
125
|
+
|
|
126
|
+
plt.figure(figsize=(5, 3), dpi=300)
|
|
127
|
+
plt.plot(OFFSET_TIME_SERIES + data_plt, color="gray", linewidth=0.5, alpha=0.5, label="original ECoG data")
|
|
128
|
+
plt.plot(OFFSET_TIME_SERIES + filtered_dat*SCALE_TIMESERIES, linewidth=0.5, color="black", label="[5-30]Hz filtered data")
|
|
129
|
+
|
|
130
|
+
plt.plot(peaks, OFFSET_TIME_SERIES + filtered_dat[peaks]*SCALE_TIMESERIES, "x", label="peaks",markersize=3, color="darkgray")
|
|
131
|
+
plt.plot(troughs, OFFSET_TIME_SERIES + filtered_dat[troughs]*SCALE_TIMESERIES, "x", label="troughs", markersize=3, color="lightgray")
|
|
132
|
+
|
|
133
|
+
plt.bar(troughs+BAR_WIDTH, np.array(sw_analyzer.prominence)*4, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[0], label="Prominence", alpha=0.5)
|
|
134
|
+
plt.bar(troughs+BAR_WIDTH*2, -np.array(sw_analyzer.sharpness)*6, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[1], label="Sharpness", alpha=0.5)
|
|
135
|
+
plt.bar(troughs+BAR_WIDTH*3, np.array(sw_analyzer.interval)*5, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[2], label="Interval", alpha=0.5)
|
|
136
|
+
plt.bar(troughs+BAR_WIDTH*4, np.array(sw_analyzer.rise_time)*5, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[3], label="Rise time", alpha=0.5)
|
|
137
|
+
|
|
138
|
+
plt.xticks(np.arange(0, data_plt.shape[0], 200), np.round(np.arange(0, int(data_plt.shape[0]/1000), 0.2), 2))
|
|
139
|
+
plt.xlabel("Time [s]")
|
|
140
|
+
plt.title("Temporal waveform shape features")
|
|
141
|
+
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
|
142
|
+
plt.ylim(-550, 700)
|
|
143
|
+
plt.xlim(0, 200)
|
|
144
|
+
plt.ylabel("a.u.")
|
|
145
|
+
plt.tight_layout()
|
|
146
|
+
|
|
147
|
+
# %%
|
|
148
|
+
# See in the following example a time series example, that is aligned to movement. With movement onset the prominence, sharpness, and interval features are reduced:
|
|
149
|
+
|
|
150
|
+
plt.figure(figsize=(8, 5), dpi=300)
|
|
151
|
+
plt.plot(OFFSET_TIME_SERIES + data_plt, color="gray", linewidth=0.5, alpha=0.5, label="original ECoG data")
|
|
152
|
+
plt.plot(OFFSET_TIME_SERIES + filtered_dat*SCALE_TIMESERIES, linewidth=0.5, color="black", label="[5-30]Hz filtered data")
|
|
153
|
+
|
|
154
|
+
plt.plot(peaks, OFFSET_TIME_SERIES + filtered_dat[peaks]*SCALE_TIMESERIES, "x", label="peaks",markersize=3, color="darkgray")
|
|
155
|
+
plt.plot(troughs, OFFSET_TIME_SERIES + filtered_dat[troughs]*SCALE_TIMESERIES, "x", label="troughs", markersize=3, color="lightgray")
|
|
156
|
+
|
|
157
|
+
plt.bar(troughs+BAR_WIDTH, np.array(sw_analyzer.prominence)*4, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[0], label="Prominence", alpha=0.5)
|
|
158
|
+
plt.bar(troughs+BAR_WIDTH*2, -np.array(sw_analyzer.sharpness)*6, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[1], label="Sharpness", alpha=0.5)
|
|
159
|
+
plt.bar(troughs+BAR_WIDTH*3, np.array(sw_analyzer.interval)*5, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[2], label="Interval", alpha=0.5)
|
|
160
|
+
plt.bar(troughs+BAR_WIDTH*4, np.array(sw_analyzer.rise_time)*5, bottom=BAR_OFFSET, width=WIDTH, color=hue_colors[3], label="Rise time", alpha=0.5)
|
|
161
|
+
|
|
162
|
+
plt.axvline(x=1500, label="Movement start", color="red")
|
|
163
|
+
|
|
164
|
+
#plt.xticks(np.arange(0, 2000, 200), np.round(np.arange(0, 2, 0.2), 2))
|
|
165
|
+
plt.xticks(np.arange(0, data_plt.shape[0], 200), np.round(np.arange(0, int(data_plt.shape[0]/1000), 0.2), 2))
|
|
166
|
+
plt.xlabel("Time [s]")
|
|
167
|
+
plt.title("Temporal waveform shape features")
|
|
168
|
+
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
|
169
|
+
plt.ylim(-450, 400)
|
|
170
|
+
plt.ylabel("a.u.")
|
|
171
|
+
plt.tight_layout()
|
|
172
|
+
|
|
173
|
+
# %%
|
|
174
|
+
# In the *sharpwave_analysis_settings* the *estimator* keyword further specifies which statistic is computed based on the individual
|
|
175
|
+
# features in one batch. The "global" setting *segment_length_features_ms* specifies the time duration for feature computation.
|
|
176
|
+
# Since there can be a different number of identified waveform shape features for different batches (i.e. different number of peaks/troughs),
|
|
177
|
+
# taking a statistical measure (e.g. the maximum or mean) will be necessary for feature comparison.
|
|
178
|
+
|
|
179
|
+
# %%
|
|
180
|
+
# Example time series computation for movement decoding
|
|
181
|
+
# -----------------------------------------------------
|
|
182
|
+
# We will now read the ECoG example/data and investigate if samples differ across movement states. Therefore we compute features and enable the default *sharpwave* features.
|
|
183
|
+
|
|
184
|
+
settings = nm_settings.get_default_settings()
|
|
185
|
+
settings = nm_settings.reset_settings(settings)
|
|
186
|
+
settings["features"]["sharpwave_analysis"] = True
|
|
187
|
+
settings["sharpwave_analysis_settings"]["interval"] = False
|
|
188
|
+
settings["sharpwave_analysis_settings"]["filter_ranges"] = [[5, 80]]
|
|
189
|
+
|
|
190
|
+
nm_channels["used"] = 0 # set only two ECoG channels for faster computation to true
|
|
191
|
+
nm_channels.loc[[3, 8], "used"] = 1
|
|
192
|
+
|
|
193
|
+
stream = nm.Stream(
|
|
194
|
+
sfreq=sfreq,
|
|
195
|
+
nm_channels=nm_channels,
|
|
196
|
+
settings=settings,
|
|
197
|
+
line_noise=line_noise,
|
|
198
|
+
coord_list=coord_list,
|
|
199
|
+
coord_names=coord_names,
|
|
200
|
+
verbose=True,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
df_features = stream.run(data=data[:, :30000])
|
|
204
|
+
|
|
205
|
+
# %%
|
|
206
|
+
# We can then plot two exemplary features, prominence and interval, and see that the movement amplitude can be clustered with those two features alone:
|
|
207
|
+
|
|
208
|
+
plt.figure(figsize=(5, 3), dpi=300)
|
|
209
|
+
plt.scatter(
|
|
210
|
+
df_features["ECOG_RIGHT_0-avgref_Sharpwave_Max_prominence_range_5_80"],
|
|
211
|
+
df_features["ECOG_RIGHT_5-avgref_Sharpwave_Mean_interval_range_5_80"],
|
|
212
|
+
c=df_features["MOV_RIGHT"], alpha=0.8, s=30
|
|
213
|
+
)
|
|
214
|
+
cbar = plt.colorbar()
|
|
215
|
+
cbar.set_label("Movement amplitude")
|
|
216
|
+
plt.xlabel("Prominence a.u.")
|
|
217
|
+
plt.ylabel("Interval a.u.")
|
|
218
|
+
plt.title("Temporal features predict movement amplitude")
|
|
219
|
+
plt.tight_layout()
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Grid Point Projection
|
|
3
|
+
=====================
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# %%
|
|
8
|
+
# In ECoG datasets the electrode locations are usually different. For this reason, we established a grid
|
|
9
|
+
# with a set of points defined in a standardized MNI brain.
|
|
10
|
+
# Data is then interpolated to this grid, such that they are common across patients, which allows across patient decoding use cases.
|
|
11
|
+
#
|
|
12
|
+
# In this notebook, we will plot these grid points and see how the features extracted from our data can be projected into this grid space.
|
|
13
|
+
#
|
|
14
|
+
# In order to do so, we'll read saved features that were computed in the ECoG movement notebook.
|
|
15
|
+
# Please note that in order to do so, when running the feature estimation, the settings
|
|
16
|
+
#
|
|
17
|
+
# .. note::
|
|
18
|
+
#
|
|
19
|
+
# .. code-block:: python
|
|
20
|
+
#
|
|
21
|
+
# stream.settings['postprocessing']['project_cortex'] = True
|
|
22
|
+
# stream.settings['postprocessing']['project_subcortex'] = True
|
|
23
|
+
#
|
|
24
|
+
# need to be set to `True` for a cortical and/or subcortical projection.
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
# %%
|
|
28
|
+
import numpy as np
|
|
29
|
+
import matplotlib.pyplot as plt
|
|
30
|
+
|
|
31
|
+
import py_neuromodulation as nm
|
|
32
|
+
from py_neuromodulation import (
|
|
33
|
+
nm_analysis,
|
|
34
|
+
nm_plots,
|
|
35
|
+
nm_IO,
|
|
36
|
+
nm_settings,
|
|
37
|
+
nm_define_nmchannels
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# %%
|
|
42
|
+
# Read features from BIDS data
|
|
43
|
+
# ----------------------------
|
|
44
|
+
#
|
|
45
|
+
# We first estimate features, with the `grid_point` projection settings enabled for cortex.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# %%
|
|
49
|
+
RUN_NAME, PATH_RUN, PATH_BIDS, PATH_OUT, datatype = nm_IO.get_paths_example_data()
|
|
50
|
+
|
|
51
|
+
(
|
|
52
|
+
raw,
|
|
53
|
+
data,
|
|
54
|
+
sfreq,
|
|
55
|
+
line_noise,
|
|
56
|
+
coord_list,
|
|
57
|
+
coord_names,
|
|
58
|
+
) = nm_IO.read_BIDS_data(
|
|
59
|
+
PATH_RUN=PATH_RUN,
|
|
60
|
+
BIDS_PATH=PATH_BIDS, datatype=datatype
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
settings = nm_settings.get_default_settings()
|
|
64
|
+
settings = nm_settings.set_settings_fast_compute(settings)
|
|
65
|
+
|
|
66
|
+
settings["postprocessing"]["project_cortex"] = True
|
|
67
|
+
|
|
68
|
+
nm_channels = nm_define_nmchannels.set_channels(
|
|
69
|
+
ch_names=raw.ch_names,
|
|
70
|
+
ch_types=raw.get_channel_types(),
|
|
71
|
+
reference="default",
|
|
72
|
+
bads=raw.info["bads"],
|
|
73
|
+
new_names="default",
|
|
74
|
+
used_types=("ecog", "dbs", "seeg"),
|
|
75
|
+
target_keywords=["MOV_RIGHT_CLEAN","MOV_LEFT_CLEAN"]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
stream = nm.Stream(
|
|
79
|
+
sfreq=sfreq,
|
|
80
|
+
nm_channels=nm_channels,
|
|
81
|
+
settings=settings,
|
|
82
|
+
line_noise=line_noise,
|
|
83
|
+
coord_list=coord_list,
|
|
84
|
+
coord_names=coord_names,
|
|
85
|
+
verbose=True,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
features = stream.run(
|
|
89
|
+
data=data[:, :int(sfreq*5)],
|
|
90
|
+
out_path_root=PATH_OUT,
|
|
91
|
+
folder_name=RUN_NAME,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# %%
|
|
95
|
+
# From nm_analysis.py, we use the :class:~`nm_analysis.FeatureReader` class to load the data.
|
|
96
|
+
|
|
97
|
+
# init analyzer
|
|
98
|
+
feature_reader = nm_analysis.Feature_Reader(
|
|
99
|
+
feature_dir=PATH_OUT, feature_file=RUN_NAME
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# %%
|
|
103
|
+
# To perform the grid projection, for all computed features we check for every grid point if there is any electrode channel within the spatial range ```max_dist_mm```, and weight
|
|
104
|
+
# this electrode contact by the inverse distance and normalize across all electrode distances within the maximum distance range.
|
|
105
|
+
# This gives us a projection matrix that we can apply to streamed data, to transform the feature-channel matrix *(n_features, n_channels)* into the grid point matrix *(n_features, n_gridpoints)*.
|
|
106
|
+
#
|
|
107
|
+
# To save computation time, this projection matrix is precomputed before the real time run computation.
|
|
108
|
+
# The cortical grid is stored in *py_neuromodulation/grid_cortex.tsv* and the electrodes coordinates are stored in *_space-mni_electrodes.tsv* in a BIDS dataset.
|
|
109
|
+
#
|
|
110
|
+
# .. note::
|
|
111
|
+
#
|
|
112
|
+
# One remark is that our cortical and subcortical grids are defined for the **left** hemisphere of the brain and, therefore, electrode contacts are mapped to the left hemisphere.
|
|
113
|
+
#
|
|
114
|
+
# From the analyzer, the user can plot the cortical projection with the function below, display the grid points and ECoG electrodes are crosses.
|
|
115
|
+
# The yellow grid points are the ones that are active for that specific ECoG electrode location. The inactive grid points are shown in purple.
|
|
116
|
+
|
|
117
|
+
feature_reader.plot_cort_projection()
|
|
118
|
+
|
|
119
|
+
# %%
|
|
120
|
+
# We can also plot only the ECoG electrodes or the grid points, with the help of the data saved in feature_reader.sidecar. BIDS sidecar files are json files where you store additional information, here it is used to save the ECoG strip positions and the grid coordinates, which are not part of the settings and nm_channels.csv. We can check what is stored in the file and then use the nmplotter.plot_cortex function:
|
|
121
|
+
|
|
122
|
+
grid_plotter = nm_plots.NM_Plot(
|
|
123
|
+
ecog_strip=np.array(feature_reader.sidecar["coords"]["cortex_right"]["positions"]),
|
|
124
|
+
grid_cortex=np.array(feature_reader.sidecar["grid_cortex"]),
|
|
125
|
+
# grid_subcortex=np.array(feature_reader.sidecar["grid_subcortex"]),
|
|
126
|
+
sess_right=feature_reader.sidecar["sess_right"],
|
|
127
|
+
proj_matrix_cortex=np.array(feature_reader.sidecar["proj_matrix_cortex"])
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# %%
|
|
131
|
+
grid_plotter.plot_cortex(
|
|
132
|
+
grid_color=np.sum(np.array(feature_reader.sidecar["proj_matrix_cortex"]),axis=1),
|
|
133
|
+
lower_clim=0.,
|
|
134
|
+
upper_clim=1.0,
|
|
135
|
+
cbar_label="Used Grid Points",
|
|
136
|
+
title = "ECoG electrodes projected onto cortical grid"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# %%
|
|
140
|
+
feature_reader.sidecar["coords"]["cortex_right"]["positions"]
|
|
141
|
+
|
|
142
|
+
# %%
|
|
143
|
+
feature_reader.nmplotter.plot_cortex(
|
|
144
|
+
ecog_strip=np.array(
|
|
145
|
+
feature_reader.sidecar["coords"]["cortex_right"]["positions"],
|
|
146
|
+
),
|
|
147
|
+
lower_clim=0.,
|
|
148
|
+
upper_clim=1.0,
|
|
149
|
+
cbar_label="Used ECoG Electrodes",
|
|
150
|
+
title = "Plot of ECoG electrodes"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# %%
|
|
154
|
+
feature_reader.nmplotter.plot_cortex(
|
|
155
|
+
np.array(
|
|
156
|
+
feature_reader.sidecar["grid_cortex"]
|
|
157
|
+
),
|
|
158
|
+
lower_clim=0.,
|
|
159
|
+
upper_clim=1.0,
|
|
160
|
+
cbar_label="All Grid Points",
|
|
161
|
+
title = "All grid points"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# %%
|
|
165
|
+
# The Projection Matrix
|
|
166
|
+
# ---------------------
|
|
167
|
+
# To go from the feature-channel matrix *(n_features, n_channels)* to the grid point matrix *(n_features, n_gridpoints)*
|
|
168
|
+
# we need a projection matrix that has the shape *(n_channels, n_gridpoints)*.
|
|
169
|
+
# It maps the strengths of the signals in each ECoG channel to the correspondent ones in the cortical grid.
|
|
170
|
+
# In the cell below we plot this matrix, that has the property that the column sum over channels for each grid point is either 1 or 0.
|
|
171
|
+
|
|
172
|
+
plt.figure(figsize=(8,5))
|
|
173
|
+
plt.imshow(np.array(feature_reader.sidecar['proj_matrix_cortex']), aspect = 'auto')
|
|
174
|
+
plt.colorbar(label = "Strength of ECoG signal in each grid point")
|
|
175
|
+
plt.xlabel("ECoG channels")
|
|
176
|
+
plt.ylabel("Grid points")
|
|
177
|
+
plt.title("Matrix mapping from ECoG to grid")
|
|
178
|
+
|
|
179
|
+
# %%
|
|
180
|
+
# Feature Plot in the Grid: An Example of Post-processing
|
|
181
|
+
# -------------------------------------------------------
|
|
182
|
+
# First we take the dataframe with all the features in all time points.
|
|
183
|
+
|
|
184
|
+
df = feature_reader.feature_arr
|
|
185
|
+
|
|
186
|
+
# %%
|
|
187
|
+
df.iloc[:5, :5]
|
|
188
|
+
|
|
189
|
+
# %%
|
|
190
|
+
# Then we filter for only 'avgref_fft_theta', which gives us the value for fft_theta in all 6 ECoG channels over all time points. Then we take only the 6th time point - as an arbitrary choice.
|
|
191
|
+
|
|
192
|
+
fft_theta_oneTimePoint = np.asarray(df[df.columns[df.columns.str.contains(pat = 'avgref_fft_theta')]].iloc[5])
|
|
193
|
+
fft_theta_oneTimePoint
|
|
194
|
+
|
|
195
|
+
# %%
|
|
196
|
+
# Then the projection of the features into the grid is gonna be the color of the grid points in the *plot_cortex* function.
|
|
197
|
+
# That is the matrix multiplication of the projection matrix of the cortex and 6 values for the *fft_theta* feature above.
|
|
198
|
+
|
|
199
|
+
grid_fft_Theta = np.array(feature_reader.sidecar["proj_matrix_cortex"]) @ fft_theta_oneTimePoint
|
|
200
|
+
|
|
201
|
+
feature_reader.nmplotter.plot_cortex(np.array(
|
|
202
|
+
feature_reader.sidecar["grid_cortex"]),grid_color = grid_fft_Theta, set_clim = True, lower_clim=min(grid_fft_Theta[grid_fft_Theta>0]), upper_clim=max(grid_fft_Theta), cbar_label="FFT Theta Projection to Grid", title = "FFT Theta Projection to Grid")
|
|
203
|
+
|
|
204
|
+
# %%
|
|
205
|
+
# Lower and upper boundaries for clim were chosen to be the max and min values of the projection of the features (minimum value excluding zero). This can be checked in the cell below:
|
|
206
|
+
|
|
207
|
+
grid_fft_Theta
|
|
208
|
+
|
|
209
|
+
# %%
|
|
210
|
+
# In the plot above we can see how the intensity of the fast fourier transform in the theta band varies for each grid point in the cortex, for one specific time point.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
First Demo
|
|
3
|
+
==========
|
|
4
|
+
|
|
5
|
+
This Demo will showcase the feature estimation and
|
|
6
|
+
exemplar analysis using simulated data.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from matplotlib import pyplot as plt
|
|
11
|
+
|
|
12
|
+
import py_neuromodulation as py_nm
|
|
13
|
+
|
|
14
|
+
from py_neuromodulation import (
|
|
15
|
+
nm_analysis,
|
|
16
|
+
nm_define_nmchannels,
|
|
17
|
+
nm_plots
|
|
18
|
+
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# %%
|
|
22
|
+
# Data Simulation
|
|
23
|
+
# ---------------
|
|
24
|
+
# We will now generate some exemplar data with 10 second duration for 6 channels with a sample rate of 1 kHz.
|
|
25
|
+
|
|
26
|
+
def generate_random_walk(NUM_CHANNELS, TIME_DATA_SAMPLES):
|
|
27
|
+
# from https://towardsdatascience.com/random-walks-with-python-8420981bc4bc
|
|
28
|
+
dims = NUM_CHANNELS
|
|
29
|
+
step_n = TIME_DATA_SAMPLES-1
|
|
30
|
+
step_set = [-1, 0, 1]
|
|
31
|
+
origin = (np.random.random([1,dims])-0.5)*1 # Simulate steps in 1D
|
|
32
|
+
step_shape = (step_n,dims)
|
|
33
|
+
steps = np.random.choice(a=step_set, size=step_shape)
|
|
34
|
+
path = np.concatenate([origin, steps]).cumsum(0)
|
|
35
|
+
return path.T
|
|
36
|
+
|
|
37
|
+
NUM_CHANNELS = 6
|
|
38
|
+
sfreq = 1000
|
|
39
|
+
TIME_DATA_SAMPLES = 10 * sfreq
|
|
40
|
+
data = generate_random_walk(NUM_CHANNELS, TIME_DATA_SAMPLES)
|
|
41
|
+
time = np.arange(0, TIME_DATA_SAMPLES/sfreq, 1/sfreq)
|
|
42
|
+
|
|
43
|
+
plt.figure(figsize=(8,4), dpi=100)
|
|
44
|
+
for ch_idx in range(data.shape[0]):
|
|
45
|
+
plt.plot(time, data[ch_idx, :])
|
|
46
|
+
plt.xlabel("Time [s]")
|
|
47
|
+
plt.ylabel("Amplitude")
|
|
48
|
+
plt.title("Example random walk data")
|
|
49
|
+
|
|
50
|
+
# %%
|
|
51
|
+
# Now let’s define the necessary setup files we will be using for data
|
|
52
|
+
# preprocessing and feature estimation. Py_neuromodualtion is based on two
|
|
53
|
+
# parametrization files: the *nm_channels.tsv* and the *nm_setting.json*.
|
|
54
|
+
#
|
|
55
|
+
# nm_channels
|
|
56
|
+
# ~~~~~~~~~~~
|
|
57
|
+
#
|
|
58
|
+
# The *nm_channel* dataframe. This dataframe contains the columns
|
|
59
|
+
#
|
|
60
|
+
# +-----------------------------------+-----------------------------------+
|
|
61
|
+
# | Column name | Description |
|
|
62
|
+
# +===================================+===================================+
|
|
63
|
+
# | **name** | name of the channel |
|
|
64
|
+
# +-----------------------------------+-----------------------------------+
|
|
65
|
+
# | **rereference** | different channel name for |
|
|
66
|
+
# | | bipolar re-referencing, or |
|
|
67
|
+
# | | average for common average |
|
|
68
|
+
# | | re-referencing |
|
|
69
|
+
# +-----------------------------------+-----------------------------------+
|
|
70
|
+
# | **used** | 0 or 1, channel selection |
|
|
71
|
+
# +-----------------------------------+-----------------------------------+
|
|
72
|
+
# | **target** | 0 or 1, for some decoding |
|
|
73
|
+
# | | applications we can define target |
|
|
74
|
+
# | | channels, e.g. EMG channels |
|
|
75
|
+
# +-----------------------------------+-----------------------------------+
|
|
76
|
+
# | **type** | channel type according to the |
|
|
77
|
+
# | | `mne-python`_ toolbox |
|
|
78
|
+
# | | |
|
|
79
|
+
# | | |
|
|
80
|
+
# | | |
|
|
81
|
+
# | | |
|
|
82
|
+
# | | e.g. ecog, eeg, ecg, emg, dbs, |
|
|
83
|
+
# | | seeg etc. |
|
|
84
|
+
# +-----------------------------------+-----------------------------------+
|
|
85
|
+
# | **status** | good or bad, used for channel |
|
|
86
|
+
# | | quality indication |
|
|
87
|
+
# +-----------------------------------+-----------------------------------+
|
|
88
|
+
# | **new_name** | this keyword can be specified to |
|
|
89
|
+
# | | indicate for example the used |
|
|
90
|
+
# | | rereferncing scheme |
|
|
91
|
+
# +-----------------------------------+-----------------------------------+
|
|
92
|
+
#
|
|
93
|
+
# .. _mne-python: https://mne.tools/stable/auto_tutorials/raw/10_raw_overview.html#sphx-glr-auto-tutorials-raw-10-raw-overview-py
|
|
94
|
+
#
|
|
95
|
+
# The :class:`~nm_stream_abc` can either be created as a *.tsv* text file, or as a pandas
|
|
96
|
+
# DataFrame. There are some helper functions that let you create the
|
|
97
|
+
# nm_channels without much effort:
|
|
98
|
+
|
|
99
|
+
nm_channels = nm_define_nmchannels.get_default_channels_from_data(data, car_rereferencing=True)
|
|
100
|
+
|
|
101
|
+
nm_channels
|
|
102
|
+
|
|
103
|
+
# %%
|
|
104
|
+
# Using this function default channel names and a common average re-referencing scheme is specified.
|
|
105
|
+
# Alternatively the *nm_define_nmchannels.set_channels* function can be used to pass each column values.
|
|
106
|
+
#
|
|
107
|
+
# nm_settings
|
|
108
|
+
# -----------
|
|
109
|
+
# Next, we will initialize the nm_settings dictionary and use the default settings, reset them, and enable a subset of features:
|
|
110
|
+
|
|
111
|
+
settings = py_nm.nm_settings.get_default_settings()
|
|
112
|
+
settings = py_nm.nm_settings.reset_settings(settings)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# %%
|
|
116
|
+
# The setting itself is a .json file which contains the parametrization for preprocessing, feature estimation, postprocessing and
|
|
117
|
+
# definition with which sampling rate features are being calculated.
|
|
118
|
+
# In this example `sampling_rate_features_hz` is specified to be 10 Hz, so every 100ms a new set of features is calculated.
|
|
119
|
+
#
|
|
120
|
+
# For many features the `segment_length_features_ms` specifies the time dimension of the raw signal being used for feature calculation. Here it is specified to be 1000 ms.
|
|
121
|
+
#
|
|
122
|
+
# We will now enable the features:
|
|
123
|
+
#
|
|
124
|
+
# * fft
|
|
125
|
+
# * bursts
|
|
126
|
+
# * sharpwave
|
|
127
|
+
#
|
|
128
|
+
# and stay with the default preprcessing methods:
|
|
129
|
+
#
|
|
130
|
+
# * notch_filter
|
|
131
|
+
# * re_referencing
|
|
132
|
+
#
|
|
133
|
+
# and use *z-score* postprocessing normalization.
|
|
134
|
+
|
|
135
|
+
settings["features"]["fft"] = True
|
|
136
|
+
settings["features"]["bursts"] = True
|
|
137
|
+
settings["features"]["sharpwave_analysis"] = True
|
|
138
|
+
|
|
139
|
+
# %%
|
|
140
|
+
# We are now ready to go to instantiate the *Stream* and call the *run* method for feature estimation:
|
|
141
|
+
|
|
142
|
+
stream = py_nm.Stream(
|
|
143
|
+
settings=settings,
|
|
144
|
+
nm_channels=nm_channels,
|
|
145
|
+
verbose=True,
|
|
146
|
+
sfreq=sfreq,
|
|
147
|
+
line_noise=50
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
features = stream.run(data)
|
|
151
|
+
|
|
152
|
+
# %%
|
|
153
|
+
# Feature Analysis
|
|
154
|
+
# ----------------
|
|
155
|
+
#
|
|
156
|
+
# There is a lot of output, which we could omit by verbose being False, but let's have a look what was being computed.
|
|
157
|
+
# We will therefore use the :class:`~nm_analysis` class to showcase some functions. For multi-run -or subject analysis we will pass here the feature_file "sub" as default directory:
|
|
158
|
+
|
|
159
|
+
analyzer = nm_analysis.Feature_Reader(
|
|
160
|
+
feature_dir=stream.PATH_OUT,
|
|
161
|
+
feature_file=stream.PATH_OUT_folder_name
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# %%
|
|
165
|
+
# Let's have a look at the resulting "feature_arr" DataFrame:
|
|
166
|
+
|
|
167
|
+
analyzer.feature_arr.iloc[:10, :7]
|
|
168
|
+
|
|
169
|
+
# %%
|
|
170
|
+
# Seems like a lot of features were calculated. The `time` column tells us about each row time index.
|
|
171
|
+
# For the 6 specified channels, it is each 31 features.
|
|
172
|
+
# We can now use some in-built plotting functions for visualization.
|
|
173
|
+
#
|
|
174
|
+
# .. note::
|
|
175
|
+
#
|
|
176
|
+
# Due to the nature of simulated data, some of the features have constant values, which are not displayed through the image normalization.
|
|
177
|
+
#
|
|
178
|
+
#
|
|
179
|
+
|
|
180
|
+
analyzer.plot_all_features(ch_used="ch1")
|
|
181
|
+
|
|
182
|
+
# %%
|
|
183
|
+
nm_plots.plot_corr_matrix(
|
|
184
|
+
figsize=(25,25),
|
|
185
|
+
show_plot=True,
|
|
186
|
+
feature=analyzer.feature_arr,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# %%
|
|
190
|
+
# The upper correlation matrix shows the correlation of every feature of every channel to every other.
|
|
191
|
+
# This notebook demonstrated a first demo how features can quickly be generated. For further feature modalities and decoding applications check out the next notebooks.
|
|
192
|
+
|