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
py_neuromodulation/nm_plots.py
CHANGED
|
@@ -6,6 +6,9 @@ from matplotlib import gridspec
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
import seaborn as sb
|
|
8
8
|
import pandas as pd
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("PynmLogger")
|
|
9
12
|
|
|
10
13
|
from py_neuromodulation import nm_IO, nm_stats
|
|
11
14
|
|
|
@@ -87,7 +90,9 @@ def plot_epoch(
|
|
|
87
90
|
):
|
|
88
91
|
if z_score is None:
|
|
89
92
|
X_epoch = stats.zscore(
|
|
90
|
-
np.nan_to_num(np.nanmean(np.squeeze(X_epoch), axis=0)),
|
|
93
|
+
np.nan_to_num(np.nanmean(np.squeeze(X_epoch), axis=0)),
|
|
94
|
+
axis=0,
|
|
95
|
+
nan_policy="omit",
|
|
91
96
|
).T
|
|
92
97
|
y_epoch = np.stack(np.array(y_epoch))
|
|
93
98
|
plt.figure(figsize=(6, 6))
|
|
@@ -237,7 +242,7 @@ def plot_corr_matrix(
|
|
|
237
242
|
|
|
238
243
|
if save_plot:
|
|
239
244
|
plt.savefig(plt_path, bbox_inches="tight")
|
|
240
|
-
|
|
245
|
+
logger.info(f"Correlation matrix figure saved to {plt_path}")
|
|
241
246
|
|
|
242
247
|
if show_plot is False:
|
|
243
248
|
plt.close()
|
|
@@ -329,7 +334,7 @@ def plot_epochs_avg(
|
|
|
329
334
|
|
|
330
335
|
if normalize_data:
|
|
331
336
|
X_epoch_mean = stats.zscore(
|
|
332
|
-
np.nanmean(np.squeeze(X_epoch), axis=0), axis=0
|
|
337
|
+
np.nanmean(np.squeeze(X_epoch), axis=0), axis=0, nan_policy="omit"
|
|
333
338
|
).T
|
|
334
339
|
else:
|
|
335
340
|
X_epoch_mean = np.nanmean(np.squeeze(X_epoch), axis=0).T
|
|
@@ -385,7 +390,7 @@ def plot_epochs_avg(
|
|
|
385
390
|
feature_name=feature_str_add,
|
|
386
391
|
)
|
|
387
392
|
plt.savefig(plt_path, bbox_inches="tight")
|
|
388
|
-
|
|
393
|
+
logger.info(f"Feature epoch average figure saved to: {str(plt_path)}")
|
|
389
394
|
if show_plot is False:
|
|
390
395
|
plt.close()
|
|
391
396
|
|
|
@@ -441,7 +446,6 @@ def plot_all_features(
|
|
|
441
446
|
OUT_PATH: str = None,
|
|
442
447
|
feature_file: str = None,
|
|
443
448
|
):
|
|
444
|
-
|
|
445
449
|
if time_limit_high_s is not None:
|
|
446
450
|
df = df[df["time"] < time_limit_high_s * 1000]
|
|
447
451
|
if time_limit_low_s is not None:
|
|
@@ -449,7 +453,7 @@ def plot_all_features(
|
|
|
449
453
|
|
|
450
454
|
cols_plt = [c for c in df.columns if c != "time"]
|
|
451
455
|
if normalize is True:
|
|
452
|
-
data_plt = stats.zscore(df[cols_plt])
|
|
456
|
+
data_plt = stats.zscore(df[cols_plt], nan_policy="omit")
|
|
453
457
|
else:
|
|
454
458
|
data_plt = df[cols_plt]
|
|
455
459
|
|
|
@@ -487,7 +491,6 @@ class NM_Plot:
|
|
|
487
491
|
sess_right: Optional[bool] = False,
|
|
488
492
|
proj_matrix_cortex: np.ndarray | None = None,
|
|
489
493
|
) -> None:
|
|
490
|
-
|
|
491
494
|
self.grid_cortex = grid_cortex
|
|
492
495
|
self.grid_subcortex = grid_subcortex
|
|
493
496
|
self.ecog_strip = ecog_strip
|
|
@@ -510,7 +513,6 @@ class NM_Plot:
|
|
|
510
513
|
) = nm_IO.read_plot_modules()
|
|
511
514
|
|
|
512
515
|
def plot_grid_elec_3d(self) -> None:
|
|
513
|
-
|
|
514
516
|
plot_grid_elec_3d(np.array(self.grid_cortex), np.array(self.ecog_strip))
|
|
515
517
|
|
|
516
518
|
def plot_cortex(
|
|
@@ -552,7 +554,6 @@ class NM_Plot:
|
|
|
552
554
|
axes.axes.set_aspect("equal", anchor="C")
|
|
553
555
|
|
|
554
556
|
if grid_cortex is not None:
|
|
555
|
-
|
|
556
557
|
grid_color = (
|
|
557
558
|
np.ones(grid_cortex.shape[0])
|
|
558
559
|
if grid_color is None
|
|
@@ -604,6 +605,8 @@ class NM_Plot:
|
|
|
604
605
|
feature_name=feature_str_add,
|
|
605
606
|
)
|
|
606
607
|
plt.savefig(plt_path, bbox_inches="tight")
|
|
607
|
-
|
|
608
|
+
logger.info(
|
|
609
|
+
f"Feature epoch average figure saved to: {str(plt_path)}"
|
|
610
|
+
)
|
|
608
611
|
if show_plot is False:
|
|
609
612
|
plt.close()
|
|
@@ -4,10 +4,8 @@ import pandas as pd
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class ReReferencer:
|
|
7
|
-
|
|
8
7
|
ref_matrix: np.ndarray
|
|
9
8
|
|
|
10
|
-
|
|
11
9
|
def __init__(
|
|
12
10
|
self,
|
|
13
11
|
sfreq: int | float,
|
|
@@ -28,11 +26,15 @@ class ReReferencer:
|
|
|
28
26
|
ValueError: rereferencing using undefined channel
|
|
29
27
|
ValueError: rereferencing to same channel
|
|
30
28
|
"""
|
|
31
|
-
|
|
29
|
+
nm_channels = nm_channels[nm_channels["used"] == 1].reset_index(
|
|
30
|
+
drop=True
|
|
31
|
+
)
|
|
32
|
+
# (channels_used,) = np.where((nm_channels.used == 1))
|
|
32
33
|
|
|
33
34
|
ch_names = nm_channels["name"].tolist()
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
# no re-referencing is being performed when there is a single channel present only
|
|
37
|
+
if nm_channels.shape[0] in (0, 1):
|
|
36
38
|
self.ref_matrix = None
|
|
37
39
|
return
|
|
38
40
|
|
|
@@ -48,8 +50,8 @@ class ReReferencer:
|
|
|
48
50
|
ref_matrix = np.zeros((len(nm_channels), len(nm_channels)))
|
|
49
51
|
for ind in range(len(nm_channels)):
|
|
50
52
|
ref_matrix[ind, ind] = 1
|
|
51
|
-
if ind not in channels_used:
|
|
52
|
-
|
|
53
|
+
# if ind not in channels_used:
|
|
54
|
+
# continue
|
|
53
55
|
ref = refs[ind]
|
|
54
56
|
if ref.lower() == "none" or pd.isnull(ref):
|
|
55
57
|
ref_idx = None
|
|
@@ -84,10 +86,10 @@ class ReReferencer:
|
|
|
84
86
|
shape(n_channels, n_samples) - data to be rereferenced.
|
|
85
87
|
|
|
86
88
|
Returns:
|
|
87
|
-
reref_data (numpy ndarray):
|
|
89
|
+
reref_data (numpy ndarray):
|
|
88
90
|
shape(n_channels, n_samples) - rereferenced data
|
|
89
91
|
"""
|
|
90
92
|
if self.ref_matrix is not None:
|
|
91
93
|
return self.ref_matrix @ data
|
|
92
94
|
else:
|
|
93
|
-
return data
|
|
95
|
+
return data
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"""This module contains the class to process a given batch of data."""
|
|
2
|
+
|
|
2
3
|
from enum import Enum
|
|
3
4
|
import math
|
|
4
5
|
import os
|
|
5
6
|
from time import time
|
|
6
7
|
from typing import Protocol, Type
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("PynmLogger")
|
|
7
11
|
|
|
8
12
|
import numpy as np
|
|
9
13
|
import pandas as pd
|
|
@@ -16,6 +20,7 @@ from py_neuromodulation import (
|
|
|
16
20
|
nm_projection,
|
|
17
21
|
nm_rereference,
|
|
18
22
|
nm_resample,
|
|
23
|
+
nm_filter_preprocessing,
|
|
19
24
|
)
|
|
20
25
|
|
|
21
26
|
_PathLike = str | os.PathLike
|
|
@@ -25,8 +30,7 @@ class Preprocessor(Protocol):
|
|
|
25
30
|
def process(self, data: np.ndarray) -> np.ndarray:
|
|
26
31
|
pass
|
|
27
32
|
|
|
28
|
-
def test_settings(self, settings: dict):
|
|
29
|
-
...
|
|
33
|
+
def test_settings(self, settings: dict): ...
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
_PREPROCESSING_CONSTRUCTORS = [
|
|
@@ -73,7 +77,7 @@ class DataProcessor:
|
|
|
73
77
|
notch_filter : nm_filter.NotchFilter,
|
|
74
78
|
Notch Filter object, needs to be instantiated beforehand
|
|
75
79
|
verbose : boolean
|
|
76
|
-
if True,
|
|
80
|
+
if True, log signal processed and computation time
|
|
77
81
|
"""
|
|
78
82
|
self.settings = self._load_settings(settings)
|
|
79
83
|
self.nm_channels = self._load_nm_channels(nm_channels)
|
|
@@ -119,6 +123,12 @@ class DataProcessor:
|
|
|
119
123
|
**self.settings.get(settings_str, {}),
|
|
120
124
|
)
|
|
121
125
|
self.preprocessors.append(preprocessor)
|
|
126
|
+
case "preprocessing_filter":
|
|
127
|
+
preprocessor = nm_filter_preprocessing.PreprocessingFilter(
|
|
128
|
+
settings=self.settings,
|
|
129
|
+
sfreq=self.sfreq_raw,
|
|
130
|
+
)
|
|
131
|
+
self.preprocessors.append(preprocessor)
|
|
122
132
|
case _:
|
|
123
133
|
raise ValueError(
|
|
124
134
|
"Invalid preprocessing method. Must be one of"
|
|
@@ -331,7 +341,7 @@ class DataProcessor:
|
|
|
331
341
|
|
|
332
342
|
nan_channels = np.isnan(data).any(axis=1)
|
|
333
343
|
|
|
334
|
-
data = np.nan_to_num(data)
|
|
344
|
+
data = np.nan_to_num(data)[self.feature_idx, :]
|
|
335
345
|
|
|
336
346
|
for processor in self.preprocessors:
|
|
337
347
|
data = processor.process(data)
|
|
@@ -341,8 +351,13 @@ class DataProcessor:
|
|
|
341
351
|
|
|
342
352
|
# normalize features
|
|
343
353
|
if self.settings["postprocessing"]["feature_normalization"]:
|
|
344
|
-
normed_features = self.feature_normalizer.process(
|
|
345
|
-
|
|
354
|
+
normed_features = self.feature_normalizer.process(
|
|
355
|
+
np.fromiter(features_dict.values(), dtype="float")
|
|
356
|
+
)
|
|
357
|
+
features_dict = {
|
|
358
|
+
key: normed_features[idx]
|
|
359
|
+
for idx, key in enumerate(features_dict.keys())
|
|
360
|
+
}
|
|
346
361
|
|
|
347
362
|
features_current = pd.Series(
|
|
348
363
|
data=list(features_dict.values()),
|
|
@@ -364,7 +379,7 @@ class DataProcessor:
|
|
|
364
379
|
] = np.nan
|
|
365
380
|
|
|
366
381
|
if self.verbose is True:
|
|
367
|
-
|
|
382
|
+
logger.info(
|
|
368
383
|
"Last batch took: "
|
|
369
384
|
+ str(np.round(time() - start_time, 2))
|
|
370
385
|
+ " seconds"
|
|
@@ -390,14 +405,14 @@ class DataProcessor:
|
|
|
390
405
|
sidecar["coords"] = self.projection.coords
|
|
391
406
|
if self.settings["postprocessing"]["project_cortex"]:
|
|
392
407
|
sidecar["grid_cortex"] = self.projection.grid_cortex
|
|
393
|
-
sidecar[
|
|
394
|
-
|
|
395
|
-
|
|
408
|
+
sidecar["proj_matrix_cortex"] = (
|
|
409
|
+
self.projection.proj_matrix_cortex
|
|
410
|
+
)
|
|
396
411
|
if self.settings["postprocessing"]["project_subcortex"]:
|
|
397
412
|
sidecar["grid_subcortex"] = self.projection.grid_subcortex
|
|
398
|
-
sidecar[
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
sidecar["proj_matrix_subcortex"] = (
|
|
414
|
+
self.projection.proj_matrix_subcortex
|
|
415
|
+
)
|
|
401
416
|
if additional_args is not None:
|
|
402
417
|
sidecar = sidecar | additional_args
|
|
403
418
|
|
|
@@ -105,23 +105,12 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
|
|
|
105
105
|
peak_right_val (np.ndarray): value of righ peak
|
|
106
106
|
"""
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
raise NoValidTroughException("No valid trough")
|
|
111
|
-
val_ind_greater = arr_ind_peaks[ind_greater]
|
|
112
|
-
peak_right_idx = arr_ind_peaks[
|
|
113
|
-
ind_greater[np.argsort(val_ind_greater)[0]]
|
|
114
|
-
]
|
|
115
|
-
|
|
116
|
-
ind_smaller = np.where(arr_ind_peaks < trough_ind)[0]
|
|
117
|
-
if ind_smaller.shape[0] == 0:
|
|
118
|
-
raise NoValidTroughException("No valid trough")
|
|
119
|
-
|
|
120
|
-
val_ind_smaller = arr_ind_peaks[ind_smaller]
|
|
121
|
-
peak_left_idx = arr_ind_peaks[
|
|
122
|
-
ind_smaller[np.argsort(val_ind_smaller)[-1]]
|
|
123
|
-
]
|
|
108
|
+
try: peak_right_idx = arr_ind_peaks[arr_ind_peaks > trough_ind][0]
|
|
109
|
+
except IndexError: raise NoValidTroughException("No valid trough")
|
|
124
110
|
|
|
111
|
+
try: peak_left_idx = arr_ind_peaks[arr_ind_peaks < trough_ind][-1]
|
|
112
|
+
except IndexError: raise NoValidTroughException("No valid trough")
|
|
113
|
+
|
|
125
114
|
return (
|
|
126
115
|
peak_left_idx,
|
|
127
116
|
peak_right_idx,
|
|
@@ -150,12 +139,10 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
|
|
|
150
139
|
"""
|
|
151
140
|
for ch_idx, ch_name in enumerate(self.ch_names):
|
|
152
141
|
for filter_name, filter in self.list_filter:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
data[ch_idx, :], filter, mode="same"
|
|
158
|
-
)
|
|
142
|
+
self.data_process_sw = (data[ch_idx, :]
|
|
143
|
+
if filter_name == "no_filter"
|
|
144
|
+
else signal.fftconvolve(data[ch_idx, :], filter, mode="same")
|
|
145
|
+
)
|
|
159
146
|
|
|
160
147
|
# check settings if troughs and peaks are analyzed
|
|
161
148
|
|
|
@@ -267,123 +254,103 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
|
|
|
267
254
|
distance=self.sw_settings["detect_troughs"]["distance_troughs_ms"],
|
|
268
255
|
)[0]
|
|
269
256
|
|
|
270
|
-
for
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
#
|
|
257
|
+
""" Find left and right peak indexes for each trough """
|
|
258
|
+
peak_pointer = 0
|
|
259
|
+
peak_idx_left = []
|
|
260
|
+
peak_idx_right = []
|
|
261
|
+
first_valid = last_valid = 0
|
|
262
|
+
|
|
263
|
+
for i, trough_idx in enumerate(troughs):
|
|
264
|
+
|
|
265
|
+
# Locate peak right of current trough
|
|
266
|
+
while peak_pointer < peaks.size and peaks[peak_pointer] < trough_idx:
|
|
267
|
+
peak_pointer += 1
|
|
268
|
+
|
|
269
|
+
if peak_pointer - 1 < 0:
|
|
270
|
+
# If trough has no peak to it's left, it's not valid
|
|
271
|
+
first_valid = i + 1 # Try with next one
|
|
284
272
|
continue
|
|
285
273
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
if self.sw_settings["sharpwave_features"]["interval"] is True:
|
|
291
|
-
if len(self.troughs_idx) > 1:
|
|
292
|
-
# take the last identified trough idx
|
|
293
|
-
# corresponds here to second last trough_idx
|
|
294
|
-
|
|
295
|
-
interval = (trough_idx - self.troughs_idx[-2]) * (
|
|
296
|
-
1000 / self.sfreq
|
|
297
|
-
)
|
|
298
|
-
else:
|
|
299
|
-
# set first interval to zero
|
|
300
|
-
interval = 0
|
|
301
|
-
self.interval.append(interval)
|
|
302
|
-
|
|
303
|
-
if self.sw_settings["sharpwave_features"]["peak_left"] is True:
|
|
304
|
-
self.peak_left.append(peak_left)
|
|
305
|
-
|
|
306
|
-
if self.sw_settings["sharpwave_features"]["peak_right"] is True:
|
|
307
|
-
self.peak_right.append(peak_right)
|
|
308
|
-
|
|
309
|
-
if self.sw_settings["sharpwave_features"]["sharpness"] is True:
|
|
310
|
-
# check if sharpness can be calculated
|
|
311
|
-
# trough_idx 5 ms need to be consistent
|
|
312
|
-
if (trough_idx - int(5 * (1000 / self.sfreq)) <= 0) or (
|
|
313
|
-
trough_idx + int(5 * (1000 / self.sfreq))
|
|
314
|
-
>= self.data_process_sw.shape[0]
|
|
315
|
-
):
|
|
316
|
-
continue
|
|
317
|
-
|
|
318
|
-
sharpness = (
|
|
319
|
-
(
|
|
320
|
-
self.data_process_sw[trough_idx]
|
|
321
|
-
- self.data_process_sw[
|
|
322
|
-
trough_idx - int(5 * (1000 / self.sfreq))
|
|
323
|
-
]
|
|
324
|
-
)
|
|
325
|
-
+ (
|
|
326
|
-
self.data_process_sw[trough_idx]
|
|
327
|
-
- self.data_process_sw[
|
|
328
|
-
trough_idx + int(5 * (1000 / self.sfreq))
|
|
329
|
-
]
|
|
330
|
-
)
|
|
331
|
-
) / 2
|
|
332
|
-
|
|
333
|
-
self.sharpness.append(sharpness)
|
|
334
|
-
|
|
335
|
-
if self.sw_settings["sharpwave_features"]["rise_steepness"] is True:
|
|
336
|
-
# steepness is calculated as the first derivative
|
|
337
|
-
# from peak/trough to trough/peak
|
|
338
|
-
# here + 1 due to python syntax, s.t. the last element is included
|
|
339
|
-
rise_steepness = np.max(
|
|
340
|
-
np.diff(
|
|
341
|
-
self.data_process_sw[peak_idx_left : trough_idx + 1]
|
|
342
|
-
)
|
|
343
|
-
)
|
|
344
|
-
self.rise_steepness.append(rise_steepness)
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
self.sw_settings["sharpwave_features"]["decay_steepness"]
|
|
348
|
-
is True
|
|
349
|
-
):
|
|
350
|
-
decay_steepness = np.max(
|
|
351
|
-
np.diff(
|
|
352
|
-
self.data_process_sw[trough_idx : peak_idx_right + 1]
|
|
353
|
-
)
|
|
354
|
-
)
|
|
355
|
-
self.decay_steepness.append(decay_steepness)
|
|
356
|
-
|
|
357
|
-
if (
|
|
358
|
-
self.sw_settings["sharpwave_features"]["rise_steepness"] is True
|
|
359
|
-
and self.sw_settings["sharpwave_features"]["decay_steepness"]
|
|
360
|
-
is True
|
|
361
|
-
and self.sw_settings["sharpwave_features"]["slope_ratio"]
|
|
362
|
-
is True
|
|
363
|
-
):
|
|
364
|
-
self.slope_ratio.append(rise_steepness - decay_steepness)
|
|
365
|
-
|
|
366
|
-
if self.sw_settings["sharpwave_features"]["prominence"] is True:
|
|
367
|
-
self.prominence.append(
|
|
368
|
-
np.abs(
|
|
369
|
-
(peak_right + peak_left) / 2
|
|
370
|
-
- self.data_process_sw[trough_idx]
|
|
371
|
-
)
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
if self.sw_settings["sharpwave_features"]["decay_time"] is True:
|
|
375
|
-
self.decay_time.append(
|
|
376
|
-
(peak_idx_left - trough_idx) * (1000 / self.sfreq)
|
|
377
|
-
) # ms
|
|
378
|
-
|
|
379
|
-
if self.sw_settings["sharpwave_features"]["rise_time"] is True:
|
|
380
|
-
self.rise_time.append(
|
|
381
|
-
(peak_idx_right - trough_idx) * (1000 / self.sfreq)
|
|
382
|
-
) # ms
|
|
383
|
-
|
|
384
|
-
if self.sw_settings["sharpwave_features"]["width"] is True:
|
|
385
|
-
self.width.append(peak_idx_right - peak_idx_left) # ms
|
|
274
|
+
if peak_pointer == peaks.size:
|
|
275
|
+
# If we went past the end of the peaks list, trough had no peak to its right
|
|
276
|
+
continue
|
|
386
277
|
|
|
278
|
+
last_valid = i
|
|
279
|
+
peak_idx_left.append(peaks[peak_pointer - 1])
|
|
280
|
+
peak_idx_right.append(peaks[peak_pointer])
|
|
281
|
+
|
|
282
|
+
troughs = troughs[first_valid:last_valid + 1] # Remove non valid troughs
|
|
283
|
+
|
|
284
|
+
peak_idx_left = np.array(peak_idx_left, dtype=np.integer)
|
|
285
|
+
peak_idx_right = np.array(peak_idx_right, dtype=np.integer)
|
|
286
|
+
|
|
287
|
+
peak_left = self.data_process_sw[peak_idx_left]
|
|
288
|
+
peak_right = self.data_process_sw[peak_idx_right]
|
|
289
|
+
trough_values = self.data_process_sw[troughs]
|
|
290
|
+
|
|
291
|
+
# No need to store trough data as it is not used anywhere else in the program
|
|
292
|
+
# self.trough.append(trough)
|
|
293
|
+
# self.troughs_idx.append(trough_idx)
|
|
294
|
+
|
|
295
|
+
""" Calculate features (vectorized) """
|
|
296
|
+
|
|
297
|
+
if self.sw_settings["sharpwave_features"]["interval"]:
|
|
298
|
+
self.interval = np.concatenate(([0], np.diff(troughs))) * (1000 / self.sfreq)
|
|
299
|
+
|
|
300
|
+
if self.sw_settings["sharpwave_features"]["peak_left"]:
|
|
301
|
+
self.peak_left = peak_left
|
|
302
|
+
|
|
303
|
+
if self.sw_settings["sharpwave_features"]["peak_right"]:
|
|
304
|
+
self.peak_right = peak_right
|
|
305
|
+
|
|
306
|
+
if self.sw_settings["sharpwave_features"]["sharpness"]:
|
|
307
|
+
# sharpess is calculated on a +- 5 ms window
|
|
308
|
+
# valid troughs need 5 ms of margin on both siddes
|
|
309
|
+
troughs_valid = troughs[np.logical_and(
|
|
310
|
+
troughs - int(5 * (1000 / self.sfreq)) > 0,
|
|
311
|
+
troughs + int(5 * (1000 / self.sfreq)) < self.data_process_sw.shape[0])]
|
|
312
|
+
|
|
313
|
+
self.sharpness = (
|
|
314
|
+
(self.data_process_sw[troughs_valid] - self.data_process_sw[troughs_valid - int(5 * (1000 / self.sfreq))]) +
|
|
315
|
+
(self.data_process_sw[troughs_valid] - self.data_process_sw[troughs_valid + int(5 * (1000 / self.sfreq))])
|
|
316
|
+
) / 2
|
|
317
|
+
|
|
318
|
+
if (self.sw_settings["sharpwave_features"]["rise_steepness"] or
|
|
319
|
+
self.sw_settings["sharpwave_features"]["decay_steepness"]):
|
|
320
|
+
|
|
321
|
+
# steepness is calculated as the first derivative
|
|
322
|
+
steepness = np.concatenate(([0],np.diff(self.data_process_sw)))
|
|
323
|
+
|
|
324
|
+
if self.sw_settings["sharpwave_features"]["rise_steepness"]: # left peak -> trough
|
|
325
|
+
# + 1 due to python syntax, s.t. the last element is included
|
|
326
|
+
self.rise_steepness = np.array([
|
|
327
|
+
np.max(np.abs(steepness[peak_idx_left[i] : troughs[i] + 1]))
|
|
328
|
+
for i in range(trough_idx.size)
|
|
329
|
+
])
|
|
330
|
+
|
|
331
|
+
if self.sw_settings["sharpwave_features"]["decay_steepness"]: # trough -> right peak
|
|
332
|
+
self.decay_steepness = np.array([
|
|
333
|
+
np.max(np.abs(steepness[troughs[i] : peak_idx_right[i] + 1]))
|
|
334
|
+
for i in range(trough_idx.size)
|
|
335
|
+
])
|
|
336
|
+
|
|
337
|
+
if (self.sw_settings["sharpwave_features"]["rise_steepness"] and
|
|
338
|
+
self.sw_settings["sharpwave_features"]["decay_steepness"] and
|
|
339
|
+
self.sw_settings["sharpwave_features"]["slope_ratio"]):
|
|
340
|
+
self.slope_ratio = self.rise_steepness - self.decay_steepness
|
|
341
|
+
|
|
342
|
+
if self.sw_settings["sharpwave_features"]["prominence"]:
|
|
343
|
+
self.prominence = np.abs((peak_right + peak_left) / 2 - trough_values)
|
|
344
|
+
|
|
345
|
+
if self.sw_settings["sharpwave_features"]["decay_time"]:
|
|
346
|
+
self.decay_time = (peak_idx_left - troughs) * (1000 / self.sfreq) # ms
|
|
347
|
+
|
|
348
|
+
if self.sw_settings["sharpwave_features"]["rise_time"]:
|
|
349
|
+
self.rise_time = (peak_idx_right - troughs) * (1000 / self.sfreq) # ms
|
|
350
|
+
|
|
351
|
+
if self.sw_settings["sharpwave_features"]["width"]:
|
|
352
|
+
self.width = peak_idx_right - peak_idx_left # ms
|
|
353
|
+
|
|
387
354
|
@staticmethod
|
|
388
355
|
def test_settings(
|
|
389
356
|
s: dict,
|