py-neuromodulation 0.0.1__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 +168 -0
- py_neuromodulation/nm_bursts.py +43 -31
- py_neuromodulation/nm_coherence.py +2 -1
- py_neuromodulation/nm_define_nmchannels.py +1 -1
- py_neuromodulation/nm_features.py +6 -1
- 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_generator.py +10 -10
- 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 +185 -43
- py_neuromodulation/utils/_logging.py +24 -0
- {py_neuromodulation-0.0.1.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -131
- py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
- {py_neuromodulation-0.0.1.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 -261
- 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.1.dist-info/RECORD +0 -72
- /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
- {py_neuromodulation-0.0.1.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from typing import Iterable
|
|
2
|
+
import numpy as np
|
|
3
|
+
from pybispectra import compute_fft, get_example_data_paths, WaveShape
|
|
4
|
+
|
|
5
|
+
from py_neuromodulation import nm_features_abc
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Bispectra(nm_features_abc.Feature):
|
|
9
|
+
def __init__(
|
|
10
|
+
self, settings: dict, ch_names: Iterable[str], sfreq: int | float
|
|
11
|
+
) -> None:
|
|
12
|
+
super().__init__(settings, ch_names, sfreq)
|
|
13
|
+
self.sfreq = sfreq
|
|
14
|
+
self.ch_names = ch_names
|
|
15
|
+
self.s = settings
|
|
16
|
+
self.f1s = settings["bispectrum"]["f1s"]
|
|
17
|
+
self.f2s = settings["bispectrum"]["f2s"]
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def test_settings(
|
|
21
|
+
settings: dict,
|
|
22
|
+
ch_names: Iterable[str],
|
|
23
|
+
sfreq: int | float,
|
|
24
|
+
):
|
|
25
|
+
s = settings
|
|
26
|
+
|
|
27
|
+
def test_range(f_name, filter_range):
|
|
28
|
+
assert isinstance(
|
|
29
|
+
filter_range[0],
|
|
30
|
+
int,
|
|
31
|
+
), f"bispectrum frequency range {f_name} needs to be of type int, got {filter_range[0]}"
|
|
32
|
+
assert isinstance(
|
|
33
|
+
filter_range[1],
|
|
34
|
+
int,
|
|
35
|
+
), f"bispectrum frequency range {f_name} needs to be of type int, got {filter_range[1]}"
|
|
36
|
+
assert (
|
|
37
|
+
filter_range[1] > filter_range[0]
|
|
38
|
+
), f"second frequency range value needs to be higher than first one, got {filter_range}"
|
|
39
|
+
assert filter_range[0] < sfreq and filter_range[1] < sfreq, (
|
|
40
|
+
"filter frequency range has to be smaller than sfreq, "
|
|
41
|
+
f"got sfreq {sfreq} and filter range {filter_range}"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
test_range("f1s", s["bispectrum"]["f1s"])
|
|
45
|
+
test_range("f2s", s["bispectrum"]["f2s"])
|
|
46
|
+
|
|
47
|
+
for feature_name, val in s["bispectrum"]["components"].items():
|
|
48
|
+
assert isinstance(
|
|
49
|
+
val, bool
|
|
50
|
+
), f"bispectrum component {feature_name} has to be of type bool, got {val}"
|
|
51
|
+
|
|
52
|
+
for feature_name, val in s["bispectrum"]["bispectrum_features"].items():
|
|
53
|
+
assert isinstance(
|
|
54
|
+
val, bool
|
|
55
|
+
), f"bispectrum feature {feature_name} has to be of type bool, got {val}"
|
|
56
|
+
|
|
57
|
+
assert (
|
|
58
|
+
f_band_bispectrum in s["frequency_ranges_hz"]
|
|
59
|
+
for f_band_bispectrum in s["bispectrum"]["frequency_bands"]
|
|
60
|
+
), (
|
|
61
|
+
"bispectrum selected frequency bands don't match the ones"
|
|
62
|
+
"specified in s['frequency_ranges_hz']"
|
|
63
|
+
f"bispectrum frequency bands: {s['bispectrum']['frequency_bands']}"
|
|
64
|
+
f"specified frequency_ranges_hz: {s['frequency_ranges_hz']}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def compute_bs_features(
|
|
68
|
+
self,
|
|
69
|
+
spectrum_ch: np.array,
|
|
70
|
+
features_compute: dict,
|
|
71
|
+
ch_name: str,
|
|
72
|
+
component: str,
|
|
73
|
+
f_band: str,
|
|
74
|
+
) -> dict:
|
|
75
|
+
for bispectrum_feature in self.s["bispectrum"]["bispectrum_features"]:
|
|
76
|
+
if bispectrum_feature == "mean":
|
|
77
|
+
func = np.nanmean
|
|
78
|
+
if bispectrum_feature == "sum":
|
|
79
|
+
func = np.nansum
|
|
80
|
+
if bispectrum_feature == "var":
|
|
81
|
+
func = np.nanvar
|
|
82
|
+
|
|
83
|
+
if f_band is not None:
|
|
84
|
+
str_feature = "_".join(
|
|
85
|
+
[
|
|
86
|
+
ch_name,
|
|
87
|
+
"Bispectrum",
|
|
88
|
+
component,
|
|
89
|
+
bispectrum_feature,
|
|
90
|
+
f_band,
|
|
91
|
+
]
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
str_feature = "_".join(
|
|
95
|
+
[
|
|
96
|
+
ch_name,
|
|
97
|
+
"Bispectrum",
|
|
98
|
+
component,
|
|
99
|
+
bispectrum_feature,
|
|
100
|
+
"whole_fband_range",
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
features_compute[str_feature] = func(spectrum_ch)
|
|
105
|
+
|
|
106
|
+
return features_compute
|
|
107
|
+
|
|
108
|
+
def calc_feature(self, data: np.array, features_compute: dict) -> dict:
|
|
109
|
+
for ch_idx, ch_name in enumerate(self.ch_names):
|
|
110
|
+
fft_coeffs, freqs = compute_fft(
|
|
111
|
+
data=np.expand_dims(data[ch_idx, :], axis=(0, 1)),
|
|
112
|
+
sampling_freq=self.sfreq,
|
|
113
|
+
n_points=data.shape[1],
|
|
114
|
+
verbose=False,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
f_spectrum_range = freqs[
|
|
118
|
+
np.logical_and(
|
|
119
|
+
freqs >= np.min([self.f1s, self.f2s]),
|
|
120
|
+
freqs <= np.max([self.f1s, self.f2s]),
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
waveshape = WaveShape(
|
|
125
|
+
data=fft_coeffs,
|
|
126
|
+
freqs=freqs,
|
|
127
|
+
sampling_freq=self.sfreq,
|
|
128
|
+
verbose=False,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
waveshape.compute(
|
|
132
|
+
f1s=(self.f1s[0], self.f1s[-1]), f2s=(self.f2s[0], self.f2s[-1])
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
bispectrum = np.squeeze(waveshape.results._data)
|
|
136
|
+
|
|
137
|
+
for component in self.s["bispectrum"]["components"]:
|
|
138
|
+
if self.s["bispectrum"]["components"][component]:
|
|
139
|
+
if component == "real":
|
|
140
|
+
spectrum_ch = bispectrum.real
|
|
141
|
+
if component == "imag":
|
|
142
|
+
spectrum_ch = bispectrum.imag
|
|
143
|
+
if component == "absolute":
|
|
144
|
+
spectrum_ch = np.abs(bispectrum)
|
|
145
|
+
if component == "phase":
|
|
146
|
+
spectrum_ch = np.angle(bispectrum)
|
|
147
|
+
|
|
148
|
+
for fb in self.s["bispectrum"]["frequency_bands"]:
|
|
149
|
+
range_ = (
|
|
150
|
+
f_spectrum_range >= self.s["frequency_ranges_hz"][fb][0]
|
|
151
|
+
) & (
|
|
152
|
+
f_spectrum_range <= self.s["frequency_ranges_hz"][fb][1]
|
|
153
|
+
)
|
|
154
|
+
# waveshape.results.plot()
|
|
155
|
+
data_bs = spectrum_ch[range_, range_]
|
|
156
|
+
|
|
157
|
+
features_compute = self.compute_bs_features(
|
|
158
|
+
data_bs, features_compute, ch_name, component, fb
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if self.s["bispectrum"][
|
|
162
|
+
"compute_features_for_whole_fband_range"
|
|
163
|
+
]:
|
|
164
|
+
features_compute = self.compute_bs_features(
|
|
165
|
+
spectrum_ch, features_compute, ch_name, component, None
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return features_compute
|
py_neuromodulation/nm_bursts.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import enum
|
|
2
2
|
import numpy as np
|
|
3
3
|
from typing import Iterable
|
|
4
|
+
from scipy import signal
|
|
4
5
|
|
|
5
6
|
from py_neuromodulation import nm_features_abc, nm_filter
|
|
6
7
|
|
|
@@ -9,12 +10,16 @@ class Burst(nm_features_abc.Feature):
|
|
|
9
10
|
def __init__(
|
|
10
11
|
self, settings: dict, ch_names: Iterable[str], sfreq: float
|
|
11
12
|
) -> None:
|
|
12
|
-
|
|
13
13
|
self.s = settings
|
|
14
14
|
self.sfreq = sfreq
|
|
15
15
|
self.ch_names = ch_names
|
|
16
16
|
self.threshold = self.s["burst_settings"]["threshold"]
|
|
17
17
|
self.time_duration_s = self.s["burst_settings"]["time_duration_s"]
|
|
18
|
+
self.samples_overlap = int(
|
|
19
|
+
self.sfreq
|
|
20
|
+
* (self.s["segment_length_features_ms"] / 1000)
|
|
21
|
+
/ self.s["sampling_rate_features_hz"]
|
|
22
|
+
)
|
|
18
23
|
|
|
19
24
|
self.fband_names = self.s["burst_settings"]["frequency_bands"]
|
|
20
25
|
self.f_ranges = [
|
|
@@ -34,11 +39,11 @@ class Burst(nm_features_abc.Feature):
|
|
|
34
39
|
)
|
|
35
40
|
).astype(int)
|
|
36
41
|
|
|
37
|
-
self.
|
|
38
|
-
self.sfreq
|
|
42
|
+
self.num_max_samples_ring_buffer = int(
|
|
43
|
+
self.sfreq * self.time_duration_s
|
|
39
44
|
)
|
|
40
45
|
|
|
41
|
-
self.bandpass_filter = nm_filter.
|
|
46
|
+
self.bandpass_filter = nm_filter.MNEFilter(
|
|
42
47
|
f_ranges=self.f_ranges,
|
|
43
48
|
sfreq=self.sfreq,
|
|
44
49
|
filter_length=self.sfreq - 1,
|
|
@@ -64,35 +69,40 @@ class Burst(nm_features_abc.Feature):
|
|
|
64
69
|
ch_names: Iterable[str],
|
|
65
70
|
sfreq: int | float,
|
|
66
71
|
):
|
|
67
|
-
assert (
|
|
68
|
-
|
|
72
|
+
assert isinstance(
|
|
73
|
+
settings["burst_settings"]["threshold"], (float, int)
|
|
69
74
|
), f"burst settings threshold needs to be type int or float, got: {settings['burst_settings']['threshold']}"
|
|
70
75
|
assert (
|
|
71
76
|
0 < settings["burst_settings"]["threshold"] < 100
|
|
72
77
|
), f"burst setting threshold needs to be between 0 and 100, got: {settings['burst_settings']['threshold']}"
|
|
73
|
-
assert (
|
|
74
|
-
|
|
78
|
+
assert isinstance(
|
|
79
|
+
settings["burst_settings"]["time_duration_s"], (float, int)
|
|
75
80
|
), f"burst settings time_duration_s needs to be type int or float, got: {settings['burst_settings']['time_duration_s']}"
|
|
76
81
|
assert (
|
|
77
82
|
settings["burst_settings"]["time_duration_s"] > 0
|
|
78
83
|
), f"burst setting time_duration_s needs to be greater than 0, got: {settings['burst_settings']['time_duration_s']}"
|
|
79
84
|
|
|
80
85
|
for fband_burst in settings["burst_settings"]["frequency_bands"]:
|
|
81
|
-
assert (
|
|
82
|
-
|
|
86
|
+
assert fband_burst in list(
|
|
87
|
+
settings["frequency_ranges_hz"].keys()
|
|
83
88
|
), f"bursting {fband_burst} needs to be defined in settings['frequency_ranges_hz']"
|
|
84
89
|
|
|
85
|
-
for burst_feature in settings["burst_settings"][
|
|
90
|
+
for burst_feature in settings["burst_settings"][
|
|
91
|
+
"burst_features"
|
|
92
|
+
].keys():
|
|
86
93
|
assert isinstance(
|
|
87
|
-
settings["burst_settings"]["burst_features"][burst_feature],
|
|
94
|
+
settings["burst_settings"]["burst_features"][burst_feature],
|
|
95
|
+
bool,
|
|
88
96
|
), (
|
|
89
97
|
f"bursting feature {burst_feature} needs to be type bool, "
|
|
90
98
|
f"got: {settings['burst_settings']['burst_features'][burst_feature]}"
|
|
91
99
|
)
|
|
92
|
-
def calc_feature(self, data: np.array, features_compute: dict) -> dict:
|
|
93
100
|
|
|
101
|
+
def calc_feature(self, data: np.array, features_compute: dict) -> dict:
|
|
94
102
|
# filter_data returns (n_channels, n_fbands, n_samples)
|
|
95
|
-
filtered_data =
|
|
103
|
+
filtered_data = np.abs(
|
|
104
|
+
signal.hilbert(self.bandpass_filter.filter_data(data), axis=2)
|
|
105
|
+
)
|
|
96
106
|
for ch_idx, ch_name in enumerate(self.ch_names):
|
|
97
107
|
for fband_idx, fband_name in enumerate(self.fband_names):
|
|
98
108
|
new_dat = filtered_data[ch_idx, fband_idx, :]
|
|
@@ -100,8 +110,12 @@ class Burst(nm_features_abc.Feature):
|
|
|
100
110
|
self.data_buffer[ch_name][fband_name] = new_dat
|
|
101
111
|
else:
|
|
102
112
|
self.data_buffer[ch_name][fband_name] = np.concatenate(
|
|
103
|
-
(
|
|
104
|
-
|
|
113
|
+
(
|
|
114
|
+
self.data_buffer[ch_name][fband_name],
|
|
115
|
+
new_dat[-self.samples_overlap :],
|
|
116
|
+
),
|
|
117
|
+
axis=0,
|
|
118
|
+
)[-self.num_max_samples_ring_buffer :]
|
|
105
119
|
|
|
106
120
|
# calc features
|
|
107
121
|
burst_thr = np.percentile(
|
|
@@ -147,9 +161,9 @@ class Burst(nm_features_abc.Feature):
|
|
|
147
161
|
if self.data_buffer[ch_name][fband_name][-1] > burst_thr:
|
|
148
162
|
in_burst = True
|
|
149
163
|
|
|
150
|
-
features_compute[
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
features_compute[f"{ch_name}_bursts_{fband_name}_in_burst"] = (
|
|
165
|
+
in_burst
|
|
166
|
+
)
|
|
153
167
|
return features_compute
|
|
154
168
|
|
|
155
169
|
@staticmethod
|
|
@@ -162,25 +176,23 @@ class Burst(nm_features_abc.Feature):
|
|
|
162
176
|
bursts = np.zeros((beta_averp_norm.shape[0] + 1), dtype=bool)
|
|
163
177
|
bursts[1:] = beta_averp_norm >= burst_thr
|
|
164
178
|
deriv = np.diff(bursts)
|
|
165
|
-
isburst = False
|
|
166
179
|
burst_length = []
|
|
167
180
|
burst_amplitude = []
|
|
168
|
-
burst_start = 0
|
|
169
181
|
|
|
170
|
-
|
|
171
|
-
if burst_state == True:
|
|
172
|
-
if isburst == True:
|
|
173
|
-
burst_length.append(index - burst_start)
|
|
174
|
-
burst_amplitude.append(beta_averp_norm[burst_start:index])
|
|
182
|
+
burst_time_points = np.where(deriv == True)[0]
|
|
175
183
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
184
|
+
for i in range(burst_time_points.size // 2):
|
|
185
|
+
burst_length.append(
|
|
186
|
+
burst_time_points[2 * i + 1] - burst_time_points[2 * i]
|
|
187
|
+
)
|
|
188
|
+
burst_amplitude.append(
|
|
189
|
+
beta_averp_norm[
|
|
190
|
+
burst_time_points[2 * i] : burst_time_points[2 * i + 1]
|
|
191
|
+
]
|
|
192
|
+
)
|
|
180
193
|
|
|
181
194
|
# the last burst length (in case isburst == True) is omitted,
|
|
182
195
|
# since the true burst length cannot be estimated
|
|
183
|
-
|
|
184
196
|
burst_length = np.array(burst_length) / sfreq
|
|
185
197
|
|
|
186
198
|
return burst_amplitude, burst_length
|
|
@@ -111,7 +111,7 @@ class CoherenceObject:
|
|
|
111
111
|
|
|
112
112
|
class NM_Coherence(nm_features_abc.Feature):
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
|
|
116
116
|
def __init__(
|
|
117
117
|
self, settings: dict, ch_names: Iterable[str], sfreq: float
|
|
@@ -119,6 +119,7 @@ class NM_Coherence(nm_features_abc.Feature):
|
|
|
119
119
|
self.s = settings
|
|
120
120
|
self.sfreq = sfreq
|
|
121
121
|
self.ch_names = ch_names
|
|
122
|
+
self.coherence_objects: Iterable[CoherenceObject] = []
|
|
122
123
|
|
|
123
124
|
for idx_coh in range(len(self.s["coherence"]["channels"])):
|
|
124
125
|
fband_names = self.s["coherence"]["frequency_bands"]
|
|
@@ -17,7 +17,7 @@ def set_channels(
|
|
|
17
17
|
ecog_only: bool = False,
|
|
18
18
|
used_types: Optional[Iterable[str]] = ("ecog", "dbs", "seeg"),
|
|
19
19
|
target_keywords: Optional[Iterable[str]] = ("mov", "squared", "label"),
|
|
20
|
-
):
|
|
20
|
+
) -> pd.DataFrame:
|
|
21
21
|
"""Return dataframe with channel-specific settings in nm_channels format.
|
|
22
22
|
|
|
23
23
|
Return an nm_channels dataframe with the columns: "name", "rereference",
|
|
@@ -11,6 +11,7 @@ from py_neuromodulation import (
|
|
|
11
11
|
nm_oscillatory,
|
|
12
12
|
nm_bursts,
|
|
13
13
|
nm_linelength,
|
|
14
|
+
nm_bispectra,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
@@ -55,6 +56,8 @@ class Features:
|
|
|
55
56
|
FeatureClass = nm_oscillatory.STFT
|
|
56
57
|
case "fft":
|
|
57
58
|
FeatureClass = nm_oscillatory.FFT
|
|
59
|
+
case "welch":
|
|
60
|
+
FeatureClass = nm_oscillatory.Welch
|
|
58
61
|
case "sharpwave_analysis":
|
|
59
62
|
FeatureClass = nm_sharpwaves.SharpwaveAnalyzer
|
|
60
63
|
case "fooof":
|
|
@@ -69,6 +72,8 @@ class Features:
|
|
|
69
72
|
FeatureClass = nm_linelength.LineLength
|
|
70
73
|
case "mne_connectivity":
|
|
71
74
|
FeatureClass = nm_mne_connectivity.MNEConnectivity
|
|
75
|
+
case "bispectrum":
|
|
76
|
+
FeatureClass = nm_bispectra.Bispectra
|
|
72
77
|
case _:
|
|
73
78
|
raise ValueError(f"Unknown feature found. Got: {feature}.")
|
|
74
79
|
|
|
@@ -108,4 +113,4 @@ class Features:
|
|
|
108
113
|
features_compute,
|
|
109
114
|
)
|
|
110
115
|
|
|
111
|
-
return features_compute
|
|
116
|
+
return features_compute
|
py_neuromodulation/nm_filter.py
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
"""Module for filter functionality."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger("PynmLogger")
|
|
6
|
+
|
|
2
7
|
import mne
|
|
3
8
|
from mne.filter import _overlap_add_filter
|
|
4
9
|
import numpy as np
|
|
5
10
|
|
|
6
11
|
|
|
7
|
-
class
|
|
8
|
-
"""
|
|
12
|
+
class MNEFilter:
|
|
13
|
+
"""mne.filter wrapper
|
|
9
14
|
|
|
10
15
|
This class stores for given frequency band ranges the filter
|
|
11
16
|
coefficients with length "filter_len".
|
|
12
17
|
The filters can then be used sequentially for band power estimation with
|
|
13
18
|
apply_filter().
|
|
19
|
+
Note that this filter can be a bandpass, bandstop, lowpass, or highpass filter
|
|
20
|
+
depending on the frequency ranges given (see further details in mne.filter.create_filter).
|
|
14
21
|
|
|
15
22
|
Parameters
|
|
16
23
|
----------
|
|
@@ -35,7 +42,7 @@ class BandPassFilter:
|
|
|
35
42
|
|
|
36
43
|
def __init__(
|
|
37
44
|
self,
|
|
38
|
-
f_ranges: list[list[int | float | None]],
|
|
45
|
+
f_ranges: list[list[int | float | None]] | list[int | float | None],
|
|
39
46
|
sfreq: int | float,
|
|
40
47
|
filter_length: str | float = "999ms",
|
|
41
48
|
l_trans_bandwidth: int | float | str = 4,
|
|
@@ -43,22 +50,36 @@ class BandPassFilter:
|
|
|
43
50
|
verbose: bool | int | str | None = None,
|
|
44
51
|
) -> None:
|
|
45
52
|
filter_bank = []
|
|
46
|
-
# mne create_filter function only accepts str and int
|
|
53
|
+
# mne create_filter function only accepts str and int for filter_length
|
|
47
54
|
if isinstance(filter_length, float):
|
|
48
55
|
filter_length = int(filter_length)
|
|
49
56
|
|
|
57
|
+
if not isinstance(f_ranges[0], list):
|
|
58
|
+
f_ranges = [f_ranges]
|
|
59
|
+
|
|
50
60
|
for f_range in f_ranges:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
try:
|
|
62
|
+
filt = mne.filter.create_filter(
|
|
63
|
+
None,
|
|
64
|
+
sfreq,
|
|
65
|
+
l_freq=f_range[0],
|
|
66
|
+
h_freq=f_range[1],
|
|
67
|
+
fir_design="firwin",
|
|
68
|
+
l_trans_bandwidth=l_trans_bandwidth, # type: ignore
|
|
69
|
+
h_trans_bandwidth=h_trans_bandwidth, # type: ignore
|
|
70
|
+
filter_length=filter_length, # type: ignore
|
|
71
|
+
verbose=verbose,
|
|
72
|
+
)
|
|
73
|
+
except:
|
|
74
|
+
filt = mne.filter.create_filter(
|
|
75
|
+
None,
|
|
76
|
+
sfreq,
|
|
77
|
+
l_freq=f_range[0],
|
|
78
|
+
h_freq=f_range[1],
|
|
79
|
+
fir_design="firwin",
|
|
80
|
+
verbose=verbose,
|
|
81
|
+
# filter_length=filter_length,
|
|
82
|
+
)
|
|
62
83
|
filter_bank.append(filt)
|
|
63
84
|
self.filter_bank = np.vstack(filter_bank)
|
|
64
85
|
|
|
@@ -77,10 +98,6 @@ class BandPassFilter:
|
|
|
77
98
|
np.ndarray, shape (n_channels, n_fbands, n_samples)
|
|
78
99
|
Filtered data.
|
|
79
100
|
|
|
80
|
-
Raises
|
|
81
|
-
------
|
|
82
|
-
ValueError
|
|
83
|
-
If data.ndim > 2
|
|
84
101
|
"""
|
|
85
102
|
if data.ndim > 2:
|
|
86
103
|
raise ValueError(
|
|
@@ -89,15 +106,29 @@ class BandPassFilter:
|
|
|
89
106
|
)
|
|
90
107
|
if data.ndim == 1:
|
|
91
108
|
data = np.expand_dims(data, axis=0)
|
|
109
|
+
|
|
92
110
|
filtered = np.array(
|
|
93
111
|
[
|
|
94
112
|
[
|
|
95
|
-
np.convolve(
|
|
96
|
-
for
|
|
113
|
+
np.convolve(filt, chan, mode="same")
|
|
114
|
+
for filt in self.filter_bank
|
|
97
115
|
]
|
|
98
116
|
for chan in data
|
|
99
117
|
]
|
|
100
118
|
)
|
|
119
|
+
|
|
120
|
+
# ensure here that the output dimension matches the input dimension
|
|
121
|
+
if data.shape[1] != filtered.shape[-1]:
|
|
122
|
+
# select the middle part of the filtered data
|
|
123
|
+
middle_index = filtered.shape[-1] // 2
|
|
124
|
+
filtered = filtered[
|
|
125
|
+
:,
|
|
126
|
+
:,
|
|
127
|
+
middle_index
|
|
128
|
+
- data.shape[1] // 2 : middle_index
|
|
129
|
+
+ data.shape[1] // 2,
|
|
130
|
+
]
|
|
131
|
+
|
|
101
132
|
return filtered
|
|
102
133
|
|
|
103
134
|
|
|
@@ -125,7 +156,7 @@ class NotchFilter:
|
|
|
125
156
|
# Code is copied from filter.py notch_filter
|
|
126
157
|
if freqs.size == 0:
|
|
127
158
|
self.filter_bank = None
|
|
128
|
-
|
|
159
|
+
logger.warning(
|
|
129
160
|
"WARNING: notch_filter is activated but data is not being"
|
|
130
161
|
f" filtered. This may be due to a low sampling frequency or"
|
|
131
162
|
f" incorrect specifications. Make sure your settings are"
|
|
@@ -170,19 +201,19 @@ class NotchFilter:
|
|
|
170
201
|
phase="zero",
|
|
171
202
|
fir_window="hamming",
|
|
172
203
|
fir_design="firwin",
|
|
173
|
-
verbose=False
|
|
204
|
+
verbose=False,
|
|
174
205
|
)
|
|
175
206
|
|
|
176
207
|
def process(self, data: np.ndarray) -> np.ndarray:
|
|
177
208
|
if self.filter_bank is None:
|
|
178
209
|
return data
|
|
179
210
|
return _overlap_add_filter(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
211
|
+
x=data,
|
|
212
|
+
h=self.filter_bank,
|
|
213
|
+
n_fft=None,
|
|
214
|
+
phase="zero",
|
|
215
|
+
picks=None,
|
|
216
|
+
n_jobs=1,
|
|
217
|
+
copy=True,
|
|
218
|
+
pad="reflect_limited",
|
|
219
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from py_neuromodulation import nm_filter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PreprocessingFilter:
|
|
7
|
+
|
|
8
|
+
def __init__(self, settings: dict, sfreq: int | float) -> None:
|
|
9
|
+
self.s = settings
|
|
10
|
+
self.sfreq = sfreq
|
|
11
|
+
self.filters = []
|
|
12
|
+
|
|
13
|
+
if self.s["preprocessing_filter"]["bandstop_filter"] is True:
|
|
14
|
+
self.filters.append(
|
|
15
|
+
nm_filter.MNEFilter(
|
|
16
|
+
f_ranges=[
|
|
17
|
+
self.s["preprocessing_filter"][
|
|
18
|
+
"bandstop_filter_settings"
|
|
19
|
+
]["frequency_high_hz"],
|
|
20
|
+
self.s["preprocessing_filter"][
|
|
21
|
+
"bandstop_filter_settings"
|
|
22
|
+
]["frequency_low_hz"],
|
|
23
|
+
],
|
|
24
|
+
sfreq=self.sfreq,
|
|
25
|
+
filter_length=self.sfreq - 1,
|
|
26
|
+
verbose=False,
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if self.s["preprocessing_filter"]["bandpass_filter"] is True:
|
|
31
|
+
self.filters.append(
|
|
32
|
+
nm_filter.MNEFilter(
|
|
33
|
+
f_ranges=[
|
|
34
|
+
self.s["preprocessing_filter"][
|
|
35
|
+
"bandpass_filter_settings"
|
|
36
|
+
]["frequency_low_hz"],
|
|
37
|
+
self.s["preprocessing_filter"][
|
|
38
|
+
"bandpass_filter_settings"
|
|
39
|
+
]["frequency_high_hz"],
|
|
40
|
+
],
|
|
41
|
+
sfreq=self.sfreq,
|
|
42
|
+
filter_length=self.sfreq - 1,
|
|
43
|
+
verbose=False,
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
if self.s["preprocessing_filter"]["lowpass_filter"] is True:
|
|
47
|
+
self.filters.append(
|
|
48
|
+
nm_filter.MNEFilter(
|
|
49
|
+
f_ranges=[
|
|
50
|
+
None,
|
|
51
|
+
self.s["preprocessing_filter"][
|
|
52
|
+
"lowpass_filter_settings"
|
|
53
|
+
]["frequency_cutoff_hz"],
|
|
54
|
+
],
|
|
55
|
+
sfreq=self.sfreq,
|
|
56
|
+
filter_length=self.sfreq - 1,
|
|
57
|
+
verbose=False,
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
if self.s["preprocessing_filter"]["highpass_filter"] is True:
|
|
61
|
+
self.filters.append(
|
|
62
|
+
nm_filter.MNEFilter(
|
|
63
|
+
f_ranges=[
|
|
64
|
+
self.s["preprocessing_filter"][
|
|
65
|
+
"highpass_filter_settings"
|
|
66
|
+
]["frequency_cutoff_hz"],
|
|
67
|
+
None,
|
|
68
|
+
],
|
|
69
|
+
sfreq=self.sfreq,
|
|
70
|
+
filter_length=self.sfreq - 1,
|
|
71
|
+
verbose=False,
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def process(self, data: np.ndarray) -> np.ndarray:
|
|
76
|
+
"""Preprocess data according to the initialized list of PreprocessingFilter objects
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
data (numpy ndarray) :
|
|
80
|
+
shape(n_channels, n_samples) - data to be preprocessed.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
preprocessed_data (numpy ndarray):
|
|
84
|
+
shape(n_channels, n_samples) - preprocessed data
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
for filter in self.filters:
|
|
88
|
+
data = filter.filter_data(
|
|
89
|
+
data if len(data.shape) == 2 else data[:, 0, :]
|
|
90
|
+
)
|
|
91
|
+
return data if len(data.shape) == 2 else data[:, 0, :]
|