{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**What does this notebook do?**\n",
"- Load the exported CGM values from NutriSense\n",
"- Print out what days are included in the dataset\n",
"- Pair down data to only one day, include CGM values, meals and exercise\n",
"- Smooth CGM data and interpolate missing values\n",
"- Calculate key metrics for that day\n",
"- Create a chart of the glucose values and include metrics"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import plotly.express as px\n",
"import datetime\n",
"from datetime import date\n",
"import numpy as np\n",
"from numpy import trapz\n",
"\n",
"# Read in CSV file\n",
"df = pd.read_csv('export.csv')\n",
"\n",
"# Remove \"time zone offset\" from \"occurred_at\" column and add new \"occurred_at_day\" column\n",
"df['occurred_at_day'] = df['occurred_at'].apply(lambda x: x[:len(x) - 15])\n",
"df['occurred_at'] = df['occurred_at'].apply(lambda x: x[:len(x) - 6])\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Print all days with data\n",
"daysWithData = df['occurred_at_day'].unique()\n",
"print(daysWithData)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Filter down to one day, pick the second day in the dataset\n",
"df = df[df['occurred_at_day']==daysWithData[2]]\n",
"day = daysWithData[2]\n",
"\n",
"# Create a datasets just with glucose measurments\n",
"gm = df[df['class']=='GlucoseMeasurement']\n",
"\n",
"# Create a dataset for meals and exercise, sort it\n",
"mealsExercise = df[((df['class']=='Meal') | (df['class']=='ExerciseActivity') )]\n",
"mealsExerciseSorted = mealsExercise.sort_values(by=[\"occurred_at\"], ascending=True)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a dataset with just 2 columns\n",
"gm_data = gm.filter(['occurred_at', 'value'])\n",
"\n",
"# rename the columns for easier readability\n",
"gm_data.columns = ['time', 'value']\n",
"\n",
"# turn time column into the index and delete time column\n",
"gm_data['time']= pd.to_datetime(gm_data['time'])\n",
"gm_data.index = gm_data['time']\n",
"del gm_data['time']\n",
"\n",
"gm_data = gm_data.resample('1T').mean() # add rows for every 1 minute\n",
"gm_data = gm_data.interpolate(method='cubic') # interpolate the new 1 minute points with data\n",
"\n",
"# Calculate a few metrics\n",
"threshold = 120 # this is an arbitrary threshold\n",
"above = gm_data[gm_data['value'] > threshold] # create a dataset with glucose measuremnts over threshold\n",
"minutesAboveThreshold = above.count()\n",
"print('Number of minutes above '+str(threshold)+': '+ minutesAboveThreshold.to_string(index=False))\n",
"\n",
"percentageAboveThreshold = int(round(minutesAboveThreshold/(60*24)*100,0))\n",
"print(\"Time above Threshold = \"+str(percentageAboveThreshold)+\"%\")\n",
"\n",
"averageGlucose = int(round(gm_data['value'].mean()))\n",
"medianGlucose = int(round(gm_data['value'].median()))\n",
"print(\"Average Glucose = \"+str(averageGlucose))\n",
"print(\"Median Glucose = \"+str(medianGlucose))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(gm_data, y=\"value\")\n",
"\n",
"# add meals and exercise to the chart\n",
"yText = 60\n",
"eventColor = \"green\"\n",
"for index, row in mealsExerciseSorted.iterrows():\n",
"\n",
" # Convert the time in pandas to something that we can use as an index for the x-axis placement\n",
" time = datetime.datetime.strptime(row['occurred_at'], '%Y-%m-%d %H:%M:%S')\n",
"\n",
" # Pick a different color depending on the event\n",
" if (row['class'] == \"Meal\"): eventColor = \"black\"\n",
" else: eventColor = \"green\"\n",
"\n",
" # draw a vertical line at the time of the meal/exercise\n",
" fig.add_shape(type=\"line\", xref=\"x\", yref=\"y\", x0=time, y0=70, x1=time , y1=140, line_color=eventColor,)\n",
" \n",
" # Alternate text placement so adjacent text doesn't overlap\n",
" if (yText == 60): yText = 65\n",
" else: yText = 60\n",
" \n",
" # Add text\n",
" fig.add_annotation(text=row['description'], xref=\"x\", yref=\"y\", x=time, y=yText, showarrow=False, font=dict(color=eventColor))\n",
"\n",
"\n",
"# Draw a line at the threshold\n",
"fig.add_shape(type=\"line\", xref=\"x\", yref=\"y\",\n",
" x0=gm_data.index[0], y0=threshold, x1=gm_data.index.max(), y1=threshold, line_color=\"red\",)\n",
"\n",
"# Show text box with summary values\n",
"fig.add_annotation(\n",
" text='Threshold = '+str(threshold)+\n",
" '
Minutes above Threshold = '+str(int(round(minutesAboveThreshold,0)))+\n",
" '
Time above Threshold = '+str(percentageAboveThreshold)+\"%\"+\n",
" '
Average Glucose = '+str(averageGlucose)+\n",
" '
Median Glucose = '+str(medianGlucose),\n",
" align='right', showarrow=False,\n",
" xref='paper', yref='paper', x=0.002, y=0.005,\n",
" bordercolor='black', borderwidth=1\n",
" )\n",
"\n",
"# Set x and y axis title and unit\n",
"fig.update_xaxes(title_text=str(day), tickformat='%H:%M')\n",
"fig.update_yaxes(title_text='mg/dL')\n",
"fig.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"interpreter": {
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}