From bc4dfaa79721451cad07da597176727aff258baa Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:51:00 +0330 Subject: [PATCH 01/10] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index cd4a34d0..a12f4d49 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,19 @@ SleepECG provides tools for sleep stage classification when [EEG](https://en.wik - detecting heartbeats from ECG signals, and - classifying sleep stages (which includes preprocessing, feature extraction, and classification). +## HeartRate Data +Description: +I have introduced Changes to the SleepECG package, primarily focusing on enabling sleep stage classification using heart rate data when EEG signals are not available. Key features of this update include: +Heart Rate to R-Interval Conversion: + +Implemented an algorithm to convert heart rate data into R-intervals. This crucial development allows the use of heart rate data, which is more readily available in many scenarios, for sleep stage classification. +Handling Data Gaps: + +Added robustness to the system by integrating code that effectively handles scenarios where data might be missing for extended periods. This ensures more reliable performance and resilience in real-world applications. +Web-Based and Local Application: + +Originally developed for a web-based project, these enhancements are equally applicable for local deployments, thereby broadening the usability scope of the SleepECG package. ### Documentation Documentation for SleepECG is available on [Read the Docs](https://sleepecg.readthedocs.io/en/stable/index.html). Check out the [changelog](https://github.com/cbrnr/sleepecg/blob/main/CHANGELOG.md) to learn what we added, changed, or fixed. From db6f04f0d2ba025e808ec915150e9bb1237048dc Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:55:47 +0330 Subject: [PATCH 02/10] Update README.md --- README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a12f4d49..b4c5a2d1 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,34 @@ from sleepecg import detect_heartbeats, get_toy_ecg ecg, fs = get_toy_ecg() # 5 min of ECG data at 360 Hz beats = detect_heartbeats(ecg, fs) # indices of detected heartbeats ``` - +### Example if Heart Rate data is available +data = [ + {'TimeStamp': '1690409900', 'HeartRate': '55'}, + {'TimeStamp': '1690410000', 'HeartRate': '60'}, + {'TimeStamp': '1690410120', 'HeartRate': '65'}, + {'TimeStamp': '1690410300', 'HeartRate': '62'}, + {'TimeStamp': '1690410600', 'HeartRate': '59'}, + {'TimeStamp': '1690410900', 'HeartRate': '58'}, + {'TimeStamp': '1690411200', 'HeartRate': '61'}, + {'TimeStamp': '1690411320', 'HeartRate': '56'}, + {'TimeStamp': '1690411380', 'HeartRate': '52'}, + {'TimeStamp': '1690411440', 'HeartRate': '50'}, + {'TimeStamp': '1690411500', 'HeartRate': '56'}, + {'TimeStamp': '1690411560', 'HeartRate': '58'}, + {'TimeStamp': '1690411620', 'HeartRate': '60'}, + {'TimeStamp': '1690411680', 'HeartRate': '65'}, + {'TimeStamp': '1690412740', 'HeartRate': '60'}, + {'TimeStamp': '1690412800', 'HeartRate': '62'}, + {'TimeStamp': '1690413800', 'HeartRate': '62'}, + {'TimeStamp': '1690514000', 'HeartRate': '62'} +] +final_rec , combined_stages_pred , stages_mode ,long_interval_indices = sleepanalyse(data) +plot_hypnogram( + final_rec, + combined_stages_pred, + stages_mode=stages_mode ) +plt.show() +![output_figure](https://github.com/MashRiza/sleepecg/assets/133785714/f22c2e56-58f2-4f25-84a1-57247ebf33e7) ### Dependencies From bd27944c7fd888c7222b52bcfe06069004a88078 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:59:28 +0330 Subject: [PATCH 03/10] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b4c5a2d1..9b37e02d 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ ecg, fs = get_toy_ecg() # 5 min of ECG data at 360 Hz beats = detect_heartbeats(ecg, fs) # indices of detected heartbeats ``` ### Example if Heart Rate data is available +``` data = [ {'TimeStamp': '1690409900', 'HeartRate': '55'}, {'TimeStamp': '1690410000', 'HeartRate': '60'}, @@ -96,8 +97,10 @@ plot_hypnogram( combined_stages_pred, stages_mode=stages_mode ) plt.show() -![output_figure](https://github.com/MashRiza/sleepecg/assets/133785714/f22c2e56-58f2-4f25-84a1-57247ebf33e7) - +``` +
+ output_figure +
### Dependencies SleepECG requires Python ≥ 3.9 and the following packages: From b57ccd2234b9bfc5a7b14867a8deef1cbe122ad4 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:01:42 +0330 Subject: [PATCH 04/10] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9b37e02d..641ecf78 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,9 @@ ecg, fs = get_toy_ecg() # 5 min of ECG data at 360 Hz beats = detect_heartbeats(ecg, fs) # indices of detected heartbeats ``` ### Example if Heart Rate data is available + +The following example stages sleep with heart rate data : + ``` data = [ {'TimeStamp': '1690409900', 'HeartRate': '55'}, @@ -101,6 +104,7 @@ plt.show()
output_figure
+ ### Dependencies SleepECG requires Python ≥ 3.9 and the following packages: From 0c3276f2f8468d425f42e8b95e07791dde489d31 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:04:31 +0330 Subject: [PATCH 05/10] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 641ecf78..49f8e2b5 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ plt.show() output_figure +the code for sleepanalyse function is in HeartRateData.py in the example folder . ### Dependencies SleepECG requires Python ≥ 3.9 and the following packages: From 02ef7277212e2fb201e5d2d270417660c6381097 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:06:20 +0330 Subject: [PATCH 06/10] adding HeartRateData.py file to example folder --- examples/HeartRateData.py | 149 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 examples/HeartRateData.py diff --git a/examples/HeartRateData.py b/examples/HeartRateData.py new file mode 100644 index 00000000..012f0210 --- /dev/null +++ b/examples/HeartRateData.py @@ -0,0 +1,149 @@ +import random +import numpy as np +from sleepecg import load_classifier, stage , SleepRecord , extract_features , plot_hypnogram +# importing matplotlib for local uses +import matplotlib.pyplot as plt +import random +data = [ + + {'TimeStamp': '1690409900', 'HeartRate': '55'}, + {'TimeStamp': '1690410000', 'HeartRate': '60'}, + {'TimeStamp': '1690410120', 'HeartRate': '65'}, + {'TimeStamp': '1690410300', 'HeartRate': '62'}, + {'TimeStamp': '1690410600', 'HeartRate': '59'}, + {'TimeStamp': '1690410900', 'HeartRate': '58'}, + {'TimeStamp': '1690411200', 'HeartRate': '61'}, + {'TimeStamp': '1690411320', 'HeartRate': '56'}, + {'TimeStamp': '1690411380', 'HeartRate': '52'}, + {'TimeStamp': '1690411440', 'HeartRate': '50'}, + {'TimeStamp': '1690411500', 'HeartRate': '56'}, + {'TimeStamp': '1690411560', 'HeartRate': '58'}, + {'TimeStamp': '1690411620', 'HeartRate': '60'}, + {'TimeStamp': '1690411680', 'HeartRate': '65'}, + {'TimeStamp': '1690412740', 'HeartRate': '60'}, + {'TimeStamp': '1690412800', 'HeartRate': '62'}, + {'TimeStamp': '1690413800', 'HeartRate': '62'}, + {'TimeStamp': '1690514000', 'HeartRate': '62'} +] + +clf = load_classifier("wrn-gru-mesa-weighted", "SleepECG") +def sleepanalyse(data , min_treshhold = 1500 , sleep_stage_duration = 60) : + # Convert to NumPy arrays + timestamps = [int(d['TimeStamp']) for d in data] + heart_rates = [int(d['HeartRate']) for d in data] + + + # Define a function to generate R-peak times on-the-fly to reduce cpu usage + # this function generates r peaks based on timeinterval between two timestamp and heart rate + + def generate_synthetic_r_peaks(timestamps, heart_rates): + peak_count = 0 + current_time = 0 # in unix seconds, relative to the start of recording + max_peaks = 0 + for i in range(len(timestamps) - 1): + time_interval = timestamps[i + 1] - timestamps[i] + n_peaks = int(time_interval / (60.0 / heart_rates[i])) + max_peaks += n_peaks + + for j in range(n_peaks): + if peak_count >= max_peaks: + return + r_peak_interval = (60.0 / heart_rates[i]) + random.uniform(0.001, 0.01) # Add a small random + current_time += r_peak_interval + yield current_time # Yield the R-peak time as needed + peak_count += 1 + + + + # Identify long intervals + long_interval_indices = [] + for i in range(len(timestamps) - 1): + time_interval = timestamps[i + 1] - timestamps[i] + if time_interval > 1800: # 30 minutes + long_interval_indices.append(i) + + + + combined_stages_pred = [] + aggregated_heartbeat_times = [] + start_idx = 0 + for end_idx in long_interval_indices: + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:end_idx + 1], heart_rates[start_idx:end_idx + 1] + )) + aggregated_heartbeat_times.extend(heartbeat_times_list) + # Check if heartbeat_times_list has enough elements + if len(heartbeat_times_list) > min_treshhold: + rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=heartbeat_times_list + ) + stages_pred = stage(clf, rec, return_mode="prob") + print(f"this is stages pred ,,,,,,,,,{stages_pred} : ") + combined_stages_pred.extend(stages_pred) + else: + print(f"Skipping segment starting at start index ({start_idx}) of data because of not enough data.") + time_interval = timestamps[end_idx + 1] - timestamps[end_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + + # Add placeholder for the long interval + #the place holder is [0,0,0,0] + time_interval = timestamps[end_idx + 1] - timestamps[end_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + start_idx = end_idx + 1 + + + + # Handle the last segment if needed + if start_idx < len(timestamps) - 1: + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:], heart_rates[start_idx:] + )) + if len(heartbeat_times_list) > min_treshhold: + rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=heartbeat_times_list + ) + stages_pred = stage(clf, rec, return_mode="prob") + combined_stages_pred.extend(stages_pred) + else: + print(f"Skipping the last segment because of not enough data.") + time_interval = timestamps[-1] - timestamps[start_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + + + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:], heart_rates[start_idx:] + )) + aggregated_heartbeat_times.extend(heartbeat_times_list) + + # Create a SleepRecord object with aggregated heartbeat times + final_rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=aggregated_heartbeat_times + ) + + # Convert to a NumPy array for easier modiy later if needed + combined_stages_pred = np.array(combined_stages_pred) + # for using in flask + stages_mode=clf.stages_mode + + return final_rec , combined_stages_pred , stages_mode , long_interval_indices + +final_rec , combined_stages_pred , stages_mode ,long_interval_indices = sleepanalyse(data) + + +# in case of ploting with matplotlib for local uses + +plot_hypnogram( + final_rec, + combined_stages_pred, + stages_mode=stages_mode +) +plt.show() From 8cc2a375be1880fd43ef64603f913e1e93c20b0c Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:12:09 +0330 Subject: [PATCH 07/10] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 49f8e2b5..56a72882 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ SleepECG provides tools for sleep stage classification when [EEG](https://en.wik - classifying sleep stages (which includes preprocessing, feature extraction, and classification). ## HeartRate Data -Description: I have introduced Changes to the SleepECG package, primarily focusing on enabling sleep stage classification using heart rate data when EEG signals are not available. Key features of this update include: Heart Rate to R-Interval Conversion: From d146a32e882dc93062b2c045dac4c00ed30374df Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Sun, 17 Dec 2023 12:00:56 +0330 Subject: [PATCH 08/10] Update README.md --- README.md | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/README.md b/README.md index 56a72882..e0b5e34c 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,6 @@ SleepECG provides tools for sleep stage classification when [EEG](https://en.wik - detecting heartbeats from ECG signals, and - classifying sleep stages (which includes preprocessing, feature extraction, and classification). -## HeartRate Data -I have introduced Changes to the SleepECG package, primarily focusing on enabling sleep stage classification using heart rate data when EEG signals are not available. Key features of this update include: - -Heart Rate to R-Interval Conversion: - -Implemented an algorithm to convert heart rate data into R-intervals. This crucial development allows the use of heart rate data, which is more readily available in many scenarios, for sleep stage classification. -Handling Data Gaps: - -Added robustness to the system by integrating code that effectively handles scenarios where data might be missing for extended periods. This ensures more reliable performance and resilience in real-world applications. -Web-Based and Local Application: - -Originally developed for a web-based project, these enhancements are equally applicable for local deployments, thereby broadening the usability scope of the SleepECG package. ### Documentation Documentation for SleepECG is available on [Read the Docs](https://sleepecg.readthedocs.io/en/stable/index.html). Check out the [changelog](https://github.com/cbrnr/sleepecg/blob/main/CHANGELOG.md) to learn what we added, changed, or fixed. @@ -68,43 +56,7 @@ from sleepecg import detect_heartbeats, get_toy_ecg ecg, fs = get_toy_ecg() # 5 min of ECG data at 360 Hz beats = detect_heartbeats(ecg, fs) # indices of detected heartbeats ``` -### Example if Heart Rate data is available - -The following example stages sleep with heart rate data : - -``` -data = [ - {'TimeStamp': '1690409900', 'HeartRate': '55'}, - {'TimeStamp': '1690410000', 'HeartRate': '60'}, - {'TimeStamp': '1690410120', 'HeartRate': '65'}, - {'TimeStamp': '1690410300', 'HeartRate': '62'}, - {'TimeStamp': '1690410600', 'HeartRate': '59'}, - {'TimeStamp': '1690410900', 'HeartRate': '58'}, - {'TimeStamp': '1690411200', 'HeartRate': '61'}, - {'TimeStamp': '1690411320', 'HeartRate': '56'}, - {'TimeStamp': '1690411380', 'HeartRate': '52'}, - {'TimeStamp': '1690411440', 'HeartRate': '50'}, - {'TimeStamp': '1690411500', 'HeartRate': '56'}, - {'TimeStamp': '1690411560', 'HeartRate': '58'}, - {'TimeStamp': '1690411620', 'HeartRate': '60'}, - {'TimeStamp': '1690411680', 'HeartRate': '65'}, - {'TimeStamp': '1690412740', 'HeartRate': '60'}, - {'TimeStamp': '1690412800', 'HeartRate': '62'}, - {'TimeStamp': '1690413800', 'HeartRate': '62'}, - {'TimeStamp': '1690514000', 'HeartRate': '62'} -] -final_rec , combined_stages_pred , stages_mode ,long_interval_indices = sleepanalyse(data) -plot_hypnogram( - final_rec, - combined_stages_pred, - stages_mode=stages_mode ) -plt.show() -``` -
- output_figure -
-the code for sleepanalyse function is in HeartRateData.py in the example folder . ### Dependencies SleepECG requires Python ≥ 3.9 and the following packages: From c39b261363a4219ee0eac952939a7fe336395f31 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Sun, 17 Dec 2023 12:03:55 +0330 Subject: [PATCH 09/10] Delete examples/HeartRateData.py --- examples/HeartRateData.py | 149 -------------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 examples/HeartRateData.py diff --git a/examples/HeartRateData.py b/examples/HeartRateData.py deleted file mode 100644 index 012f0210..00000000 --- a/examples/HeartRateData.py +++ /dev/null @@ -1,149 +0,0 @@ -import random -import numpy as np -from sleepecg import load_classifier, stage , SleepRecord , extract_features , plot_hypnogram -# importing matplotlib for local uses -import matplotlib.pyplot as plt -import random -data = [ - - {'TimeStamp': '1690409900', 'HeartRate': '55'}, - {'TimeStamp': '1690410000', 'HeartRate': '60'}, - {'TimeStamp': '1690410120', 'HeartRate': '65'}, - {'TimeStamp': '1690410300', 'HeartRate': '62'}, - {'TimeStamp': '1690410600', 'HeartRate': '59'}, - {'TimeStamp': '1690410900', 'HeartRate': '58'}, - {'TimeStamp': '1690411200', 'HeartRate': '61'}, - {'TimeStamp': '1690411320', 'HeartRate': '56'}, - {'TimeStamp': '1690411380', 'HeartRate': '52'}, - {'TimeStamp': '1690411440', 'HeartRate': '50'}, - {'TimeStamp': '1690411500', 'HeartRate': '56'}, - {'TimeStamp': '1690411560', 'HeartRate': '58'}, - {'TimeStamp': '1690411620', 'HeartRate': '60'}, - {'TimeStamp': '1690411680', 'HeartRate': '65'}, - {'TimeStamp': '1690412740', 'HeartRate': '60'}, - {'TimeStamp': '1690412800', 'HeartRate': '62'}, - {'TimeStamp': '1690413800', 'HeartRate': '62'}, - {'TimeStamp': '1690514000', 'HeartRate': '62'} -] - -clf = load_classifier("wrn-gru-mesa-weighted", "SleepECG") -def sleepanalyse(data , min_treshhold = 1500 , sleep_stage_duration = 60) : - # Convert to NumPy arrays - timestamps = [int(d['TimeStamp']) for d in data] - heart_rates = [int(d['HeartRate']) for d in data] - - - # Define a function to generate R-peak times on-the-fly to reduce cpu usage - # this function generates r peaks based on timeinterval between two timestamp and heart rate - - def generate_synthetic_r_peaks(timestamps, heart_rates): - peak_count = 0 - current_time = 0 # in unix seconds, relative to the start of recording - max_peaks = 0 - for i in range(len(timestamps) - 1): - time_interval = timestamps[i + 1] - timestamps[i] - n_peaks = int(time_interval / (60.0 / heart_rates[i])) - max_peaks += n_peaks - - for j in range(n_peaks): - if peak_count >= max_peaks: - return - r_peak_interval = (60.0 / heart_rates[i]) + random.uniform(0.001, 0.01) # Add a small random - current_time += r_peak_interval - yield current_time # Yield the R-peak time as needed - peak_count += 1 - - - - # Identify long intervals - long_interval_indices = [] - for i in range(len(timestamps) - 1): - time_interval = timestamps[i + 1] - timestamps[i] - if time_interval > 1800: # 30 minutes - long_interval_indices.append(i) - - - - combined_stages_pred = [] - aggregated_heartbeat_times = [] - start_idx = 0 - for end_idx in long_interval_indices: - heartbeat_times_list = list(generate_synthetic_r_peaks( - timestamps[start_idx:end_idx + 1], heart_rates[start_idx:end_idx + 1] - )) - aggregated_heartbeat_times.extend(heartbeat_times_list) - # Check if heartbeat_times_list has enough elements - if len(heartbeat_times_list) > min_treshhold: - rec = SleepRecord( - sleep_stage_duration=sleep_stage_duration, - heartbeat_times=heartbeat_times_list - ) - stages_pred = stage(clf, rec, return_mode="prob") - print(f"this is stages pred ,,,,,,,,,{stages_pred} : ") - combined_stages_pred.extend(stages_pred) - else: - print(f"Skipping segment starting at start index ({start_idx}) of data because of not enough data.") - time_interval = timestamps[end_idx + 1] - timestamps[end_idx] - num_placeholder_blocks = int(time_interval / sleep_stage_duration) - placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) - combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) - - # Add placeholder for the long interval - #the place holder is [0,0,0,0] - time_interval = timestamps[end_idx + 1] - timestamps[end_idx] - num_placeholder_blocks = int(time_interval / sleep_stage_duration) - placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) - combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) - start_idx = end_idx + 1 - - - - # Handle the last segment if needed - if start_idx < len(timestamps) - 1: - heartbeat_times_list = list(generate_synthetic_r_peaks( - timestamps[start_idx:], heart_rates[start_idx:] - )) - if len(heartbeat_times_list) > min_treshhold: - rec = SleepRecord( - sleep_stage_duration=sleep_stage_duration, - heartbeat_times=heartbeat_times_list - ) - stages_pred = stage(clf, rec, return_mode="prob") - combined_stages_pred.extend(stages_pred) - else: - print(f"Skipping the last segment because of not enough data.") - time_interval = timestamps[-1] - timestamps[start_idx] - num_placeholder_blocks = int(time_interval / sleep_stage_duration) - placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) - combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) - - - heartbeat_times_list = list(generate_synthetic_r_peaks( - timestamps[start_idx:], heart_rates[start_idx:] - )) - aggregated_heartbeat_times.extend(heartbeat_times_list) - - # Create a SleepRecord object with aggregated heartbeat times - final_rec = SleepRecord( - sleep_stage_duration=sleep_stage_duration, - heartbeat_times=aggregated_heartbeat_times - ) - - # Convert to a NumPy array for easier modiy later if needed - combined_stages_pred = np.array(combined_stages_pred) - # for using in flask - stages_mode=clf.stages_mode - - return final_rec , combined_stages_pred , stages_mode , long_interval_indices - -final_rec , combined_stages_pred , stages_mode ,long_interval_indices = sleepanalyse(data) - - -# in case of ploting with matplotlib for local uses - -plot_hypnogram( - final_rec, - combined_stages_pred, - stages_mode=stages_mode -) -plt.show() From 7010d8a02395731d16314637fc00eb9f2ba06b18 Mon Sep 17 00:00:00 2001 From: Mashriza <133785714+MashRiza@users.noreply.github.com> Date: Sun, 17 Dec 2023 16:05:29 +0330 Subject: [PATCH 10/10] Adding HeartRateSleep function to feature_extraction.py --- sleepecg/feature_extraction.py | 124 +++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/sleepecg/feature_extraction.py b/sleepecg/feature_extraction.py index e15671b3..d2b70c5e 100644 --- a/sleepecg/feature_extraction.py +++ b/sleepecg/feature_extraction.py @@ -10,6 +10,8 @@ from typing import Iterable, Optional import numpy as np +import pandas as pd +import random from numpy.lib.stride_tricks import sliding_window_view from scipy.interpolate import interp1d from scipy.signal import periodogram @@ -776,3 +778,125 @@ def extract_features( stages = [y for _, y in Xy] return features, stages, feature_ids + + +def HeartRateSleep (data , min_treshhold = 1500 , sleep_stage_duration = 60 , long_interval = 3600) : + ''' + + this function will get pandas dataframe of HeartRate and Timestamp + will retrun "final_rec" , "combined_stages_pred" , "stages_mode" , "long_interval_indices" + + ''' + # loading classifier + clf = load_classifier("wrn-gru-mesa-weighted", "SleepECG") + # converting to numeric values if needed + df['TimeStamp'] = pd.to_numeric(df['TimeStamp']) + df['HeartRate'] = pd.to_numeric(df['HeartRate']) + + # Convert to list + timestamps = df['TimeStamp'].to_list() + heart_rates = df['HeartRate'].to_list() + + + # Define a function to generate R-peak times on-the-fly to reduce cpu usage + # this function generates r peaks based on timeinterval between two timestamp and heart rate + + def generate_synthetic_r_peaks(timestamps, heart_rates): + peak_count = 0 + current_time = 0 # in unix seconds, relative to the start of recording + max_peaks = 0 + for i in range(len(timestamps) - 1): + time_interval = timestamps[i + 1] - timestamps[i] + n_peaks = int(time_interval / (60.0 / heart_rates[i])) + max_peaks += n_peaks + + for j in range(n_peaks): + if peak_count >= max_peaks: + return + r_peak_interval = (60.0 / heart_rates[i]) + random.uniform(0.001, 0.01) # Add a small random + current_time += r_peak_interval + yield current_time # Yield the R-peak time as needed + peak_count += 1 + + + + # Identify long intervals + long_interval_indices = [] + for i in range(len(timestamps) - 1): + time_interval = timestamps[i + 1] - timestamps[i] + if time_interval > long_interval: # 30 minutes by defult + long_interval_indices.append(i) + + + + combined_stages_pred = [] + aggregated_heartbeat_times = [] + start_idx = 0 + for end_idx in long_interval_indices: + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:end_idx + 1], heart_rates[start_idx:end_idx + 1] + )) + aggregated_heartbeat_times.extend(heartbeat_times_list) + # Check if heartbeat_times_list has enough elements + if len(heartbeat_times_list) > min_treshhold: + rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=heartbeat_times_list + ) + stages_pred = stage(clf, rec, return_mode="prob") + print(f"this is stages pred ,,,,,,,,,{stages_pred} : ") + combined_stages_pred.extend(stages_pred) + else: + print(f"Skipping segment starting at start index ({start_idx}) of data because of not enough data.") + time_interval = timestamps[end_idx + 1] - timestamps[end_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + + # Add placeholder for the long interval + #the place holder is [0,0,0,0] + time_interval = timestamps[end_idx + 1] - timestamps[end_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + start_idx = end_idx + 1 + + + + # Handle the last segment if needed + if start_idx < len(timestamps) - 1: + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:], heart_rates[start_idx:] + )) + if len(heartbeat_times_list) > min_treshhold: + rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=heartbeat_times_list + ) + stages_pred = stage(clf, rec, return_mode="prob") + combined_stages_pred.extend(stages_pred) + else: + print(f"Skipping the last segment because of not enough data.") + time_interval = timestamps[-1] - timestamps[start_idx] + num_placeholder_blocks = int(time_interval / sleep_stage_duration) + placeholder_array = np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32) + combined_stages_pred.extend([placeholder_array] * num_placeholder_blocks) + + + heartbeat_times_list = list(generate_synthetic_r_peaks( + timestamps[start_idx:], heart_rates[start_idx:] + )) + aggregated_heartbeat_times.extend(heartbeat_times_list) + + # Create a SleepRecord object with aggregated heartbeat times + final_rec = SleepRecord( + sleep_stage_duration=sleep_stage_duration, + heartbeat_times=aggregated_heartbeat_times + ) + + # Convert to a NumPy array for easier modiy later if needed + combined_stages_pred = np.array(combined_stages_pred) + stages_mode=clf.stages_mode + + return final_rec , combined_stages_pred , stages_mode , long_interval_indices +