glucose360 0.0.1__py3-none-any.whl → 0.0.2__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.
- glucose360/__init__.py +5 -0
- glucose360/config.ini +10 -0
- glucose360/event_colors.json +47 -0
- glucose360/events.py +45 -43
- glucose360/features.py +314 -24
- glucose360/main.py +13 -0
- glucose360/plots.py +537 -493
- glucose360/preprocessing.py +594 -557
- glucose360-0.0.2.dist-info/METADATA +177 -0
- glucose360-0.0.2.dist-info/RECORD +13 -0
- {glucose360-0.0.1.dist-info → glucose360-0.0.2.dist-info}/WHEEL +1 -1
- glucose360-0.0.2.dist-info/licenses/LICENSE +339 -0
- glucose360-0.0.1.dist-info/LICENSE +0 -674
- glucose360-0.0.1.dist-info/METADATA +0 -34
- glucose360-0.0.1.dist-info/RECORD +0 -10
- {glucose360-0.0.1.dist-info → glucose360-0.0.2.dist-info}/top_level.txt +0 -0
glucose360/__init__.py
CHANGED
glucose360/config.ini
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
{
|
2
|
+
"gold": "#ffd700",
|
3
|
+
"red": "#ff0000",
|
4
|
+
"limegreen": "#32cd32",
|
5
|
+
"hotpink": "#ff69b4",
|
6
|
+
"dodgerblue": "#1e90ff",
|
7
|
+
"darkolivegreen": "#556b2f",
|
8
|
+
"sienna": "#a0522d",
|
9
|
+
"seagreen": "#2e8b57",
|
10
|
+
"maroon2": "#7f0000",
|
11
|
+
"darkgreen": "#006400",
|
12
|
+
"slategray": "#708090",
|
13
|
+
"olive": "#808000",
|
14
|
+
"rosybrown": "#bc8f8f",
|
15
|
+
"peru": "#cd853f",
|
16
|
+
"yellowgreen": "#9acd32",
|
17
|
+
"lightseagreen": "#20b2aa",
|
18
|
+
"indianred": "#cd5c5c",
|
19
|
+
"goldenrod": "#daa520",
|
20
|
+
"darkseagreen": "#8fbc8f",
|
21
|
+
"purple": "#800080",
|
22
|
+
"darkorchid": "#9932cc",
|
23
|
+
"darkorange": "#ff8c00",
|
24
|
+
"slateblue": "#6a5acd",
|
25
|
+
"lime": "#00ff00",
|
26
|
+
"mediumspringgreen": "#00fa9a",
|
27
|
+
"darksalmon": "#e9967a",
|
28
|
+
"crimson": "#dc143c",
|
29
|
+
"aqua": "#00ffff",
|
30
|
+
"deepskyblue": "#00bfff",
|
31
|
+
"purple3": "#a020f0",
|
32
|
+
"greenyellow": "#adff2f",
|
33
|
+
"tomato": "#ff6347",
|
34
|
+
"thistle": "#d8bfd8",
|
35
|
+
"fuchsia": "#ff00ff",
|
36
|
+
"palevioletred": "#db7093",
|
37
|
+
"khaki": "#f0e68c",
|
38
|
+
"laserlemon": "#ffff54",
|
39
|
+
"plum": "#dda0dd",
|
40
|
+
"lightgreen": "#90ee90",
|
41
|
+
"deeppink": "#ff1493",
|
42
|
+
"paleturquoise": "#afeeee",
|
43
|
+
"violet": "#ee82ee",
|
44
|
+
"aquamarine": "#7fffd4",
|
45
|
+
"peachpuff": "#ffdab9",
|
46
|
+
"darkslategray": "#2f4f4f"
|
47
|
+
}
|
glucose360/events.py
CHANGED
@@ -4,11 +4,12 @@ from scipy.integrate import trapezoid
|
|
4
4
|
import configparser
|
5
5
|
import glob, os, zipfile, tempfile
|
6
6
|
import math
|
7
|
+
from importlib import resources
|
8
|
+
from glucose360.preprocessing import load_config
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
config.read(config_path)
|
10
|
+
# Initialize config at module level
|
11
|
+
config = load_config()
|
12
|
+
INTERVAL = int(config["variables"]["interval"])
|
12
13
|
ID = config['variables']['id']
|
13
14
|
GLUCOSE = config['variables']['glucose']
|
14
15
|
TIME = config['variables']['time']
|
@@ -23,8 +24,8 @@ def import_events(
|
|
23
24
|
name: str = None,
|
24
25
|
day_col: str = "Day",
|
25
26
|
time_col: str = "Time",
|
26
|
-
before: int =
|
27
|
-
after: int =
|
27
|
+
before: int = 30,
|
28
|
+
after: int = 180,
|
28
29
|
type: str = "imported event"
|
29
30
|
) -> pd.DataFrame:
|
30
31
|
"""Bulk imports events from standalone .csv files or from those within a given directory or .zip file
|
@@ -37,9 +38,9 @@ def import_events(
|
|
37
38
|
:type day_col: str, optional
|
38
39
|
:param time_col: the name of the column specifying what time during the day the event occurred, defaults to 'Time'
|
39
40
|
:type time_col: str, optional
|
40
|
-
:param before: the amount of minutes to also look at before the event timestamp, defaults to
|
41
|
+
:param before: the amount of minutes to also look at before the event timestamp, defaults to 30
|
41
42
|
:type before: int, optional
|
42
|
-
:param after: the amount of minutes to also look at after the event timestamp, defaults to
|
43
|
+
:param after: the amount of minutes to also look at after the event timestamp, defaults to 180
|
43
44
|
:type after: int, optional
|
44
45
|
:param type: the type of event to classify all the imported events as, defaults to 'imported event'
|
45
46
|
:type type: str, optional
|
@@ -79,8 +80,8 @@ def import_events_directory(
|
|
79
80
|
id: str,
|
80
81
|
day_col: str = "Day",
|
81
82
|
time_col: str = "Time",
|
82
|
-
before: int =
|
83
|
-
after: int =
|
83
|
+
before: int = 30,
|
84
|
+
after: int = 180,
|
84
85
|
type: str = "imported event"
|
85
86
|
) -> pd.DataFrame:
|
86
87
|
"""Bulk imports events from .csv files within a given directory
|
@@ -93,9 +94,9 @@ def import_events_directory(
|
|
93
94
|
:type day_col: str, optional
|
94
95
|
:param time_col: the name of the column specifying what time during the day the event occurred, defaults to 'Time'
|
95
96
|
:type time_col: str, optional
|
96
|
-
:param before: the amount of minutes to also look at before the event timestamp, defaults to
|
97
|
+
:param before: the amount of minutes to also look at before the event timestamp, defaults to 30
|
97
98
|
:type before: int, optional
|
98
|
-
:param after: the amount of minutes to also look at after the event timestamp, defaults to
|
99
|
+
:param after: the amount of minutes to also look at after the event timestamp, defaults to 180
|
99
100
|
:type after: int, optional
|
100
101
|
:param type: the type of event to classify all the imported events as, defaults to 'imported event'
|
101
102
|
:type type: str, optional
|
@@ -114,8 +115,8 @@ def import_events_csv(
|
|
114
115
|
id: str,
|
115
116
|
day_col: str = "Day",
|
116
117
|
time_col: str = "Time",
|
117
|
-
before: int =
|
118
|
-
after: int =
|
118
|
+
before: int = 30,
|
119
|
+
after: int = 180,
|
119
120
|
type: str = "imported event"
|
120
121
|
) -> pd.DataFrame:
|
121
122
|
"""Bulk imports events from a single .csv file
|
@@ -128,9 +129,9 @@ def import_events_csv(
|
|
128
129
|
:type day_col: str, optional
|
129
130
|
:param time_col: the name of the column specifying what time during the day the event occurred, defaults to 'Time'
|
130
131
|
:type time_col: str, optional
|
131
|
-
:param before: the amount of minutes to also look at before the event timestamp, defaults to
|
132
|
+
:param before: the amount of minutes to also look at before the event timestamp, defaults to 30
|
132
133
|
:type before: int, optional
|
133
|
-
:param after: the amount of minutes to also look at after the event timestamp, defaults to
|
134
|
+
:param after: the amount of minutes to also look at after the event timestamp, defaults to 180
|
134
135
|
:type after: int, optional
|
135
136
|
:param type: the type of event to classify all the imported events as, defaults to 'imported event'
|
136
137
|
:type type: str, optional
|
@@ -263,10 +264,11 @@ def get_episodes(
|
|
263
264
|
_episodes_helper(data, id, "hypo", hypo_lvl1, 1, min_length, end_length),
|
264
265
|
_episodes_helper(data, id, "hypo", hypo_lvl2, 2, min_length, end_length)])
|
265
266
|
|
266
|
-
episodes.
|
267
|
+
if not episodes.empty:
|
268
|
+
episodes = episodes.sort_values(by=[TIME])
|
267
269
|
output = pd.concat([output, episodes])
|
268
270
|
|
269
|
-
return output
|
271
|
+
return output
|
270
272
|
|
271
273
|
def get_excursions(
|
272
274
|
df: pd.DataFrame,
|
@@ -388,7 +390,7 @@ def retrieve_event_data(
|
|
388
390
|
final = datetime + pd.Timedelta(row[AFTER], "m")
|
389
391
|
|
390
392
|
patient_data = df.loc[id]
|
391
|
-
data = patient_data[(patient_data[TIME] >= initial) & (patient_data[TIME] <= final)].copy()
|
393
|
+
data = patient_data[(patient_data[TIME] >= initial) & (patient_data[TIME] <= final)].copy().reset_index(drop=True)
|
392
394
|
|
393
395
|
data[ID] = id
|
394
396
|
data[DESCRIPTION] = row[DESCRIPTION]
|
@@ -562,7 +564,7 @@ def event_metrics(
|
|
562
564
|
final = datetime + pd.Timedelta(event[AFTER], "m")
|
563
565
|
|
564
566
|
patient_data = df.loc[id]
|
565
|
-
data = patient_data[(patient_data[TIME] >= initial) & (patient_data[TIME] <= final)].copy()
|
567
|
+
data = patient_data[(patient_data[TIME] >= initial) & (patient_data[TIME] <= final)].copy().reset_index(drop=True)
|
566
568
|
|
567
569
|
metrics = pd.Series()
|
568
570
|
metrics["Baseline"] = baseline(data)
|
@@ -610,8 +612,8 @@ def create_event_features(
|
|
610
612
|
event_features = {}
|
611
613
|
for id in df.index.unique():
|
612
614
|
sub_features = {}
|
613
|
-
for
|
614
|
-
sub_features.update(create_event_features_helper(df.loc[id], sub_events,
|
615
|
+
for event_type, sub_events in events[events[ID] == id].groupby(TYPE):
|
616
|
+
sub_features.update(create_event_features_helper(df.loc[id], sub_events, event_type))
|
615
617
|
event_features[id] = sub_features
|
616
618
|
|
617
619
|
return pd.DataFrame(event_features).T
|
@@ -619,7 +621,7 @@ def create_event_features(
|
|
619
621
|
def create_event_features_helper(
|
620
622
|
df: pd.DataFrame,
|
621
623
|
sub_events: pd.DataFrame,
|
622
|
-
|
624
|
+
event_type: str,
|
623
625
|
) -> dict[str, float]:
|
624
626
|
"""Calculates aggregate event-based metrics for a single patient and type of event. Helper method for 'create_event_features()'.
|
625
627
|
|
@@ -627,32 +629,32 @@ def create_event_features_helper(
|
|
627
629
|
:type df: 'pandas.DataFrame'
|
628
630
|
:param sub_events: Pandas DataFrame containing events of only one type solely for the patient whose CGM trace is also given
|
629
631
|
:type sub_events: 'pandas.DataFrame'
|
630
|
-
:param
|
631
|
-
:type
|
632
|
+
:param event_type: the type of event that 'sub_events' contains
|
633
|
+
:type event_type: str
|
632
634
|
:return: a dictionary with str-type keys that refer to the name of the calculated features and float-type values
|
633
635
|
:rtype: dict[str, float]
|
634
636
|
"""
|
635
637
|
|
636
638
|
features = {
|
637
|
-
f"Mean {
|
638
|
-
f"Mean Glucose During {
|
639
|
-
f"Mean Upwards Slope of {
|
640
|
-
f"Mean Downwards Slope of {
|
641
|
-
f"Mean Minimum Glucose of {
|
642
|
-
f"Mean Maximum Glucose of {
|
643
|
-
f"Mean Amplitude of {
|
644
|
-
f"Mean iAUC of {
|
639
|
+
f"Mean {event_type} Duration": [],
|
640
|
+
f"Mean Glucose During {event_type}s": [],
|
641
|
+
f"Mean Upwards Slope of {event_type}s (mg/dL per min)": [],
|
642
|
+
f"Mean Downwards Slope of {event_type}s (mg/dL per min)": [],
|
643
|
+
f"Mean Minimum Glucose of {event_type}s": [],
|
644
|
+
f"Mean Maximum Glucose of {event_type}s": [],
|
645
|
+
f"Mean Amplitude of {event_type}s": [],
|
646
|
+
f"Mean iAUC of {event_type}s": []
|
645
647
|
}
|
646
648
|
|
647
649
|
for _, event in sub_events.iterrows():
|
648
650
|
event_data = retrieve_event_data(df, event)
|
649
651
|
|
650
|
-
duration = event[AFTER]
|
651
|
-
features[f"Mean {
|
652
|
+
duration = event[AFTER] + event[BEFORE]
|
653
|
+
features[f"Mean {event_type} Duration"].append(duration)
|
652
654
|
|
653
|
-
features[f"Mean Glucose During {
|
654
|
-
features[f"Mean Minimum Glucose of {
|
655
|
-
features[f"Mean Maximum Glucose of {
|
655
|
+
features[f"Mean Glucose During {event_type}s"].append(event_data[GLUCOSE].mean())
|
656
|
+
features[f"Mean Minimum Glucose of {event_type}s"].append(nadir(event_data))
|
657
|
+
features[f"Mean Maximum Glucose of {event_type}s"].append(peak(event_data))
|
656
658
|
|
657
659
|
event_time = event[TIME]
|
658
660
|
closest_idx = (event_data[TIME] - event_time).abs().idxmin()
|
@@ -661,20 +663,20 @@ def create_event_features_helper(
|
|
661
663
|
peak_glucose = peak(event_data)
|
662
664
|
peak_time = event_data.loc[event_data[GLUCOSE].idxmax(), TIME]
|
663
665
|
amplitude = peak_glucose - event_glucose
|
664
|
-
features[f"Mean Amplitude of {
|
666
|
+
features[f"Mean Amplitude of {event_type}s"].append(abs(amplitude))
|
665
667
|
|
666
668
|
time_diff_to_peak = (peak_time - event_time).total_seconds() / 60.0
|
667
669
|
slope_to_peak = (peak_glucose - event_glucose) / time_diff_to_peak if time_diff_to_peak != 0 else np.nan
|
668
|
-
features[f"Mean Upwards Slope of {
|
670
|
+
features[f"Mean Upwards Slope of {event_type}s (mg/dL per min)"].append(slope_to_peak)
|
669
671
|
|
670
672
|
end_time = event_data[TIME].iloc[-1]
|
671
673
|
end_glucose = event_data[GLUCOSE].iloc[-1]
|
672
674
|
time_diff_peak_to_end = (end_time - peak_time).total_seconds() / 60.0
|
673
675
|
slope_peak_to_end = (end_glucose - peak_glucose) / time_diff_peak_to_end if time_diff_peak_to_end != 0 else np.nan
|
674
|
-
features[f"Mean Downwards Slope of {
|
676
|
+
features[f"Mean Downwards Slope of {event_type}s (mg/dL per min)"].append(slope_peak_to_end)
|
675
677
|
|
676
|
-
features[f"Mean iAUC of {
|
678
|
+
features[f"Mean iAUC of {event_type}s"].append(iAUC(event_data, event_glucose))
|
677
679
|
|
678
680
|
features = {k: np.mean(v) for k, v in features.items()}
|
679
|
-
features[f"Mean # of {
|
681
|
+
features[f"Mean # of {event_type}s per day"] = sub_events.shape[0] / len(df[TIME].dt.date.unique())
|
680
682
|
return features
|