Skip to content

Commit

Permalink
test
Browse files Browse the repository at this point in the history
  • Loading branch information
xmival00 committed Jul 5, 2024
1 parent 74f8466 commit 46c5ff2
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 124 deletions.
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ Sleep classification and feature extraction
Automated feature extraction and sleep classification was developed during the following projects.

| F. Mivalt et V. Kremen et al., “Electrical brain stimulation and continuous behavioral state tracking in ambulatory humans,” J. Neural Eng., vol. 19, no. 1, p. 016019, Feb. 2022, doi: 10.1088/1741-2552/ac4bfd.
|
| F. Mivalt et V. Sladky et al., “Automated sleep classification with chronic neural implants in freely behaving canines,” J. Neural Eng., vol. 20, no. 4, p. 046025, Aug. 2023, doi: 10.1088/1741-2552/aced21.
Our work was based on the following references:

| Gerla, V., Kremen, V., Macas, M., Dudysova, D., Mladek, A., Sos, P., & Lhotska, L. (2019). Iterative expert-in-the-loop classification of sleep PSG recordings using a hierarchical clustering. Journal of Neuroscience Methods, 317(February), 61?70. https://doi.org/10.1016/j.jneumeth.2019.01.013
|
| Kremen, V., Brinkmann, B. H., Van Gompel, J. J., Stead, S. (Matt) M., St Louis, E. K., & Worrell, G. A. (2018). Automated Unsupervised Behavioral State Classification using Intracranial Electrophysiology. Journal of Neural Engineering. https://doi.org/10.1088/1741-2552/aae5ab
|
| Kremen, V., Duque, J. J., Brinkmann, B. H., Berry, B. M., Kucewicz, M. T., Khadjevand, F., G.A. Worrell, G. A. (2017). Behavioral state classification in epileptic brain using intracranial electrophysiology. Journal of Neural Engineering, 14(2), 026001. https://doi.org/10.1088/1741-2552/aa5688

Expand All @@ -69,7 +69,7 @@ Evoked Response Potential Analysis
EEG Slow Wave Detection and Analysis
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Readme to the EEG Slow Detection project available in this repository in this repository: `projects/slow_wave_detection.rst <./projects/slow_wave_detection.rst>`_.
Readme to the EEG Slow Detection project available in this repository in this repository: `projects/slow_wave_detection/readme <./projects/slow_wave_detection/readme.rst>`_.

| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Expand Down
Binary file removed _images/favicon-32x32_piespro.png
Binary file not shown.
Binary file removed _images/icon_piespro.png
Binary file not shown.
Binary file removed _images/logo.png
Binary file not shown.
Binary file removed _images/logo_final_noframe_white.png
Binary file not shown.
Binary file removed _images/logo_frame.png
Binary file not shown.
Binary file added _images/slope_vaclav.pdf
Binary file not shown.
Binary file added _images/slow_waves_example.pdf
Binary file not shown.
169 changes: 62 additions & 107 deletions best/feature_extraction/WaveDetector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,122 +5,72 @@
# LICENSE file in the root directory of this source tree.


"""
This section contains codes for extracting and analyzing electrophysiology features from EEG data - Slow Wave Detector
The code also plots the extracted features and saves the plots to PDF files.
The code can be run from the command line or from a Python IDE.
The code requires the following packages to be installed: numpy, scipy, matplotlib.
Acknowledgements:
The code is part of the project of Analyzing EEG data from sleep studies for publication of manuscript:
NREM sleep slow wave activity features are associated with amyloid accumulation in older adults with
obstructive sleep apnea. By D. Carvalho et al., 2024
The code is based on the following publication and extends and improves the features and methods of original
wave detections from the paper:
Riedner, B. a., Vyazovskiy, V. V., Huber, R., Massimini, M., Esser, S., Murphy, M., & Tononi, G. (2007).
Sleep homeostasis and cortical synchronization:
III. A high-density EEG study of sleep slow waves in humans. Sleep, 30(12), 1643–1657.
Sleep classification and feature extraction
F. Mivalt et V. Kremen et al., “Electrical brain stimulation and continuous behavioral state tracking in ambulatory humans,” J. Neural Eng., vol. 19, no. 1, p. 016019, Feb. 2022, doi: 10.1088/1741-2552/ac4bfd.
F. Mivalt et V. Sladky et al., “Automated sleep classification with chronic neural implants in freely behaving canines,” J. Neural Eng., vol. 20, no. 4, p. 046025, Aug. 2023, doi: 10.1088/1741-2552/aced21.
Gerla, V., Kremen, V., Macas, M., Dudysova, D., Mladek, A., Sos, P., & Lhotska, L. (2019). Iterative expert-in-the-loop classification of sleep PSG recordings using a hierarchical clustering. Journal of Neuroscience Methods, 317(February), 61?70. https://doi.org/10.1016/j.jneumeth.2019.01.013
Kremen, V., Brinkmann, B. H., Van Gompel, J. J., Stead, S. (Matt) M., St Louis, E. K., & Worrell, G. A. (2018). Automated Unsupervised Behavioral State Classification using Intracranial Electrophysiology. Journal of Neural Engineering. https://doi.org/10.1088/1741-2552/aae5ab
Kremen, V., Duque, J. J., Brinkmann, B. H., Berry, B. M., Kucewicz, M. T., Khadjevand, F., G.A. Worrell, G. A. (2017). Behavioral state classification in epileptic brain using intracranial electrophysiology. Journal of Neural Engineering, 14(2), 026001. https://doi.org/10.1088/1741-2552/aa5688
The BEST was developed under projects supported by NIH Brain Initiative UH2&3 NS095495 Neurophysiologically-Based
Brain State Tracking & Modulation in Focal Epilepsy, DARPA HR0011-20-2-0028 Manipulating and Optimizing Brain Rhythms
for Enhancement of Sleep (Morpheus). Filip Mivalt was also partially supported by the grant FEKT-K-22-7649 realized
within the project Quality Internal Grants of the Brno University of Technology (KInG BUT),
Reg. No. CZ.02.2.69/0.0/0.0/19_073/0016948, which is financed from the OP RDE.
# License:
# See the LICENSE file in the root directory of this repository for license information.
Version 1.0 (2024-06-01) by V. Kremen (Kremen.Vaclav@mayo.edu)
endregion Description and Acknowledgements
"""


"""

import numpy as np
from copy import deepcopy
from best.signal import LowFrequencyFilter, fft_filter

A generic wave detector - WaveDetector object detects minimums and maximums of waves in a given frequency bandpass. Designed mainly to detect slow (delta - 0.5 - 4 Hz) waves. WaveDetector can also return statistical report of detected waves such as Δt, slope, peak2peak and min and max values.
class WaveDetector:
"""
A generic wave detector - WaveDetector object detects minimums and maximums of waves in a given frequency bandpass. Designed mainly to detect slow (delta - 0.5 - 4 Hz) waves. WaveDetector can also return statistical report of detected waves such as Δt, slope, peak2peak and min and max values.
Designed for Delta wave detection. Can be deployed on any frequency range.
Example
^^^^^^^^
Example
^^^^^^^^
.. code-block:: python
.. code-block:: python
import numpy as np
import matplotlib.pyplot as plt
from sleep_classification.WaveDetector import WaveDetector
import numpy as np
import matplotlib.pyplot as plt
from sleep_classification.WaveDetector import WaveDetector
fs = 200
signal_length = 5 # s
wave_freq1 = 2
wave_freq2 = 1
noise_level = 0.5
fs = 200
signal_length = 5 # s
wave_freq1 = 2
wave_freq2 = 1
noise_level = 0.5
t = np.arange(0, signal_length * fs) / fs
noise = np.random.randn(t.shape[0]) * noise_level
x1 = np.sin(2*np.pi * wave_freq1 * t)
x2 = np.sin(2*np.pi * wave_freq2 * t) * 0.5
x_noise = x1 + x2 + noise
t = np.arange(0, signal_length * fs) / fs
noise = np.random.randn(t.shape[0]) * noise_level
x1 = np.sin(2*np.pi * wave_freq1 * t)
x2 = np.sin(2*np.pi * wave_freq2 * t) * 0.5
x_noise = x1 + x2 + noise
WDet = WaveDetector(fs=fs, cutoff_low=0.5, cutoff_high=4)
WDet = WaveDetector(fs=fs, cutoff_low=0.5, cutoff_high=4)
stats, det = WDet(x_noise)
min_pos = det['min_pos']
min_val = det['min_val']
max_pos = det['max_pos']
max_val = det['max_val']
stats, det = WDet(x_noise)
min_pos = det['min_pos']
min_val = det['min_val']
max_pos = det['max_pos']
max_val = det['max_val']
#plt.plot(t, x_noise)
#plt.xlabel('t [s]')
#plt.title('Original')
#plt.show()
#plt.plot(t, x_noise)
#plt.xlabel('t [s]')
#plt.title('Original')
#plt.show()
plt.plot(t, x_noise)
plt.stem(t[min_pos], min_val, 'r')
plt.stem(t[max_pos], max_val, 'k')
plt.title('Detections')
plt.show()
plt.plot(t, x_noise)
plt.stem(t[min_pos], min_val, 'r')
plt.stem(t[max_pos], max_val, 'k')
plt.title('Detections')
plt.show()
print('Slope Stats')
print(stats['slope_stats'])
print('Slope Stats')
print(stats['slope_stats'])
print('Peak2Peak Stats')
print(stats['pk2pk_stats'])
print('Peak2Peak Stats')
print(stats['pk2pk_stats'])
print('delta_t Stats')
print(stats['delta_t_stats'])
print('delta_t Stats')
print(stats['delta_t_stats'])
.. image:: ../../_images/1_det.png
:width: 300
.. image:: ../../_images/1_det.png
:width: 300
"""
import numpy as np
from copy import deepcopy
from best.signal import LowFrequencyFilter, fft_filter

class WaveDetector:
"""
Designed for Delta wave detection. Can be deployed on any frequency range. See the documentation above for an usage example.
"""

def __init__(self, fs, cutoff_low=0.5, cutoff_high=4):
Expand Down Expand Up @@ -387,7 +337,7 @@ def _correct_extreme_positions(X, extreme_positions, n_samples_interval=10, tag=

##### Vaclav Detector

def bandpass_filter(data, lowcut, highcut, fs, order=4):
def _bandpass_filter(data, lowcut, highcut, fs, order=4):
"""
Apply a bandpass filter to the data.
Expand All @@ -408,7 +358,7 @@ def bandpass_filter(data, lowcut, highcut, fs, order=4):
y = filtfilt(b, a, data)
return y

def moving_average(data, window_size):
def _moving_average(data, window_size):
"""
Apply a moving average filter to the data.
Expand All @@ -422,7 +372,7 @@ def moving_average(data, window_size):
# Filter the signal by moving average filter
return np.convolve(data, np.ones(window_size) / window_size, mode='same')

def zero_crossings(data):
def _zero_crossings(data):
"""
Find the indices where the data crosses zero.
Expand All @@ -434,7 +384,7 @@ def zero_crossings(data):
"""
return np.where(np.diff(np.signbit(data)))[0]

def detect_slow_waves(data, zeros, fs, max_distance, min_distance, amplitude_threshold):
def _detect_slow_waves(data, zeros, fs, max_distance, min_distance, amplitude_threshold):
"""
Detect slow waves in the data.
Expand Down Expand Up @@ -468,10 +418,15 @@ def detect_slow_waves(data, zeros, fs, max_distance, min_distance, amplitude_thr
slow_waves.append((start, end, amplitude, slope, trough_idx + start))
return slow_waves

def SlowWaveDetect(data, fs, max_distance, min_distance, amplitude_threshold, pdf_path, sleep_state,
epoch_number, slow_waves_to_remove=None, time_threshold=None, verbose=True):
def slow_wave_detect(data, fs, max_distance, min_distance, amplitude_threshold, pdf_path, sleep_state,
epoch_number, slow_waves_to_remove=None, time_threshold=None, verbose=True):
"""
Detect slow waves in EEG data and plot the results.
The code is based on the following publication and extends and improves the features and methods of original
wave detections from the paper:
- Riedner, B. a., Vyazovskiy, V. V., Huber, R., Massimini, M., Esser, S., Murphy, M., & Tononi, G. (2007).
Sleep homeostasis and cortical synchronization: III. A high-density EEG study of sleep slow waves in humans. Sleep, 30(12), 1643–1657.
Parameters:
data (array-like): Input EEG data.
Expand All @@ -490,13 +445,13 @@ def SlowWaveDetect(data, fs, max_distance, min_distance, amplitude_threshold, pd
tuple: Tuple containing the detected slow waves, their amplitudes and slopes, the mean and standard deviation of the amplitudes and slopes, and the number of detected waves.
"""
# Filter the signal by 50 msec moving average filter
filtered_eeg = moving_average(data, int(0.05 * fs)) # 50 msec moving average filter
filtered_eeg = _moving_average(data, int(0.05 * fs)) # 50 msec moving average filter

# Detect zero crossings
zeros = zero_crossings(filtered_eeg)
zeros = _zero_crossings(filtered_eeg)

# Identify slow waves
slow_waves = detect_slow_waves(filtered_eeg, zeros, fs, max_distance, min_distance, amplitude_threshold)
slow_waves = _detect_slow_waves(filtered_eeg, zeros, fs, max_distance, min_distance, amplitude_threshold)

# Remove slow waves close to those in 'slow_waves_to_remove' if provided
if slow_waves_to_remove is not None and time_threshold is not None:
Expand Down Expand Up @@ -575,5 +530,5 @@ def is_wave_close(wave, waves_to_remove, threshold_samples):

__all__ = [
'WaveDetector',

'slow_wave_detect'
]
15 changes: 8 additions & 7 deletions projects/slow_wave_detection/example_one_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
import math
from datetime import datetime
import concurrent.futures
import pickle

from scipy.io import savemat, loadmat

import matplotlib.pyplot as plt

Expand All @@ -73,7 +74,7 @@
# endregion imports

# region FILE_PATH
DATA_PATH = f'path/to/data.mat'
DATA_PATH = f'./patient_one_data.mat'
# endregion FILE_PATH

# region Parameters
Expand Down Expand Up @@ -502,8 +503,8 @@ def do_stats(path):
mean_frequency, median_frequency, mean_bands, relative_bands
]

# region Save the data to a pickle file for debugging and preparing the data
# import pickle
# region Save the data to a mat file for debugging and preparing the data
# from scipy.io import loadmat
#
# data = {
# 'fzcz': fzcz,
Expand All @@ -515,9 +516,9 @@ def do_stats(path):
# }
# filename = f'patient_one_data.pkl'
# savemat(DATA_PATH, data, do_compression=True)
# endregion Save the data to a pickle file
# endregion Save the data to a mat file

# region Load the data from a pickle file
# region Load the data from a mat file
# Construct the filename with the epoch number

# Load the data from the file
Expand Down Expand Up @@ -984,4 +985,4 @@ def do_stats(path):

# Save final 'res' to a CSV file
res.to_csv('Results_extraction.csv', index=False)
# endregion Main
# endregion Main
Binary file added projects/slow_wave_detection/patient_one_data.mat
Binary file not shown.
23 changes: 17 additions & 6 deletions projects/slow_wave_detection/readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ Version
Version 1.0 (2024-06-01) by V. Kremen (Kremen.Vaclav@mayo.edu)


Examples
""""""""""""""""""
.. image:: ../../_images/1_det.png
:width: 300

.. image:: ../../_images/slope_vaclav.pdf
:width: 300



Code
""""""""""""""""""
The code is also attached for convenience in here:
Expand Down Expand Up @@ -90,7 +100,8 @@ The code is also attached for convenience in here:
import math
from datetime import datetime
import concurrent.futures
import pickle
from scipy.io import savemat, loadmat
import matplotlib.pyplot as plt
Expand All @@ -110,7 +121,7 @@ The code is also attached for convenience in here:
# endregion imports
# region FILE_PATH
DATA_PATH = f'path/to/data.mat'
DATA_PATH = f'./patient_one_data.mat'
# endregion FILE_PATH
# region Parameters
Expand Down Expand Up @@ -525,8 +536,8 @@ The code is also attached for convenience in here:
mean_frequency, median_frequency, mean_bands, relative_bands
]
# region Save the data to a pickle file for debugging and preparing the data
# import pickle
# region Save the data to a mat file for debugging and preparing the data
# from scipy.io import loadmat
#
# data = {
# 'fzcz': fzcz,
Expand All @@ -538,9 +549,9 @@ The code is also attached for convenience in here:
# }
# filename = f'patient_one_data.pkl'
# savemat(DATA_PATH, data, do_compression=True)
# endregion Save the data to a pickle file
# endregion Save the data to a mat file
# region Load the data from a pickle file
# region Load the data from a mat file
# Construct the filename with the epoch number
# Load the data from the file
Expand Down

0 comments on commit 46c5ff2

Please sign in to comment.