-
Notifications
You must be signed in to change notification settings - Fork 0
/
code.py
357 lines (247 loc) · 19.6 KB
/
code.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# -*- coding: utf-8 -*-
"""Biomedical engineering.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/1w2Sq2BaHbndhNXHYjw7IPrBmRukSgyV8
#Στοιχεία σπουδαστή
<h3>Σκουρτσίδης Γεώργιος
<h3>ΑΜ: 03114307
# Περιγραφή Άσκησης
Σκοπός της Άσκησης είναι η εξοικείωση του φοιτητή με την εφαρμογή κριτηρίων αξιολόγησης της
διακριτικής ικανότητας αισθητήρων καταγραφής φυσιολογικών παραμέτρων
Στο πλαίσιο αυτής της άσκησης, σας δίνονται δεδομένα χρονοσειρών γλυκόζης ατόμων με σακχαρώδη
διαβήτη τύπου 1, τα οποία προέκυψαν από την εφαρμογή υπολογιστικών πειραμάτων ανοικτού βρόχου
στον προσομοιωτή UVa T1DM. Ο προσομοιωτής UVa T1DM είναι ένα υπολογιστικό περιβάλλον που
διαθέτει εικονικούς ασθενείς (παιδιά, έφηβους, ενήλικες) με σακχαρώδη διαβήτη τύπου 1 και έχει λάβει
έγκριση από τον Αμερικανικό Οργανισμό Φαρμάκων (FDA) για να χρησιμοποιείται ως υποκατάσταστο
προκλινικών δοκιμών για την αξιολόγηση ελεγκτών γλυκόζης.
Τα δεδομένα που σας δίνονται αντιστοιχούν σε περίπου 3.5 ημέρες και αποτελούνται από τρεις στήλες:
(i) στη στήλη Α παρουσιάζεται ο χρόνος σε λεπτά, (ii) στη στήλη Β, οι τιμές γλυκόζης αίματος σε mg/dl, και
(iii) στη στήλη C, οι καταγραφές γλυκόζης από αισθητήρα συνεχούς καταγραφής γλυκόζης σε mg/dl.
Συνήθως οι αισθητήρες συνεχούς καταγραφής γλυκόζης παρουσιάζουν ανακρίβειες και αποκλίσεις από
την γλυκόζη αναφοράς (γλυκόζη αίματος). Σε αυτήν την άσκηση, καλείστε να αξιολογήσετε την ακρίβεια
των αισθητήρων συνεχούς καταγραφής γλυκόζης, εφαρμόζοντας κατάλληλα κριτήρια αξιολόγησης, όπως
το Root Mean Square Error και η ROC ανάλυση.
# Ερωτήσεις
Load and inspect file
"""
import pandas as pd
df = pd.read_csv("Data.csv")
df
"""Αλλαγή ονομάτων σε στήλες για ευκολότερη διαχείριση παρακάτω."""
df = df.rename(columns={"Time (min)":"time", "Blood Glucose (mg/dl)":"real", "Sensor Glucose Readings(mg/dl)":"predicted"})
df.head()
"""## Ερώτηση 1 - RMSE
Να εφαρμόσετε την τετραγωνική ρίζα του μέσου τετραγωνικού σφάλματος (Root Mean Square Error
(RMSE)) για να αξιολογήσετε κατά πόσο οι καταγραφές γλυκόζης από τον αισθητήρα προσεγγίζουν τις
τιμές γλυκόζης αίματος.
"""
def rmse(df):
return ((df.predicted - df.real) ** 2).mean() ** .5
print(f"RMSE: {rmse(df)}")
"""To RMSE υποδεικνύει την προσαρμογή του μοντέλου στα δεδομένα, δηλαδή πόσο κοντά βρίσκονται τα παρατηρούμενα σημεία στις προβλεπόμενες τιμές του μοντέλου.Το RMSE μπορεί να ερμηνευθεί ως η τυπική απόκλιση της ανεξήγητης διακύμανσης και έχει την χρήσιμη ιδιότητα να βρίσκεται στις ίδιες μονάδες με τα δεδομένα μας. Οι χαμηλότερες τιμές του RMSE υποδεικνύουν καλύτερη εφαρμογή. Το RMSE είναι ένα μέτρο για το πόσο ακριβώς το μοντέλο προβλέπει τα πραγματικά δεδομένα.
## Ερώτηση 2 - Υπογλυκαιμίες
Θεωρώντας ως υπογλυκαιμικά επεισόδια τις τιμές γλυκόζης που είναι μικρότερες από 70 mg/dl, να
εφαρμόσετε τη ROC ανάλυση για να αξιολογήσετε την ικανότητα του αισθητήρα συνεχούς
καταγραφής γλυκόζης να ανιχνεύει τις υπογλυκαιμίες. Εφαρμόζοντας την τιμή 70 mg/dl ως κατώφλι
γλυκόζης, οι χρονοσειρές γλυκόζης αντιμετωπίζονται ως δυαδικές και με αυτό τον τρόπο καθίσταται
εφικτή η εφαρμογή της ROC ανάλυσης. Να υπολογίσετε τους δείκτες: Ευαισθησία, Ειδικότητα, Θετική
Διαγνωστική Αξία, Αρνητική Διαγνωστική Αξία, Ακρίβεια.
"""
# 0 - όχι υπογλυκαιμικό επεισόδιο (>=70)
# 1 - υπογλυκαιμικό επεισόδιο (<70)
threshold = 70
df_under = df.copy()
df_under['predicted'] = (df['predicted'] < threshold).astype(int)
df_under['real'] = (df['real'] < threshold).astype(int)
df_under.head()
"""### Confusion matrix"""
from sklearn.metrics import confusion_matrix
conf_matrix = confusion_matrix(df_under['real'].to_numpy(), df_under['predicted'].to_numpy())
print("Confusion Matrix\n\n",conf_matrix)
"""Prettier output"""
import seaborn as sns
import numpy as np
def plot_conf_matrix(conf_matrix):
group_names = ['True Neg','False Pos','False Neg','True Pos']
group_counts = ["{0:0.0f}".format(value) for value in
conf_matrix.flatten()]
group_percentages = ["{0:.2%}".format(value) for value in
conf_matrix.flatten()/np.sum(conf_matrix)]
labels = [f"{v1}\n{v2}\n{v3}" for v1, v2, v3 in
zip(group_names,group_counts,group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(conf_matrix, annot=labels, fmt='', cmap='Blues')
plot_conf_matrix(conf_matrix)
# from the confusion matrix
TP = true_pos = conf_matrix[1][1]
TN = true_neg = conf_matrix[0][0]
FP = false_pos = conf_matrix[0][1]
FN = false_neg = conf_matrix[1][0]
"""### Δείκτες αξιολόγησης"""
def set_metrics(TP,TN,FP,FN,results):
results["TPR"] = TP / (TP + FN) # Sensitivity or Recall
results["TNR"] = TN / (TN + FP) # Specificity
results["PPV"] = TP / (TP + FP) # Precision
results["NPV"] = TN / (TN + FN) # Νegative Predictive Value
results["ACC"] = (TP + TN) / (TP + TN + FP + FN) # Accuracy
return results
"""#### Περιγραφή δεικτών/μετρικών
<h4> Ευαισθησία (True Positive Rate)</h4>
Αριθμός δειγμάτων κατηγοριοποιημένος ως `Positive` / πραγματικός αριθμός δειγμάτων που είναι `Positive`
Ποσοστό των actual θετικών που αναγνωρίστηκαν σωστά (πόσα σχετικά επιλέγονται). Περιγράφει πόσο καλό είναι το μοντέλο στην πρόβλεψη της θετικής κλάσης όταν το πραγματικό αποτέλεσμα είναι θετικό.
Αλλιώς λέγεται **Ευαισθησία ή Ανάκληση - Sensitivity ή Recall**.
$TPR = \frac{TP}{P} = \frac{TP}{(TP+FN)}$
<h4> Ειδικότητα (True Negative Rate)</h4>
Αριθμός δειγμάτων κατηγοριοποιημένος ως `Negative` / πραγματικός αριθμός δειγμάτων που είναι `Negative`
Αλλιώς λέγεται **Ειδικότητα - Specificity**.
$SPC = \frac{TN}{N} = \frac{TN}{(TN+FP)}$
<h4> Θετική Διαγνωστική Αξία</h4>
Positive Predictive Value. Ποσοστό των θετικών που είναι σωστά (πόσα επιλεγμένα είναι σχετικά)
Λέγεται επίσης **Precision**.
$PPV = \frac{TP}{(TP + FP)}$
<h4>Αρνητική Διαγνωστική Αξία</h4>
Λέγεται και Negative Predictive Value
$NPV = \frac{TN}{(TN + FN)}$
<h4> Ακρίβεια (Accuracy )</h4>
Αριθμός σωστών προβλέψεων / συνολικός αριθμός δειγμάτων
$ACC = \frac{TP + TN}{(TN + TN + FP + FN)}$
#### Συγκριση δεικτών
"""
def print_metrics(results,df):
print(f"Aκρίβεια: {results['ACC']: .3f}")
print(f"Ειδικότητα: {results['TNR']: .3f}")
print(f"Ευαισθησία: {results['TPR']: .3f}")
print(f"Θετική Διαγνωστική Αξία: {results['PPV']: .3f}")
print(f"Αρνητική Διαγνωστική Αξία: {results['NPV']: .3f}")
results = set_metrics(TP,TN,FP,FN,{})
print_metrics(results,df_under)
"""### Γράφημα ROC και Precision-Recall
<h3> ROC </h3>
Θα σχεδιάσουμε το ROC γράφημα και το Precision-Recall για ένα μόνο threshold (δε θα πραγματοποιήσουμε εξαντλητική αναζήτηση για την εύρεση του βέλτιστου threshold).
Το ROC είναι μια γραφική παράσταση του ψευδώς θετικού ρυθμού(ειδικότητα) (άξονας x) έναντι του πραγματικού θετικού ρυθμού(ευαισθησία) (άξονας y) για έναν αριθμό διαφορετικών τιμών κατωφλίου μεταξύ 0 και 1.
Ένας τυχαίος ταξινομητής δείχνει πάντα μια ευθεία γραμμή από την αρχή (0,0) έως την επάνω δεξιά γωνία (1,1). Οι καμπύλες ROC στην περιοχή με την επάνω αριστερή γωνία (0, 1) υποδεικνύουν καλά επίπεδα απόδοσης, ενώ οι καμπύλες ROC στην άλλη περιοχή με την κάτω δεξιά γωνία (1,0) υποδεικνύουν χαμηλά επίπεδα απόδοσης.
Όσο πιο αριστερά βρίσκεται ένα σημείο στο γράφημα ROC τόσο μειώνεται το ποσοστό των False Positives.Ιδανικά θέλουμε ένα σημείο που να βρίσκεται κοντά ή πάνω στον $y'y$ άξονα $(x=0)$ και κοντά στην τιμή $1$ στον $y'y$ $(y=1)$.
Οι μικρότερες τιμές στον άξονα-x της γραφικής παράστασης δείχνουν χαμηλότερα ψευδώς θετικά και υψηλότερα αληθινά αρνητικά.
Μεγαλύτερες τιμές στον άξονα y της γραφικής παράστασης δείχνουν υψηλότερα αληθινά θετικά και χαμηλότερα ψευδώς αρνητικά.
Το γράφημα αυτό μας δείχνει την καλύτερη τιμή για το threshold, ανάλογα με το πόσα False Positive είμαστε διαθετιμένοι να αποδεχτούμε (ανάλογα με το εκάστοτε πρόβλημα).
<h2> Precision - Recall </h2>
Η γραφική παράσταση ακρίβειας-ανάκλησης χρησιμοποιεί ανάκληση στον άξονα x και την ακρίβεια στον άξονα y. Η ανάκληση είναι πανομοιότυπη με την ευαισθησία και η ακρίβεια είναι ίδια με θετική προγνωστική τιμή.
Αντίστοιχα παρουσιάζονται ο τυχαίος ταξινομητής και ο τέλειος ταξινομητής σε precision-recall γράφημα.
Ένας τυχαίος ταξινομητής δείχνει μια ευθεία γραμμή ως P / (P + N). Για παράδειγμα, η γραμμή είναι y = 0.5 όταν ο λόγος θετικών και αρνητικών είναι 1:1, ενώ 0.25 όταν ο λόγος είναι 1:3.
Ένας τέλειος ταξινομητής δείχνει έναν συνδυασμό δύο ευθειών γραμμών. Το τελικό σημείο εξαρτάται από την αναλογία θετικών και αρνητικών. Για παράδειγμα, το τελικό σημείο είναι (1.0, 0.5) όταν ο λόγος θετικών και αρνητικών είναι 1:1, ενώ (1.0, 0.25) όταν ο λόγος είναι 1:3.
Ένα σημείο στο ROC γράφημα έχει 1-1 αντιστοίχιση στο γράφημα Precision-Recall
"""
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt
def plot_roc_presision_recall(df):
# Get ROC curve FPR and TPR from true labels vs score values
fpr, tpr, thres = roc_curve(df['real'].to_numpy(), df['predicted'].to_numpy())
# Calculate ROC Area Under the Curve (AUC) from FPR and TPR data points
roc_auc = auc(fpr, tpr)
# Calculate precision and recall from true labels vs score values
precision, recall, thres = precision_recall_curve(df_under['real'].to_numpy(), df_under['predicted'].to_numpy())
plt.figure(figsize=(8, 3))
plt.subplot(1,2,1)
lw = 2
plt.plot(fpr, tpr, color='darkorange', lw=lw, label='ROC curve (area = %0.4f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc="lower right")
plt.grid(True)
plt.subplot(1,2,2)
plt.step(recall, precision, color='orange', where='post')
# plt.fill_between(recall, precision, step='post', alpha=0.5, color='orange')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title('Precision - Recall Curve')
plt.grid(True)
left = 0.125 # the left side of the subplots of the figure
right = 0.9 # the right side of the subplots of the figure
bottom = 0.1 # the bottom of the subplots of the figure
top = 0.9 # the top of the subplots of the figure
wspace = 0.5 # the amount of width reserved for blank space between subplots
hspace = 0.2 # the amount of height reserved for white space between subplots
plt.subplots_adjust(left, bottom, right, top, wspace, hspace)
plt.show()
plot_roc_presision_recall(df_under)
"""Το threshold αυτό μας έδωσε ένα σημείο στο ROC πολύ κοντά στην τιμή (1,0) και στο Precision-Recall ακριβώς πάνω στο (1,1), πράγμα που σημαίνει πως έκανε πολύ καλή δουλειά στην κατηγοριοποίηση των δειγμάτων.
## Ερώτηση 3 - Υπεργλυκαιμίες
Ομοίως, να αξιολογήσετε την ικανότητα του αισθητήρα συνεχούς καταγραφής γλυκόζης να ανιχνεύει
τις υπεργλυκαιμίες που αντιστοιχούν σε τιμές γλυκόζης μεγαλύτερες από 180 mg/dl. Να υπολογίσετε
τους δείκτες: Ευαισθησία, Ειδικότητα, Θετική Διαγνωστική Αξία, Αρνητική Διαγνωστική Αξία, Ακρίβεια
"""
# 0 - όχι υπερ-γλυκαιμικό επεισόδιο
# 1 - υπερ-γλυκαιμικό επεισόδιο
threshold = 180
df_over = df.copy()
df_over['predicted'] = (df['predicted'] > threshold).astype(int)
df_over['real'] = (df['real'] > threshold).astype(int)
df_over.head()
"""### Confusion matrix"""
conf_matrix = confusion_matrix(df_over['real'].to_numpy(), df_over['predicted'].to_numpy())
print("Confusion Matrix\n\n",conf_matrix)
"""Prettier output"""
plot_conf_matrix(conf_matrix)
# from the confusion matrix
TP = true_pos = conf_matrix[1][1]
TN = true_neg = conf_matrix[0][0]
FP = false_pos = conf_matrix[0][1]
FN = false_neg = conf_matrix[1][0]
"""### Δείκτες αξιολόγησης"""
results = set_metrics(TP,TN,FP,FN,{})
print_metrics(results,df_over)
"""### Γράφημα ROC και Precision-Recall"""
plot_roc_presision_recall(df_over)
"""Το threshold αυτό μας έδωσε ένα σημείο στο ROC πολύ κοντά στην τιμή (1,0) και στο Precision-Recall κοντά στο (1,1), πράγμα που σημαίνει πως έκανε πολύ καλή δουλειά στην κατηγοριοποίηση των δειγμάτων.
## Ερώτηση 4 - Σχολιασμός
Με βάση τα αποτελέσματα των ερωτημάτων 1, 2, και 3, να αναφέρετε τα συμπεράσματά σας σχετικά
την ικανότητα του αισθητήρα συνεχούς καταγραφής γλυκόζης να ανιχνεύει τις υπογλυκαιμίες και τις
υπεργλυκαιμίες.
Τα αποτελέσματα από τα προηγούμενα ερωτήματα είναι ιδιαίτερα ενθαρρυντικά. Στις υπογλυκαιμίες ο confusion matrix μας δίνει
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-AMS_CHTML-full"></script>
\begin{array}{|c|c|}
\hline
3774 & 49 \\
\hline
75 & 1142 \\
\hline
\end{array}
Οι μετρικές:
\begin{array}{|c|c|}
\hline
Aκρίβεια & 0.975\\
Ειδικότητα& 0.987\\
Ευαισθησία & 0.938\\
Θετική\space Διαγνωστική\space Αξία: & 0.959 \\
Αρνητική\space Διαγνωστική\space Αξία & 0.981\\
\hline
\end{array}
Στις υπερ-γλυκαιμίες: \begin{array}{|c|c|}
\hline
4348 & 130 \\
\hline
92 & 470 \\
\hline
\end{array}
Οι μετρικές:
\begin{array}{|c|c|}
\hline
Aκρίβεια & 0.956\\
Ειδικότητα & 0.971\\
Ευαισθησία & 0.836\\
Θετική\space Διαγνωστική\space Αξία: & 0.783 \\
Αρνητική\space Διαγνωστική\space Αξία & 0.979\\
\hline
\end{array}
Παρατηρώντας το ROC και Precision-Recall γράφημα βλέπουμε πως τα αποτελέσματα είναι σχεδόν τέλεια.Το ίδιο βλέπουμε και παρατηρώντας τις μετρικές,καθώς πετυχαίνουν υψηλές τιμές.
Η βελτίωση που ίσως θα μπορούσαμε να κάνουμε θα ήταν να μειώσουμε τα False Negatives, καθώς είναι ιδιαίτερα επικίνδυνα σε ένα ιατρικό ζήτημα,όπως το αναφερόμενο. Θα προτιμήσουμε αύξηση του Specificity σε βάρος όλων των άλλων μετρικών. Είναι προτιμότερο το σύστημα να κάνει πιο συχνά "false alarm" παρά να λέει (έστω και σπάνια) σε έναν άνθρωπο που έχει πρόβλημα πως όλα είναι καλά. Το 1ο είναι απλώς ενοχλητικό, το 2ο είναι επικίνδυνο.
"""