{ "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", "- Filter down CGM data to only one day\n", "- Extract data for meals and exercise\n", "- Interpolate missing data and smooth curve\n", "- Plot CGM data for that day\n", "- Add meals and exercise events into the chart " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import plotly.express as px\n", "import datetime\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", "day = daysWithData[2]\n", "df = df[df['occurred_at_day']==day]\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", "\n", "# Only keep the columns that we need\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" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Chart the data\n", "fig = px.line(gm_data, y=\"value\")\n", "\n", "# Set defaults for color and text placement\n", "yText = 60\n", "eventColor = \"green\"\n", "\n", "# Loop through all meals and exercise events\n", "for index, row in mealsExerciseSorted.iterrows():\n", "\n", " # Pick a different color depending on the event\n", " if (row['class'] == \"Meal\"):\n", " eventColor = \"black\"\n", " else:\n", " eventColor = \"green\"\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", " # 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", "# Add axis titles\n", "fig.update_xaxes(title_text=str(day), tickformat='%H:%M')\n", "fig.update_yaxes(title_text='mg/dL')\n", "\n", "# Show the chart\n", "fig.show()" ] } ], "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 }