Project#
In dit hoofdstuk werk je met behulp van Python een casus uit met echte patiëntendata. Gebruik hierbij de functies en modules die je eerder in de cursus bent tegengekomen.
Maak je eigen .ipynb-bestand en houd daarin je antwoorden bij. Voeg headers in Markdown toe om de opdrachten uit elkaar te houden.
Inleiding#
Voor patiënten met type 1 of 2 diabetes is het belangrijk dat hun bloedglucose goed gereguleerd is. Goede regulatie beperkt de kans op ontwikkeling van macro- en microvasculaire complicaties. Er zijn meerdere manieren om bloedglucose te monitoren:
Bloedglucosemonitoring (
BGM). Diabeten die insuline gebruiken, prikken dagelijks 4 a 6 keer: bij ontbijt, lunch, diner en soms bij de twee tussendoortjes op een dag, en voor het slapen gaan. Daarnaast prikken ze ook wanneer er symptomen zijn van een hypo- of hyperglycemie. Met die prikken wordt bloedglucose gemeten.Tegenwoordig gebruiken steeds meer diabeten een continue glucosemeter (
CGM), die, afhankelijk van het type, iedere 5 of 15 minuten een glucosemeting doet.HbA1c. Dit is een maat voor de hoeveelheid glucose die zich in de afgelopen drie maanden heeft opgestapeld in de rode bloedcellen. (Dit is niet hetzelfde als glucoseconcentratie in het bloed.)
Met de komst van de continue glucosemter, de CGM, hebben diabeten en hun behandelaren meer inzicht gekregen in de glucoseregulatie. Er zijn op basis daarvan nieuwe klinische CGM-parameters gedefinieerd (zie Tabel 1 en Figuur 1). De verwachting is dat we daarmee de belangrijke maar moeilijker te bepalen HbA1c kunnen schatten.
Voor deze opdracht maken we gebruik van open-source data van een studie\(^{1}\) die onderzoekt of CGM zonder regelmatige vingerprik net zo veilig is als het gebruik van CGM met regelmatige vingerprik. Dit onderzoek is gedaan bij Type 1 diabetespatiënten. De CGM die de patiënten gebruiken tijdens dit onderzoek bepaalt iedere 5 minuten een glucosewaarde.
De tabel hieronder bevat de variabelen en eenheden die we in deze opdrachten gebruiken. Bij sommmige van de waarden staat ook een doelwaarde. In de tabel hieronder zijn \(a\) en \(b\) constanten die gebruikt worden om tussen glucosewaarden te rekenen, daarover straks meer.
Tabel 1 Gebruikte variabelen en hun eenheid, doel en omschrijving.
Afkorting |
Naam |
Definitie |
Doel |
Eenheid |
|
|---|---|---|---|---|---|
\(g_{BGM}\) |
|
bloedglucosemonitoring |
glucoseconcentratie (op onregelmatige tijden) |
- |
mmol/L |
\(g_{CGM}\) |
|
continueglucosemonitoring |
glucoseconcentratie (gemaakt door een continue glucosemeter) |
- |
mmol/L |
\(h\) |
|
hemoglobinA1c |
hoeveelheid glucose in bloedcel |
- |
mmol/mol |
\(h_{CGM}\) |
|
extimated HbA1c |
schatting van HbA1c op basis van gemiddelde CGM |
< 53 |
mmol/L |
\(a\) |
- |
- |
- |
- |
12.71 mmol/mol |
\(b\) |
- |
- |
- |
- |
4.70587 L/mol |
\(p_{TAR}\) |
|
time above range |
percentage van de tijd dat de CGM boven 10 mmol/L is |
< 30 |
% |
\(p_{TIR}\) |
|
time in range |
percentage van de tijd dat de CGM tussen 3.9 en 10 mmol/L is |
> 70 |
% |
\(p_{TBR}\) |
|
time below range |
percentage van de tijd dat de CGM onder 3.9 mmol/L is |
< 5 |
% |
Figuur 1 Voorbeeld van een gestapeld staafdiagram van CGM-waarden (mmol/L) met doelwaarden. Zo is in het ideale geval de TIR (percentage van de tijd dat \(3.9 <CGM< 10\)) meer dan 70%. Zie ook Tabel 1.
Opdrachten#
Opdracht 11.1
De gegevens zijn opgeslagen in het bestand glucosedata.pickle dat je hier kunt vinden. Een ‘pickle’ bestand bevat een object dat we uit kunnen pakken in Python. Gebruik onderstaande code om deze pickle uit te pakken. Het resultaat wordt opgeslagen in glucodat.
with open('glucosedata.pickle', 'rb') as handle:
glucodat = pickle.load(handle)
Inspecteer de variabele glucodat. Zoals je ziet is het een dictionary. Hoeveel patiënten zitten er in deze dictionary?
Iedere patiënt heeft een unieke ID. De ID van patient 2 is bijvoorbeeld ‘ID_2’. Je kunt alle IDs krijgen met de .keys() method op de dictonary. Voor elke patiënt zijn metingen van BGM en CGM en HbA1c opgeslagen. Om de BGM waarden van bijvoorbeeld patiënt ‘ID_2’ te verkrijgen, gebruik je (zoals je in Hoofdstuk 2 hebt geleerd) dictionary indexing:
gbm_values = glucodat['ID_2']['BGM']
Tijd
BGM, CGM en HBA1c zijn opgeslagen als NumPy arrays. De BGM is vaak gemeten op onregelmatige tijden, de CGM zijn de continue glucosemetingen, meestal regelmatig gemeten, de HBA1c is slecht één keer gemeten, maar die meting is twee keer opgeslagen. Hou er rekening mee dat al deze NumPy arrays twee kolommen hebben. De eerste kolom bevat de tijd van elke meting, de tweede kolom de waarde op dat moment.
De tijd is in dagen, de glucosedata (BGM, CGM) zijn in mmol/L, en de HbA1c is in
mmol/mol. Per patiënt zijn er 3 maanden aan data geselecteerd. Patiënten hebben verschillende hoeveelheden BGM- en CGM-metingen gedaan. Daardoor is de data van iedere patiënt verschillend van lengte.
Opdracht 11.2
Gebruik de numpy.random module (zie Hoofdstuk 6) om drie willekeurige patiënten te selecteren. Maak vervolgens in één figuur drie subplots (voor elke patiënt een subplot, zie Hoofdstuk 8), en zet per patiënt in één subplot (zie Hoofdstuk 8):
de
CGMvan de patiënt als functie van tijdde
BGMvan de patiënt als functie van tijdde gemiddelde bloedglucoseconcentratie
g(mmol/L) van de patiënt geschat a.d.h.v. opgestapelde glucose in bloedcellen als
met \(a\) = 12.71mmol/mol en \(b\) = 4.70587 L/mol.
Hiervoor is het van belang dat je eerst de waarde \(h\) berekent. Hou er rekening me dat HbA1c een NumPy array met shape [2, 2] is. Voor een patiënt ziet hij er bijvoorbeeld zo uit.
[[-9.83796296e-04 5.73820000e+01]
[ 8.99990162e+01 5.73820000e+01]]
De eerste kolom bevat hier twee tijdpunten, op ~0 dagen en ~90 dagen, en de tweede kolom bevat tweemaal de gemeten HbA1c waarde (die dus niet verandert door de tijd heen).
Voeg zinvolle titels, labels en een legenda toe.
HbA1c vs klinische CGM parameters#
Nu gaan we HbA1c vergelijken met een paar klinische CGM-parameters. We onderscheiden vier parameters (per patiënt):
1:
eA1c: In Opdracht 11.2 schatten we een gemiddelde glucoseconcentratieguitHbA1cvolgens de formule \(g = \frac{HbA1c − a}{b}\). Omgekeerd kunnen we uit het gemiddelde \(g_{cgm}\) (mmol/mol) van de CGM-metingen van de patiënt deHbA1cschatten volgens \(h_{CGM} = a +b\overline{g}_{cgm}\).
met a = 12.71 mmol/mol en b = 4.70587L/mol. Deze \(h_{CGM}\) wordt eA1c genoemd (voor “estimated HbA1c”).
2:
TAR: Dit is het percentage samples van CGM hoger dan 10.0 mmol/L.3:
TIR: Dit het percentage samples van CGM tussen de 3.9 en 10.0 mmol/L.4:
TBR: Dit is het percentage samples van CGM kleiner dan 3.9 mmol/L.
Opdracht 11.3
Maak een Python functie gluc_param die voor een gegeven CGM signaal (van een patiënt) alle vier parameters (eA1c, TAR, TIR, TBR) bepaalt en teruggeeft.
Test deze functie met de data van drie patienten. Bijvoorbeeld, door de functie aan te roepen als gluc_param(glucodat['ID_2']['CGM']).
De streefwaarde van HbA1c voor diabetespatiënten is < 53 mmol/mol. Voor sommige patiënten is deze streefwaarde lastig te behalen. Nu willen we kijken hoe de verschillende HbA1c streefwaarden zich verhouden tot de CGM-parameters (eA1c, TAR, TIR, TBR). We onderscheiden drie groepen:
Patiëntgroep 1: |
laag: |
Patiëntgroep 2: |
midden: 53 < |
patiëntgroep 3: |
hoog: |
Opdracht 11.4
Maak een stacked bar chart, waarbij de drie verschillende patiëntgroepen naast elkaar op de x-as staan, en, per patiëntgroep de gemiddelde TBR, TIR en TAR voor de patiëntgroep op elkaar gestapeld staan, een beetje zoals in Figuur 1.
Hints:
Bepaal deze klinische parameters voor alle patiënten en plaats in één container.
Bereken de gemiddelde
TBR,TIRenTAR, voor de verschillende groepen.Maak een bar-graph met de Matplotlib-functie
bar. Hier staat een voorbeeld van een stacked bar chart in Matplotlib.
Moving average#
Het is je wellicht opgevallen dat de CGM-metingen als functie van tijd enorm fluctueren. Door de metingen te middelen over tijd ontstaat er een langzaam variërende functie van tijd, en die zegt vermoedelijk meer over HbA1c. Het langzaam variërende signaal construeren we met een “moving averaging” filter van window size \(M\). Een moving average, of voortschrijdend gemiddelde, bepaalt het gemiddelde over de afgelopen \(M\) metingen en vervangt daarmee de waarde in het signaal. Met andere woorden: het is een filter dat op basis van metingen \(x_{1},x_{1},...,x_{n}\) het signaal \(y_{1},y_{1},...,y_{n}\) bepaalt volgens de regel
Het getal \(M\) wordt de window size van het filter genoemd. Als M=1 kopiëren we gewoon de waarden van \(x\) naar \(y\), en verder geldt dat hoe hoger \(M\) is, hoe gladder het signaal \(y\) wordt. Op Wikipedia staat een uitgebreide uitleg en onderstaand voorbeeld met \(M=3\)
Dag |
Koers |
MA berekend over 3 dagen |
|---|---|---|
1 |
10 |
|
2 |
11 |
|
3 |
15 |
12 |
4 |
7 |
11 |
5 |
9 |
10,3 |
6 |
6 |
7,3 |
7 |
7 |
7,3 |
8 |
8 |
7 |
9 |
17 |
10,7 |
10 |
20 |
15 |
Opdracht 11.5
Maak een Python functie movingav met syntax y = movingav(x, m) die uit een vector x een vector y bepaalt zoals gedefinieerd in (1).
Hint: Gebruik np.convolve() in mode=same. Zoals je in de documentatie kunt lezen voert deze functie voert convolutie uit over een inputsignaal met een filter. Om een moving average filter met window size \(M\) te maken, kun je iets gebruiken als filter = np.ones((M))/M.
Controleer deze functie met onderstaande code, en leg uit waarom de plot klopt.
x = np.array([0, 1, 1, 1, 1, 1])
t = np.array([0, 1, 2, 3, 4, 5]);
M = 2
y = movingav(x, M)
print(y)
plt.stem(t,y) # ook wel "lollipop plot" genoemd
plt.show()
Opdracht 11.6
Selecteer drie patiënten, en, per patiënt, maak een figuur met daarin een plot van de:
ongefilterde
CGMdata,gefilterde
CGMdata (dus bepaald m.b.v. movingav)de geschatte gemiddelde bloedglucoseconcentratie
gbepaald op basis van deHbA1cgegevens.
Dit plaatje zou hierop moeten lijken:
Opdracht 11.7
Maak plaatjes met verschillende orde \(M\) van het moving average filter. Filter bijvoorbeeld ook eens met grote orders, bijvoorbeeld het aantal samples voor een dag, of voor een week. Je mag er vanuit gaan dat er elke vijf minuten gemeten is. Wat valt je op voor verschillende filter ordes?
Referenties#
[1]: Aleppo G, Ruedy KJ, Riddlesworth TD, Kruger DF, Peters AL, Hirsch I, et al. REPLACE-BG: A Randomized Trial Comparing Continuous Glucose Monitoring With and Without Routine Blood Glucose Monitoring in Adults With Well-Controlled Type 1 Diabetes. Diabetes Care [Internet]. 2017 Apr 1 [cited 2021 Oct 4];40(4):538–45. Available from: https://care.diabetesjournals.org/content/40/4/538. Dataset beschikbaar vanaf: https://public.jaeb.org/datasets/diabetes.