iints-sdk-python35 0.0.18__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.
- iints/__init__.py +183 -0
- iints/analysis/__init__.py +12 -0
- iints/analysis/algorithm_xray.py +387 -0
- iints/analysis/baseline.py +92 -0
- iints/analysis/clinical_benchmark.py +198 -0
- iints/analysis/clinical_metrics.py +551 -0
- iints/analysis/clinical_tir_analyzer.py +136 -0
- iints/analysis/diabetes_metrics.py +43 -0
- iints/analysis/edge_efficiency.py +33 -0
- iints/analysis/edge_performance_monitor.py +315 -0
- iints/analysis/explainability.py +94 -0
- iints/analysis/explainable_ai.py +232 -0
- iints/analysis/hardware_benchmark.py +221 -0
- iints/analysis/metrics.py +117 -0
- iints/analysis/population_report.py +188 -0
- iints/analysis/reporting.py +345 -0
- iints/analysis/safety_index.py +311 -0
- iints/analysis/sensor_filtering.py +54 -0
- iints/analysis/validator.py +273 -0
- iints/api/__init__.py +0 -0
- iints/api/base_algorithm.py +307 -0
- iints/api/registry.py +103 -0
- iints/api/template_algorithm.py +195 -0
- iints/assets/iints_logo.png +0 -0
- iints/cli/__init__.py +0 -0
- iints/cli/cli.py +2598 -0
- iints/core/__init__.py +1 -0
- iints/core/algorithms/__init__.py +0 -0
- iints/core/algorithms/battle_runner.py +138 -0
- iints/core/algorithms/correction_bolus.py +95 -0
- iints/core/algorithms/discovery.py +92 -0
- iints/core/algorithms/fixed_basal_bolus.py +58 -0
- iints/core/algorithms/hybrid_algorithm.py +92 -0
- iints/core/algorithms/lstm_algorithm.py +138 -0
- iints/core/algorithms/mock_algorithms.py +162 -0
- iints/core/algorithms/pid_controller.py +88 -0
- iints/core/algorithms/standard_pump_algo.py +64 -0
- iints/core/device.py +0 -0
- iints/core/device_manager.py +64 -0
- iints/core/devices/__init__.py +3 -0
- iints/core/devices/models.py +160 -0
- iints/core/patient/__init__.py +9 -0
- iints/core/patient/bergman_model.py +341 -0
- iints/core/patient/models.py +285 -0
- iints/core/patient/patient_factory.py +117 -0
- iints/core/patient/profile.py +41 -0
- iints/core/safety/__init__.py +12 -0
- iints/core/safety/config.py +37 -0
- iints/core/safety/input_validator.py +95 -0
- iints/core/safety/supervisor.py +39 -0
- iints/core/simulation/__init__.py +0 -0
- iints/core/simulation/scenario_parser.py +61 -0
- iints/core/simulator.py +874 -0
- iints/core/supervisor.py +367 -0
- iints/data/__init__.py +53 -0
- iints/data/adapter.py +142 -0
- iints/data/column_mapper.py +398 -0
- iints/data/datasets.json +132 -0
- iints/data/demo/__init__.py +1 -0
- iints/data/demo/demo_cgm.csv +289 -0
- iints/data/importer.py +275 -0
- iints/data/ingestor.py +162 -0
- iints/data/nightscout.py +128 -0
- iints/data/quality_checker.py +550 -0
- iints/data/registry.py +166 -0
- iints/data/tidepool.py +38 -0
- iints/data/universal_parser.py +813 -0
- iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
- iints/data/virtual_patients/default_patient.yaml +11 -0
- iints/data/virtual_patients/patient_559_config.yaml +11 -0
- iints/emulation/__init__.py +80 -0
- iints/emulation/legacy_base.py +414 -0
- iints/emulation/medtronic_780g.py +337 -0
- iints/emulation/omnipod_5.py +367 -0
- iints/emulation/tandem_controliq.py +393 -0
- iints/highlevel.py +451 -0
- iints/learning/__init__.py +3 -0
- iints/learning/autonomous_optimizer.py +194 -0
- iints/learning/learning_system.py +122 -0
- iints/metrics.py +34 -0
- iints/population/__init__.py +11 -0
- iints/population/generator.py +131 -0
- iints/population/runner.py +327 -0
- iints/presets/__init__.py +28 -0
- iints/presets/presets.json +114 -0
- iints/research/__init__.py +30 -0
- iints/research/config.py +68 -0
- iints/research/dataset.py +319 -0
- iints/research/losses.py +73 -0
- iints/research/predictor.py +329 -0
- iints/scenarios/__init__.py +3 -0
- iints/scenarios/generator.py +92 -0
- iints/templates/__init__.py +0 -0
- iints/templates/default_algorithm.py +91 -0
- iints/templates/scenarios/__init__.py +0 -0
- iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
- iints/templates/scenarios/chaos_runaway_ai.json +25 -0
- iints/templates/scenarios/example_scenario.json +35 -0
- iints/templates/scenarios/exercise_stress.json +30 -0
- iints/utils/__init__.py +3 -0
- iints/utils/plotting.py +50 -0
- iints/utils/run_io.py +152 -0
- iints/validation/__init__.py +133 -0
- iints/validation/schemas.py +94 -0
- iints/visualization/__init__.py +34 -0
- iints/visualization/cockpit.py +691 -0
- iints/visualization/uncertainty_cloud.py +612 -0
- iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
- iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
- iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
- iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
- iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
- iints_sdk_python35-0.0.18.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
timestamp,glucose,carbs,insulin
|
|
2
|
+
0,115.0,0.0,0.0
|
|
3
|
+
5,115.2,0.0,0.0
|
|
4
|
+
10,115.3,0.0,0.0
|
|
5
|
+
15,115.5,0.0,0.0
|
|
6
|
+
20,115.7,0.0,0.0
|
|
7
|
+
25,115.9,0.0,0.0
|
|
8
|
+
30,116.0,0.0,0.0
|
|
9
|
+
35,116.2,0.0,0.0
|
|
10
|
+
40,116.4,0.0,0.0
|
|
11
|
+
45,116.6,0.0,0.0
|
|
12
|
+
50,116.7,0.0,0.0
|
|
13
|
+
55,116.9,0.0,0.0
|
|
14
|
+
60,144.1,45.0,0.0
|
|
15
|
+
65,143.5,0.0,0.0
|
|
16
|
+
70,142.9,0.0,0.0
|
|
17
|
+
75,142.4,0.0,0.0
|
|
18
|
+
80,141.9,0.0,0.0
|
|
19
|
+
85,141.4,0.0,0.0
|
|
20
|
+
90,140.9,0.0,0.0
|
|
21
|
+
95,140.5,0.0,0.0
|
|
22
|
+
100,140.0,0.0,0.0
|
|
23
|
+
105,139.6,0.0,0.0
|
|
24
|
+
110,139.1,0.0,0.0
|
|
25
|
+
115,138.7,0.0,0.0
|
|
26
|
+
120,138.3,0.0,0.0
|
|
27
|
+
125,138.0,0.0,0.0
|
|
28
|
+
130,137.6,0.0,0.0
|
|
29
|
+
135,137.2,0.0,0.0
|
|
30
|
+
140,136.9,0.0,0.0
|
|
31
|
+
145,136.6,0.0,0.0
|
|
32
|
+
150,136.2,0.0,0.0
|
|
33
|
+
155,135.9,0.0,0.0
|
|
34
|
+
160,135.6,0.0,0.0
|
|
35
|
+
165,135.3,0.0,0.0
|
|
36
|
+
170,135.1,0.0,0.0
|
|
37
|
+
175,134.8,0.0,0.0
|
|
38
|
+
180,134.5,0.0,0.0
|
|
39
|
+
185,134.3,0.0,0.0
|
|
40
|
+
190,134.0,0.0,0.0
|
|
41
|
+
195,133.8,0.0,0.0
|
|
42
|
+
200,133.5,0.0,0.0
|
|
43
|
+
205,133.3,0.0,0.0
|
|
44
|
+
210,133.1,0.0,0.0
|
|
45
|
+
215,132.9,0.0,0.0
|
|
46
|
+
220,132.7,0.0,0.0
|
|
47
|
+
225,132.4,0.0,0.0
|
|
48
|
+
230,132.2,0.0,0.0
|
|
49
|
+
235,132.1,0.0,0.0
|
|
50
|
+
240,131.9,0.0,0.0
|
|
51
|
+
245,131.7,0.0,0.0
|
|
52
|
+
250,131.5,0.0,0.0
|
|
53
|
+
255,131.3,0.0,0.0
|
|
54
|
+
260,131.1,0.0,0.0
|
|
55
|
+
265,131.0,0.0,0.0
|
|
56
|
+
270,130.8,0.0,0.0
|
|
57
|
+
275,130.6,0.0,0.0
|
|
58
|
+
280,130.5,0.0,0.0
|
|
59
|
+
285,130.3,0.0,0.0
|
|
60
|
+
290,130.2,0.0,0.0
|
|
61
|
+
295,130.0,0.0,0.0
|
|
62
|
+
300,129.8,0.0,0.0
|
|
63
|
+
305,129.7,0.0,0.0
|
|
64
|
+
310,129.5,0.0,0.0
|
|
65
|
+
315,129.4,0.0,0.0
|
|
66
|
+
320,129.2,0.0,0.0
|
|
67
|
+
325,129.1,0.0,0.0
|
|
68
|
+
330,129.0,0.0,0.0
|
|
69
|
+
335,128.8,0.0,0.0
|
|
70
|
+
340,128.7,0.0,0.0
|
|
71
|
+
345,128.5,0.0,0.0
|
|
72
|
+
350,128.4,0.0,0.0
|
|
73
|
+
355,128.2,0.0,0.0
|
|
74
|
+
360,164.1,60.0,0.0
|
|
75
|
+
365,163.0,0.0,0.0
|
|
76
|
+
370,161.9,0.0,0.0
|
|
77
|
+
375,160.8,0.0,0.0
|
|
78
|
+
380,159.7,0.0,0.0
|
|
79
|
+
385,158.7,0.0,0.0
|
|
80
|
+
390,157.7,0.0,0.0
|
|
81
|
+
395,156.7,0.0,0.0
|
|
82
|
+
400,155.8,0.0,0.0
|
|
83
|
+
405,154.9,0.0,0.0
|
|
84
|
+
410,153.9,0.0,0.0
|
|
85
|
+
415,153.0,0.0,0.0
|
|
86
|
+
420,152.2,0.0,0.0
|
|
87
|
+
425,151.3,0.0,0.0
|
|
88
|
+
430,150.5,0.0,0.0
|
|
89
|
+
435,149.7,0.0,0.0
|
|
90
|
+
440,148.9,0.0,0.0
|
|
91
|
+
445,148.1,0.0,0.0
|
|
92
|
+
450,147.3,0.0,0.0
|
|
93
|
+
455,146.6,0.0,0.0
|
|
94
|
+
460,145.8,0.0,0.0
|
|
95
|
+
465,145.1,0.0,0.0
|
|
96
|
+
470,144.4,0.0,0.0
|
|
97
|
+
475,143.7,0.0,0.0
|
|
98
|
+
480,143.0,0.0,0.0
|
|
99
|
+
485,142.4,0.0,0.0
|
|
100
|
+
490,141.7,0.0,0.0
|
|
101
|
+
495,141.1,0.0,0.0
|
|
102
|
+
500,140.4,0.0,0.0
|
|
103
|
+
505,139.8,0.0,0.0
|
|
104
|
+
510,139.2,0.0,0.0
|
|
105
|
+
515,138.6,0.0,0.0
|
|
106
|
+
520,138.0,0.0,0.0
|
|
107
|
+
525,137.4,0.0,0.0
|
|
108
|
+
530,136.9,0.0,0.0
|
|
109
|
+
535,136.3,0.0,0.0
|
|
110
|
+
540,135.8,0.0,0.0
|
|
111
|
+
545,135.2,0.0,0.0
|
|
112
|
+
550,134.7,0.0,0.0
|
|
113
|
+
555,134.2,0.0,0.0
|
|
114
|
+
560,133.7,0.0,0.0
|
|
115
|
+
565,133.2,0.0,0.0
|
|
116
|
+
570,132.7,0.0,0.0
|
|
117
|
+
575,132.2,0.0,0.0
|
|
118
|
+
580,131.7,0.0,0.0
|
|
119
|
+
585,131.2,0.0,0.0
|
|
120
|
+
590,130.8,0.0,0.0
|
|
121
|
+
595,130.3,0.0,0.0
|
|
122
|
+
600,129.8,0.0,0.0
|
|
123
|
+
605,129.4,0.0,0.0
|
|
124
|
+
610,128.9,0.0,0.0
|
|
125
|
+
615,128.5,0.0,0.0
|
|
126
|
+
620,128.1,0.0,0.0
|
|
127
|
+
625,127.7,0.0,0.0
|
|
128
|
+
630,127.2,0.0,0.0
|
|
129
|
+
635,126.8,0.0,0.0
|
|
130
|
+
640,126.4,0.0,0.0
|
|
131
|
+
645,126.0,0.0,0.0
|
|
132
|
+
650,125.6,0.0,0.0
|
|
133
|
+
655,125.2,0.0,0.0
|
|
134
|
+
660,124.8,0.0,0.0
|
|
135
|
+
665,124.5,0.0,0.0
|
|
136
|
+
670,124.1,0.0,0.0
|
|
137
|
+
675,123.7,0.0,0.0
|
|
138
|
+
680,123.3,0.0,0.0
|
|
139
|
+
685,123.0,0.0,0.0
|
|
140
|
+
690,122.6,0.0,0.0
|
|
141
|
+
695,122.3,0.0,0.0
|
|
142
|
+
700,121.9,0.0,0.0
|
|
143
|
+
705,121.6,0.0,0.0
|
|
144
|
+
710,121.2,0.0,0.0
|
|
145
|
+
715,120.9,0.0,0.0
|
|
146
|
+
720,162.6,70.0,0.0
|
|
147
|
+
725,161.1,0.0,0.0
|
|
148
|
+
730,159.6,0.0,0.0
|
|
149
|
+
735,158.2,0.0,0.0
|
|
150
|
+
740,156.9,0.0,0.0
|
|
151
|
+
745,155.5,0.0,0.0
|
|
152
|
+
750,154.2,0.0,0.0
|
|
153
|
+
755,152.9,0.0,0.0
|
|
154
|
+
760,151.7,0.0,0.0
|
|
155
|
+
765,150.5,0.0,0.0
|
|
156
|
+
770,149.3,0.0,0.0
|
|
157
|
+
775,148.1,0.0,0.0
|
|
158
|
+
780,147.0,0.0,0.0
|
|
159
|
+
785,145.9,0.0,0.0
|
|
160
|
+
790,144.8,0.0,0.0
|
|
161
|
+
795,143.8,0.0,0.0
|
|
162
|
+
800,142.8,0.0,0.0
|
|
163
|
+
805,141.8,0.0,0.0
|
|
164
|
+
810,140.8,0.0,0.0
|
|
165
|
+
815,139.8,0.0,0.0
|
|
166
|
+
820,138.9,0.0,0.0
|
|
167
|
+
825,138.0,0.0,0.0
|
|
168
|
+
830,137.1,0.0,0.0
|
|
169
|
+
835,136.3,0.0,0.0
|
|
170
|
+
840,135.4,0.0,0.0
|
|
171
|
+
845,134.6,0.0,0.0
|
|
172
|
+
850,133.8,0.0,0.0
|
|
173
|
+
855,133.0,0.0,0.0
|
|
174
|
+
860,132.3,0.0,0.0
|
|
175
|
+
865,131.5,0.0,0.0
|
|
176
|
+
870,130.8,0.0,0.0
|
|
177
|
+
875,130.1,0.0,0.0
|
|
178
|
+
880,129.4,0.0,0.0
|
|
179
|
+
885,128.7,0.0,0.0
|
|
180
|
+
890,128.1,0.0,0.0
|
|
181
|
+
895,127.5,0.0,0.0
|
|
182
|
+
900,126.8,0.0,0.0
|
|
183
|
+
905,126.2,0.0,0.0
|
|
184
|
+
910,125.7,0.0,0.0
|
|
185
|
+
915,125.1,0.0,0.0
|
|
186
|
+
920,124.5,0.0,0.0
|
|
187
|
+
925,124.0,0.0,0.0
|
|
188
|
+
930,123.5,0.0,0.0
|
|
189
|
+
935,123.0,0.0,0.0
|
|
190
|
+
940,122.5,0.0,0.0
|
|
191
|
+
945,122.0,0.0,0.0
|
|
192
|
+
950,121.5,0.0,0.0
|
|
193
|
+
955,121.1,0.0,0.0
|
|
194
|
+
960,120.6,0.0,0.0
|
|
195
|
+
965,120.2,0.0,0.0
|
|
196
|
+
970,119.8,0.0,0.0
|
|
197
|
+
975,119.4,0.0,0.0
|
|
198
|
+
980,119.0,0.0,0.0
|
|
199
|
+
985,118.6,0.0,0.0
|
|
200
|
+
990,118.2,0.0,0.0
|
|
201
|
+
995,117.9,0.0,0.0
|
|
202
|
+
1000,117.5,0.0,0.0
|
|
203
|
+
1005,117.2,0.0,0.0
|
|
204
|
+
1010,116.9,0.0,0.0
|
|
205
|
+
1015,116.6,0.0,0.0
|
|
206
|
+
1020,116.3,0.0,0.0
|
|
207
|
+
1025,116.0,0.0,0.0
|
|
208
|
+
1030,115.7,0.0,0.0
|
|
209
|
+
1035,115.4,0.0,0.0
|
|
210
|
+
1040,115.2,0.0,0.0
|
|
211
|
+
1045,114.9,0.0,0.0
|
|
212
|
+
1050,114.7,0.0,0.0
|
|
213
|
+
1055,114.4,0.0,0.0
|
|
214
|
+
1060,114.2,0.0,0.0
|
|
215
|
+
1065,114.0,0.0,0.0
|
|
216
|
+
1070,113.8,0.0,0.0
|
|
217
|
+
1075,113.6,0.0,0.0
|
|
218
|
+
1080,113.4,0.0,0.0
|
|
219
|
+
1085,113.3,0.0,0.0
|
|
220
|
+
1090,113.1,0.0,0.0
|
|
221
|
+
1095,112.9,0.0,0.0
|
|
222
|
+
1100,112.8,0.0,0.0
|
|
223
|
+
1105,112.6,0.0,0.0
|
|
224
|
+
1110,112.5,0.0,0.0
|
|
225
|
+
1115,112.4,0.0,0.0
|
|
226
|
+
1120,112.3,0.0,0.0
|
|
227
|
+
1125,112.2,0.0,0.0
|
|
228
|
+
1130,112.1,0.0,0.0
|
|
229
|
+
1135,112.0,0.0,0.0
|
|
230
|
+
1140,111.9,0.0,0.0
|
|
231
|
+
1145,111.8,0.0,0.0
|
|
232
|
+
1150,111.7,0.0,0.0
|
|
233
|
+
1155,111.7,0.0,0.0
|
|
234
|
+
1160,111.6,0.0,0.0
|
|
235
|
+
1165,111.6,0.0,0.0
|
|
236
|
+
1170,111.5,0.0,0.0
|
|
237
|
+
1175,111.5,0.0,0.0
|
|
238
|
+
1180,111.4,0.0,0.0
|
|
239
|
+
1185,111.4,0.0,0.0
|
|
240
|
+
1190,111.4,0.0,0.0
|
|
241
|
+
1195,111.4,0.0,0.0
|
|
242
|
+
1200,96.4,0.0,0.0
|
|
243
|
+
1205,96.4,0.0,0.0
|
|
244
|
+
1210,96.4,0.0,0.0
|
|
245
|
+
1215,96.4,0.0,0.0
|
|
246
|
+
1220,96.4,0.0,0.0
|
|
247
|
+
1225,96.4,0.0,0.0
|
|
248
|
+
1230,96.5,0.0,0.0
|
|
249
|
+
1235,96.5,0.0,0.0
|
|
250
|
+
1240,96.5,0.0,0.0
|
|
251
|
+
1245,96.6,0.0,0.0
|
|
252
|
+
1250,96.6,0.0,0.0
|
|
253
|
+
1255,96.7,0.0,0.0
|
|
254
|
+
1260,96.7,0.0,0.0
|
|
255
|
+
1265,96.8,0.0,0.0
|
|
256
|
+
1270,96.8,0.0,0.0
|
|
257
|
+
1275,96.9,0.0,0.0
|
|
258
|
+
1280,97.0,0.0,0.0
|
|
259
|
+
1285,97.1,0.0,0.0
|
|
260
|
+
1290,97.1,0.0,0.0
|
|
261
|
+
1295,97.2,0.0,0.0
|
|
262
|
+
1300,97.3,0.0,0.0
|
|
263
|
+
1305,97.4,0.0,0.0
|
|
264
|
+
1310,97.5,0.0,0.0
|
|
265
|
+
1315,97.6,0.0,0.0
|
|
266
|
+
1320,97.7,0.0,0.0
|
|
267
|
+
1325,112.8,0.0,0.0
|
|
268
|
+
1330,112.9,0.0,0.0
|
|
269
|
+
1335,113.0,0.0,0.0
|
|
270
|
+
1340,113.1,0.0,0.0
|
|
271
|
+
1345,113.3,0.0,0.0
|
|
272
|
+
1350,113.4,0.0,0.0
|
|
273
|
+
1355,113.5,0.0,0.0
|
|
274
|
+
1360,113.6,0.0,0.0
|
|
275
|
+
1365,113.7,0.0,0.0
|
|
276
|
+
1370,113.9,0.0,0.0
|
|
277
|
+
1375,114.0,0.0,0.0
|
|
278
|
+
1380,114.1,0.0,0.0
|
|
279
|
+
1385,114.3,0.0,0.0
|
|
280
|
+
1390,114.4,0.0,0.0
|
|
281
|
+
1395,114.6,0.0,0.0
|
|
282
|
+
1400,114.7,0.0,0.0
|
|
283
|
+
1405,114.8,0.0,0.0
|
|
284
|
+
1410,115.0,0.0,0.0
|
|
285
|
+
1415,115.1,0.0,0.0
|
|
286
|
+
1420,115.3,0.0,0.0
|
|
287
|
+
1425,115.4,0.0,0.0
|
|
288
|
+
1430,115.6,0.0,0.0
|
|
289
|
+
1435,115.7,0.0,0.0
|
iints/data/importer.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
|
6
|
+
import io
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from iints.data.ingestor import DataIngestor
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _normalize_column(name: str) -> str:
|
|
16
|
+
return re.sub(r"[^a-z0-9]+", "", name.lower())
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _find_column(columns: Iterable[str], candidates: Iterable[str]) -> Optional[str]:
|
|
20
|
+
normalized = {col: _normalize_column(col) for col in columns}
|
|
21
|
+
candidate_set = {_normalize_column(c) for c in candidates}
|
|
22
|
+
for col, norm in normalized.items():
|
|
23
|
+
if norm in candidate_set:
|
|
24
|
+
return col
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
DEFAULT_MAPPINGS: Dict[str, Dict[str, List[str]]] = {
|
|
29
|
+
"generic": {
|
|
30
|
+
"timestamp": ["timestamp", "time", "datetime", "date", "eventtime", "device timestamp"],
|
|
31
|
+
"glucose": ["glucose", "bg", "sgv", "sensorglucose", "glucosemgdl", "glucosevalue"],
|
|
32
|
+
"carbs": ["carbs", "carb", "carbohydrates", "carbsg", "carbgrams"],
|
|
33
|
+
"insulin": ["insulin", "insulinunits", "bolus", "basal", "totalinsulin"],
|
|
34
|
+
},
|
|
35
|
+
"dexcom": {
|
|
36
|
+
"timestamp": ["timestamp", "eventtime", "device timestamp"],
|
|
37
|
+
"glucose": ["glucose", "glucosevalue", "sgv", "sensorglucose"],
|
|
38
|
+
"carbs": ["carbs", "carb", "carbohydrates"],
|
|
39
|
+
"insulin": ["insulin", "insulinunits", "bolus", "basal"],
|
|
40
|
+
},
|
|
41
|
+
"libre": {
|
|
42
|
+
"timestamp": ["timestamp", "device timestamp", "datetime", "time"],
|
|
43
|
+
"glucose": ["glucose", "glucosevalue", "sensorglucose", "sgv"],
|
|
44
|
+
"carbs": ["carbs", "carb", "carbohydrates"],
|
|
45
|
+
"insulin": ["insulin", "insulinunits", "bolus", "basal"],
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
IMPORT_FORMAT_SCHEMAS: Dict[str, Dict[str, List[str]]] = {
|
|
50
|
+
"generic": {
|
|
51
|
+
"required": ["timestamp", "glucose"],
|
|
52
|
+
"optional": ["carbs", "insulin"],
|
|
53
|
+
},
|
|
54
|
+
"dexcom": {
|
|
55
|
+
"required": ["timestamp", "glucose"],
|
|
56
|
+
"optional": ["carbs", "insulin"],
|
|
57
|
+
},
|
|
58
|
+
"libre": {
|
|
59
|
+
"required": ["timestamp", "glucose"],
|
|
60
|
+
"optional": ["carbs", "insulin"],
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class ImportResult:
|
|
67
|
+
dataframe: pd.DataFrame
|
|
68
|
+
scenario: Dict[str, Any]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def guess_column_mapping(columns: Iterable[str], data_format: str = "generic") -> Dict[str, Optional[str]]:
|
|
72
|
+
candidates = DEFAULT_MAPPINGS.get(data_format, DEFAULT_MAPPINGS["generic"])
|
|
73
|
+
return {
|
|
74
|
+
"timestamp": _find_column(columns, candidates.get("timestamp", [])),
|
|
75
|
+
"glucose": _find_column(columns, candidates.get("glucose", [])),
|
|
76
|
+
"carbs": _find_column(columns, candidates.get("carbs", [])),
|
|
77
|
+
"insulin": _find_column(columns, candidates.get("insulin", [])),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_import_schema(
|
|
82
|
+
columns: Iterable[str],
|
|
83
|
+
data_format: str,
|
|
84
|
+
column_map: Optional[Dict[str, str]] = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
schema = IMPORT_FORMAT_SCHEMAS.get(data_format, IMPORT_FORMAT_SCHEMAS["generic"])
|
|
87
|
+
candidates = DEFAULT_MAPPINGS.get(data_format, DEFAULT_MAPPINGS["generic"])
|
|
88
|
+
mapping = column_map or {}
|
|
89
|
+
|
|
90
|
+
missing: List[str] = []
|
|
91
|
+
for key in schema["required"]:
|
|
92
|
+
if key in mapping:
|
|
93
|
+
continue
|
|
94
|
+
found = _find_column(columns, candidates.get(key, []))
|
|
95
|
+
if found is None:
|
|
96
|
+
missing.append(key)
|
|
97
|
+
|
|
98
|
+
if missing:
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"Missing required columns for format '{data_format}': {', '.join(missing)}. "
|
|
101
|
+
f"Columns: {list(columns)}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def import_cgm_dataframe(
|
|
106
|
+
df: pd.DataFrame,
|
|
107
|
+
data_format: str = "generic",
|
|
108
|
+
column_map: Optional[Dict[str, str]] = None,
|
|
109
|
+
time_unit: str = "minutes",
|
|
110
|
+
source: Optional[str] = None,
|
|
111
|
+
) -> pd.DataFrame:
|
|
112
|
+
"""
|
|
113
|
+
Import CGM data from an in-memory DataFrame into the universal IINTS schema.
|
|
114
|
+
"""
|
|
115
|
+
columns = list(df.columns)
|
|
116
|
+
mapping = column_map or {}
|
|
117
|
+
mapping = {k: v for k, v in mapping.items() if v}
|
|
118
|
+
|
|
119
|
+
candidates = DEFAULT_MAPPINGS.get(data_format, DEFAULT_MAPPINGS["generic"])
|
|
120
|
+
|
|
121
|
+
validate_import_schema(columns, data_format=data_format, column_map=mapping)
|
|
122
|
+
|
|
123
|
+
def resolve(key: str, required: bool = True) -> Optional[str]:
|
|
124
|
+
if key in mapping:
|
|
125
|
+
return mapping[key]
|
|
126
|
+
col = _find_column(columns, candidates.get(key, []))
|
|
127
|
+
if required and col is None:
|
|
128
|
+
raise ValueError(f"Missing required column for '{key}'. Columns: {columns}")
|
|
129
|
+
return col
|
|
130
|
+
|
|
131
|
+
ts_col = resolve("timestamp", required=True)
|
|
132
|
+
glucose_col = resolve("glucose", required=True)
|
|
133
|
+
carbs_col = resolve("carbs", required=False)
|
|
134
|
+
insulin_col = resolve("insulin", required=False)
|
|
135
|
+
|
|
136
|
+
df = df.rename(
|
|
137
|
+
columns={
|
|
138
|
+
ts_col: "timestamp",
|
|
139
|
+
glucose_col: "glucose",
|
|
140
|
+
carbs_col: "carbs",
|
|
141
|
+
insulin_col: "insulin",
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if "carbs" not in df.columns:
|
|
146
|
+
df["carbs"] = 0.0
|
|
147
|
+
if "insulin" not in df.columns:
|
|
148
|
+
df["insulin"] = 0.0
|
|
149
|
+
|
|
150
|
+
# Parse timestamps
|
|
151
|
+
if pd.api.types.is_numeric_dtype(df["timestamp"]):
|
|
152
|
+
# Assume numeric (minutes or seconds)
|
|
153
|
+
if time_unit == "seconds":
|
|
154
|
+
df["timestamp"] = df["timestamp"].astype(float) / 60.0
|
|
155
|
+
else:
|
|
156
|
+
df["timestamp"] = df["timestamp"].astype(float)
|
|
157
|
+
elif pd.api.types.is_datetime64_any_dtype(df["timestamp"]):
|
|
158
|
+
ts = df["timestamp"]
|
|
159
|
+
df["timestamp"] = (ts - ts.iloc[0]).dt.total_seconds() / 60.0
|
|
160
|
+
else:
|
|
161
|
+
# Try datetime parsing, fallback to numeric
|
|
162
|
+
ts = pd.to_datetime(df["timestamp"], errors="coerce")
|
|
163
|
+
if ts.isna().all():
|
|
164
|
+
if time_unit == "seconds":
|
|
165
|
+
df["timestamp"] = df["timestamp"].astype(float) / 60.0
|
|
166
|
+
else:
|
|
167
|
+
df["timestamp"] = df["timestamp"].astype(float)
|
|
168
|
+
else:
|
|
169
|
+
df["timestamp"] = (ts - ts.iloc[0]).dt.total_seconds() / 60.0
|
|
170
|
+
|
|
171
|
+
df["source"] = source or data_format
|
|
172
|
+
ingestor = DataIngestor()
|
|
173
|
+
ingestor._validate_schema(df, ingestor.UNIVERSAL_SCHEMA)
|
|
174
|
+
return df[list(ingestor.UNIVERSAL_SCHEMA.keys())]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def import_cgm_csv(
|
|
178
|
+
path: Union[str, Path],
|
|
179
|
+
data_format: str = "generic",
|
|
180
|
+
column_map: Optional[Dict[str, str]] = None,
|
|
181
|
+
time_unit: str = "minutes",
|
|
182
|
+
source: Optional[str] = None,
|
|
183
|
+
) -> pd.DataFrame:
|
|
184
|
+
"""
|
|
185
|
+
Import CGM data from CSV into the universal IINTS schema.
|
|
186
|
+
"""
|
|
187
|
+
df = pd.read_csv(path)
|
|
188
|
+
return import_cgm_dataframe(
|
|
189
|
+
df,
|
|
190
|
+
data_format=data_format,
|
|
191
|
+
column_map=column_map,
|
|
192
|
+
time_unit=time_unit,
|
|
193
|
+
source=source,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def scenario_from_dataframe(
|
|
198
|
+
df: pd.DataFrame,
|
|
199
|
+
scenario_name: str,
|
|
200
|
+
scenario_version: str = "1.0",
|
|
201
|
+
description: str = "Imported CGM scenario",
|
|
202
|
+
carb_threshold: float = 0.1,
|
|
203
|
+
absorption_delay_minutes: int = 10,
|
|
204
|
+
duration_minutes: int = 60,
|
|
205
|
+
) -> Dict[str, Any]:
|
|
206
|
+
stress_events = []
|
|
207
|
+
if "carbs" in df.columns:
|
|
208
|
+
for _, row in df[df["carbs"] > carb_threshold].iterrows():
|
|
209
|
+
stress_events.append(
|
|
210
|
+
{
|
|
211
|
+
"start_time": int(row["timestamp"]),
|
|
212
|
+
"event_type": "meal",
|
|
213
|
+
"value": float(row["carbs"]),
|
|
214
|
+
"absorption_delay_minutes": absorption_delay_minutes,
|
|
215
|
+
"duration": duration_minutes,
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
"scenario_name": scenario_name,
|
|
221
|
+
"scenario_version": scenario_version,
|
|
222
|
+
"description": description,
|
|
223
|
+
"stress_events": stress_events,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def scenario_from_csv(
|
|
228
|
+
path: Union[str, Path],
|
|
229
|
+
scenario_name: str = "Imported CGM Scenario",
|
|
230
|
+
scenario_version: str = "1.0",
|
|
231
|
+
data_format: str = "generic",
|
|
232
|
+
column_map: Optional[Dict[str, str]] = None,
|
|
233
|
+
time_unit: str = "minutes",
|
|
234
|
+
carb_threshold: float = 0.1,
|
|
235
|
+
) -> ImportResult:
|
|
236
|
+
df = import_cgm_csv(
|
|
237
|
+
path,
|
|
238
|
+
data_format=data_format,
|
|
239
|
+
column_map=column_map,
|
|
240
|
+
time_unit=time_unit,
|
|
241
|
+
)
|
|
242
|
+
scenario = scenario_from_dataframe(
|
|
243
|
+
df,
|
|
244
|
+
scenario_name=scenario_name,
|
|
245
|
+
scenario_version=scenario_version,
|
|
246
|
+
carb_threshold=carb_threshold,
|
|
247
|
+
)
|
|
248
|
+
return ImportResult(dataframe=df, scenario=scenario)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def export_standard_csv(df: pd.DataFrame, output_path: Union[str, Path]) -> str:
|
|
252
|
+
output_path = Path(output_path)
|
|
253
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
df.to_csv(output_path, index=False)
|
|
255
|
+
return str(output_path)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _read_demo_csv_text() -> str:
|
|
259
|
+
if sys.version_info >= (3, 9):
|
|
260
|
+
from importlib.resources import files
|
|
261
|
+
return files("iints.data.demo").joinpath("demo_cgm.csv").read_text()
|
|
262
|
+
from importlib import resources
|
|
263
|
+
return resources.read_text("iints.data.demo", "demo_cgm.csv")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def load_demo_dataframe() -> pd.DataFrame:
|
|
267
|
+
text = _read_demo_csv_text()
|
|
268
|
+
return pd.read_csv(io.StringIO(text))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def export_demo_csv(output_path: Union[str, Path]) -> str:
|
|
272
|
+
output_path = Path(output_path)
|
|
273
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
274
|
+
output_path.write_text(_read_demo_csv_text())
|
|
275
|
+
return str(output_path)
|