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)
-
+```
+
+
+
### 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()
+
### 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()
+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()
-```
-
-
-
-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
+