{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "source": [ "# Guide to Extracting Data w/ APIs from the Withings Sleep Tracking Mat\n", "\n", "\n", "\n", "There are lots of fitness and health tracking devices that require you to strap something to your arm, finger, or head. But what if you could track your sleep without even feeling it? With the [Withings Sleep Tracking Mat](https://www.withings.com/us/en/sleep), a tracking mat that goes right under your mattress, you can do just that. While this notebook is meant for the tracking mat, it can be easily adapted to any other Withings product with some modifications.\n", "\n", "If you want to know more about Withings Sleep Tracking Mat, see the [README](https://github.com/alrojo/wearipedia/tree/main/wearables/withings_sleepmat) for a detailed analysis of performances, sensors, data privacy, and extraction pipelines.\n", "\n", "TODO: fix the withings sleepmat readme url\n", "\n", "We will be able to extract the following parameters (see the definitions at [this documenation page](https://developer.withings.com/api-reference#operation/sleepv2-getsummary)):\n", "\n", "| Parameter Name | Sampling Frequency |\n", "|-----------------------|--------------------|\n", "| Heart Rate | Every 10 minutes OR every second (when set to [continuous heart rate mode](https://support.withings.com/hc/en-us/articles/360010042798-ScanWatch-Tracking-my-heart-rate)) |\n", "| # of REM sleep phases | Per sleep |\n", "| Sleep Efficiency | Per sleep |\n", "| Sleep Latency | Per sleep |\n", "| Total Sleep Time | Per sleep |\n", "| Total Time in bed | Per sleep |\n", "| Wakeup latency | Per sleep |\n", "| Waso | Per sleep |\n", "| Asleepduration | Per sleep |\n", "| Deep sleep duration | Per sleep |\n", "| Duration to sleep | Per sleep |\n", "| Duration to wakeup | Per sleep |\n", "| Average heart rate | Per sleep |\n", "| Max heart rate | Per sleep |\n", "| Min heart rate | Per sleep |\n", "| Light sleep duration | Per sleep |\n", "| Night events | Per sleep |\n", "| Out of bed count | Per sleep |\n", "| REM sleep duration | Per sleep |\n", "| Average resp rate | Per sleep |\n", "| Minimal resp. rate | Per sleep |\n", "| Max resp. rate | Per sleep |\n", "| Sleep score | Per sleep |\n", "| Total snoring time | Per sleep |\n", "| Snoring episode count | Per sleep |\n", "| Wakeup count | Per sleep |\n", "| Wakeup duration | Per sleep |\n", "\n", "TODO: Consider adding variable names as a column to decrease confusion/searching\n", "\n", "Note that Withings provides even more measurements than just these. You can check these out at the [API reference](https://developer.withings.com/api-reference/). Since we focus on heart rate and sleep here, though, those are the main measurement types we extract.\n", "\n", "

\n", "In this guide, we sequentially cover the following **five** topics to extract from the Withings API:\n", "1. **Setup**\n", "2. **Authentication/Authorization**\n", " - This requires a couple extra steps on your part\n", "3. **Data extraction**\n", " - You can get data from the API in a couple lines of code.\n", "4. **Data visualization**\n", " - 4.1: We reproduce a plot for heart rate over the course of a day\n", " - 4.2: We reproduce a plot for sleep data over the course of a week\n", "5. **Data analysis**\n", " - 5.1: We try to find a correlation between the length of a sleep period and the median heart rate for that sleep period. We find that the correlation is not statistically significant." ], "metadata": { "id": "OD2g9xsgxSYy" } }, { "cell_type": "markdown", "source": [ "# 1. Setup\n", "\n", "Relevant libraries are imported below. Just run the code to import all the libraries." ], "metadata": { "id": "EGZZcSOH3PT1" } }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OVi0ihtq1QJ7" }, "outputs": [], "source": [ "import requests\n", "import urllib\n", "import json\n", "from datetime import datetime\n", "from tqdm import tqdm\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "from scipy.ndimage import gaussian_filter\n", "from scipy import stats" ] }, { "cell_type": "markdown", "source": [ "# 2. Authentication/Authorization\n", "\n", "To be able to make requests to the API, the easiest way is to use the public developer API. This section roughly follows the steps outlined [here](https://developer.dexcom.com/authentication) on their website.\n", "\n", "First, follow the non-colab steps listed below:\n", "\n", "1. Visit the [developer portal](https://developer.withings.com/) and click \"Open Developer Dashboard\" on the top right.\n", "2. Once logged in, click \"Add an app\".\n", "3. For now, you can just click \"I don't know\" under \"Services\", accept terms of use, and click \"Next\".\n", "5. Put whatever you want under \"Application Name\" (we used `withings-test`), anything under \"Application Description\", and \"https://wbsapi.withings.net/v2/oauth2\" under Registered URLs, then click \"Done\".\n", " - NOTE: \"registered URLs\" is intended to be a URL to a webserver you control and can receive requests from. However, in this notebook we are simply using it as a placeholder, as this functionality is not strictly necessary for obtaining your data.\n", "\n", "In the end, you should see something like the below.\n", "\n", "\n", "\n", "Now we can proceed with the rest of the notebook.\n", "\n", "To be able to make requests to the API and extract the data we need, we need to first issue an access token. This (ephemeral) access token will serve as our key to the data. While, you don't necessarily need to be familiar with how the issuing of the authtoken occurs, you can learn more about it by visiting [the official Withings tutorial](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/get-access/oauth-web-flow/)." ], "metadata": { "id": "d2Kbo6d4waj9" } }, { "cell_type": "code", "source": [ "#@title 6. Enter your credentials below (from the application you just created)\n", "CLIENT_ID = \"d97ef704c1357d5330414a6ef6ee939062a7ef69656c51a8ab1565bc5eb4bd1e\" #@param {type:\"string\"}\n", "CUSTOMER_SECRET = \"692001d6c0d3c915b313669fd1dbc036b9062b5c4209a25c1e5bdcf721ad7e94\" #@param {type:\"string\"}\n", "\n", "STATE = 'string'\n", "ACCOUNT_URL = 'https://account.withings.com'\n", "CALLBACK_URI = 'https://wbsapi.withings.net/v2/oauth2'\n", "\n", "\n", "payload = {'response_type': 'code', # imposed string by the api\n", " 'client_id': CLIENT_ID,\n", " 'state': STATE,\n", " 'scope': 'user.info,user.metrics,user.activity', # see docs (https://developer.withings.com/api-reference/#operation/oauth2-authorize) for enhanced scope\n", " 'redirect_uri': CALLBACK_URI, # URL of this app\n", " #'mode': 'demo' # Use demo mode, DELETE THIS FOR REAL APP\n", "}\n", "\n", "url = f'{ACCOUNT_URL}/oauth2_user/authorize2?'\n", "\n", "for key, value in payload.items():\n", " url += f'{key}={value}&'\n", "\n", "url = url[:-1]\n", "\n", "print(url)" ], "metadata": { "cellView": "form", "id": "ev3-mu5ou88c", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "74c8edb0-db7e-49b1-b8db-ae4fd5c69621" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=d97ef704c1357d5330414a6ef6ee939062a7ef69656c51a8ab1565bc5eb4bd1e&state=string&scope=user.info,user.metrics,user.activity&redirect_uri=https://wbsapi.withings.net/v2/oauth2\n" ] } ] }, { "cell_type": "markdown", "source": [ "7. Now visit the above URL and click \"Allow this app\", and copy the URL you were redirected to into the text field below. Note that if you mess up once, you have to go through the above URL again (including clicking \"Allow this app\"). Also, the URL is only valid for 30 seconds, so be quick!" ], "metadata": { "id": "kpLZcG86vSuL" } }, { "cell_type": "code", "source": [ "#@title 7. Copy and paste the URL you were redirected to below\n", "redirect_url = \"https://wbsapi.withings.net/v2/oauth2?code=2669a4df72b43435ffda73401abc4d2813ae5dfa&state=string\" #@param {type:\"string\"}\n", "\n", "try:\n", " code = urllib.parse.parse_qs(urllib.parse.urlparse(redirect_url).query)['code'][0]\n", "except Exception as e:\n", " print(f'Caught error:\\n{e}\\n')\n", " print(\"Please copy and paste the entire URL (including https)\")\n", "\n", "params = {\n", " 'action': 'requesttoken',\n", " 'grant_type': 'authorization_code',\n", " 'client_id': CLIENT_ID,\n", " 'client_secret': CUSTOMER_SECRET,\n", " 'code': code,\n", " #'scope': 'user.info',\n", " 'redirect_uri': 'https://wbsapi.withings.net/v2/oauth2'\n", "}\n", "\n", "out = requests.get('https://wbsapi.withings.net/v2/oauth2', data=params)\n", "\n", "out = json.loads(out.text)\n", "\n", "try:\n", " access_token = out['body']['access_token']\n", "except KeyError as e:\n", " print('Took too long to paste in redirect URL. Please repeat step 7.')" ], "metadata": { "id": "oZ8kzSuBoily", "cellView": "form" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Now that we have our access token, we can begin making requests to the API! This access token will last only three hours, though, so you would need to re-do step 7 if three hours pass." ], "metadata": { "id": "JObtRZuFvuJl" } }, { "cell_type": "markdown", "source": [ "# 3. Data extraction\n", "\n", "Here, data extraction is pretty simple! We've made it possible to get all heart rate and sleep data in one function call each.\n", "\n", "If you need to customize this part further and need to dig into the code, notice that all we need to do is make a few GET requests with the right query parameters. See [the overall health data API page](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/all-available-health-data) or the [\"Measure\" endpoints specifically](https://developer.withings.com/api-reference/#operation/measurev2-getactivity) for more info." ], "metadata": { "id": "GUtc7x14ebLe" } }, { "cell_type": "code", "source": [ "#@title Enter start and end dates\n", "start_date = \"2020-03-28\" #@param {type:\"date\"}\n", "end_date = \"2022-05-28\" #@param {type:\"date\"}\n", "\n", "num_to_description = {1: 'Weight (kg)',\n", " 4: 'Height (meter)',\n", " 5: 'Fat Free Mass (kg)',\n", " 6: 'Fat Ratio (%)',\n", " 8: 'Fat Mass Weight (kg)',\n", " 9: 'Diastolic Blood Pressure (mmHg)',\n", " 10: 'Systolic Blood Pressure (mmHg)',\n", " 11: 'Heart Pulse (bpm) - only for BPM and scale devices',\n", " 12: 'Temperature (celsius)',\n", " 54: 'SP02 (%)',\n", " 71: 'Body Temperature (celsius)',\n", " 73: 'Skin Temperature (celsius)',\n", " 76: 'Muscle Mass (kg)',\n", " 77: 'Hydration (kg)',\n", " 88: 'Bone Mass (kg)',\n", " 91: 'Pulse Wave Velocity (m/s)',\n", " 123: 'VO2 max is a numerical measurement of your body’s ability to consume oxygen (ml/min/kg).',\n", " 135: 'QRS interval duration based on ECG signal',\n", " 136: 'PR interval duration based on ECG signal',\n", " 137: 'QT interval duration based on ECG signal',\n", " 138: 'Corrected QT interval duration based on ECG signal',\n", " 139: 'Atrial fibrillation result from PPG'}\n", "\n", "NUM_RETRIES = 5\n", "\n", "def fetch_all_wrapper(endpoint_url, data, headers, arr_key, parse_data=lambda x: x):\n", " # wrapper around public API that retrieves arbitrarily large # of\n", " # records, since there is a restriction of # of records per API response\n", " # NOTES:\n", " # out['body'][arr_key] is concatenated across several requests\n", " # parse_data is a function that parses the returned array\n", "\n", " cur_offset = 0\n", " arr_complete = None\n", "\n", " while True:\n", " # endpoint can be flaky if the response payload is extremely large,\n", " # so retry at most NUM_RETRIES times\n", " for i in range(NUM_RETRIES):\n", " data_args = {\n", " **data,\n", " 'offset': cur_offset\n", " }\n", " out = requests.post(endpoint_url, data=data_args, headers=headers)\n", "\n", " out = json.loads(out.text)\n", "\n", " if out['status'] == 401:\n", " raise Exception(f'request response is {out} for request {data_args} to endpoint {endpoint_url}, headers {headers}')\n", "\n", " try:\n", " arr = parse_data(out['body'][arr_key])\n", " break\n", " except KeyError:\n", " if 'body' in out.keys():\n", " raise Exception(f'got key {arr_key}, expected one of {out[\"body\"].keys()}')\n", " elif out['status'] == 2555:\n", " # when the payload is too large, this is the status code\n", " continue\n", " else:\n", " raise Exception(f'request response is {out} for request {data_args} to endpoint {endpoint_url}, headers {headers}')\n", "\n", " # for example, https://developer.withings.com/api-reference/#operation/measurev2-getactivity\n", " # vs. https://developer.withings.com/api-reference/#operation/measure-getmeas\n", " if type(arr) == type({}):\n", " if arr_complete is None:\n", " arr_complete = dict()\n", "\n", " arr_complete.update(arr)\n", "\n", " elif type(arr) == type([]):\n", " if arr_complete is None:\n", " arr_complete = []\n", "\n", " arr_complete += arr\n", "\n", " # continue if there's still more to get\n", " if 'more' in out['body'].keys() and out['body']['more'] == 1:\n", " cur_offset = out['body']['offset']\n", " else:\n", " break\n", "\n", " # replace with concatenated version\n", " out['body'][arr_key] = arr_complete\n", "\n", " return out\n", "\n", "def fetch_all_heart_rate(start='2020-03-10', end='2022-05-28'):\n", " # get all dates heart rate was collected for\n", " out = fetch_all_wrapper('https://wbsapi.withings.net/v2/measure', {\n", " 'action': 'getactivity',\n", " 'startdateymd': start,\n", " 'enddateymd': end,\n", " 'data_fields': 'hr_average'\n", " }, {'Authorization': f'Bearer {access_token}'},\n", " arr_key='activities')\n", "\n", " dates = [act['date'] for act in out['body']['activities']]\n", "\n", " # now for each date get the heart rate data and store as list of dicts\n", " dict_list = []\n", " for date in tqdm(dates):\n", " out = fetch_all_wrapper('https://wbsapi.withings.net/v2/measure', {\n", " 'action': 'getintradayactivity',\n", " 'startdate': int(datetime.strptime(date, '%Y-%m-%d').timestamp()),\n", " 'enddate': int(datetime.strptime(date, '%Y-%m-%d').timestamp()) + 24 * 3600,\n", " 'data_fields': 'heart_rate'\n", " }, {'Authorization': f'Bearer {access_token}'},\n", " arr_key='series')\n", "\n", " dict_list += [{'datetime': datetime.fromtimestamp(int(k)), **v} for k,v in out['body']['series'].items()]\n", "\n", " df = pd.DataFrame.from_dict(dict_list)\n", "\n", " return df\n", "\n", "\n", "def fetch_all_sleeps(start='2020-03-10', end='2022-05-28'):\n", " out = fetch_all_wrapper('https://wbsapi.withings.net/v2/sleep', {\n", " 'action': 'getsummary',\n", " 'startdateymd': '2020-07-01',\n", " 'enddateymd': '2022-07-01',\n", " 'data_fields': 'nb_rem_episodes,sleep_efficiency,sleep_latency,total_sleep_time,total_timeinbed,wakeup_latency,waso,asleepduration,deepsleepduration,durationtosleep,durationtowakeup,hr_average,hr_max,hr_min,lightsleepduration,night_events,out_of_bed_count,remsleepduration,rr_average,rr_max,rr_min,sleep_score,snoring,snoringepisodecount,wakeupcount,wakeupduration'\n", " }, {'Authorization': f'Bearer {access_token}'}, arr_key='series')\n", "\n", " df = pd.DataFrame.from_dict(out['body']['series'])\n", "\n", " return df" ], "metadata": { "id": "oIhFUppKKwBC", "cellView": "form" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "hr_df = fetch_all_heart_rate(start=start_date, end=end_date)\n", "sleeps_df = fetch_all_sleeps(start=start_date, end=end_date)" ], "metadata": { "id": "b22VGbw0KIms", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "45295260-7e7e-4de1-d28d-7fa4722f6dfa" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ "100%|██████████| 18/18 [00:13<00:00, 1.30it/s]\n" ] } ] }, { "cell_type": "code", "source": [], "metadata": { "id": "PgBgJi5o8PgT" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "hr_df" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "l4PylUanOjFb", "outputId": "f193dae8-007d-4375-fe48-87f81769aa76" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " datetime heart_rate model model_id deviceid\n", "0 2022-05-25 14:46:46 66 None 1058 None\n", "1 2022-05-25 14:48:07 71 None 1058 None\n", "2 2022-05-25 14:53:27 88 None 1058 None\n", "3 2022-05-25 14:54:17 96 None 1058 None\n", "4 2022-05-25 14:56:41 87 None 1058 None\n", "... ... ... ... ... ...\n", "59641 2022-03-05 23:26:56 67 None 1058 None\n", "59642 2022-03-05 23:30:17 68 None 1058 None\n", "59643 2022-03-05 23:36:31 85 None 1058 None\n", "59644 2022-03-05 23:49:32 87 None 1058 None\n", "59645 2022-03-05 23:51:39 73 None 1058 None\n", "\n", "[59646 rows x 5 columns]" ], "text/html": [ "\n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
datetimeheart_ratemodelmodel_iddeviceid
02022-05-25 14:46:4666None1058None
12022-05-25 14:48:0771None1058None
22022-05-25 14:53:2788None1058None
32022-05-25 14:54:1796None1058None
42022-05-25 14:56:4187None1058None
..................
596412022-03-05 23:26:5667None1058None
596422022-03-05 23:30:1768None1058None
596432022-03-05 23:36:3185None1058None
596442022-03-05 23:49:3287None1058None
596452022-03-05 23:51:3973None1058None
\n", "

59646 rows × 5 columns

\n", "
\n", " \n", " \n", " \n", "\n", " \n", "
\n", "
\n", " " ] }, "metadata": {}, "execution_count": 8 } ] }, { "cell_type": "code", "source": [ "data = np.unique([(int(datetime.strftime(dt, '%H')) - 8) % 24 for dt in hr_df.datetime], return_counts=True)" ], "metadata": { "id": "Yfq41iyrxU4v" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "plt.bar(data[0], data[1])" ], "metadata": { "id": "X4TsM9ctxv3w", "outputId": "8f26442c-1785-47b4-aa64-989a17c2d0c0", "colab": { "base_uri": "https://localhost:8080/", "height": 282 } }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 39 }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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\n" }, "metadata": { "needs_background": "light" } } ] }, { "cell_type": "code", "source": [ "set(hr_df.deviceid)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ZVN5eZn3w7f4", "outputId": "e020cc63-6f52-4d0d-bb6e-00080e3e5640" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{None}" ] }, "metadata": {}, "execution_count": 26 } ] }, { "cell_type": "code", "source": [ "set(hr_df.model_id)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "34LonxJBwBOA", "outputId": "6db22464-fed2-4286-cdba-1e017e291ac8" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{1058}" ] }, "metadata": {}, "execution_count": 24 } ] }, { "cell_type": "code", "source": [ "set(hr_df.model)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "6YRDbrWm6ScK", "outputId": "af095fbd-def1-4885-8cd0-0ca9cdcfecf2" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{None}" ] }, "metadata": {}, "execution_count": 40 } ] }, { "cell_type": "code", "source": [ "access_token" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 35 }, "id": "oVU3PgxYvx21", "outputId": "e97e8374-5833-414d-9917-c31d219c9efc" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'fafdab17e72f36fb210d40f75ab5c4995eb96a77'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 18 } ] }, { "cell_type": "code", "source": [ "!curl --header \"Authorization: Bearer fafdab17e72f36fb210d40f75ab5c4995eb96a77\" --data \"action=getdevice\" 'https://wbsapi.withings.net/v2/user '" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "aM8TQPPsvxTL", "outputId": "259260c7-f823-49ea-beb1-e92345f85183" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "{\"status\":0,\"body\":{\"devices\":[{\"type\":\"Sleep Monitor\",\"battery\":\"high\",\"model\":\"Aura Sensor V2\",\"model_id\":63,\"timezone\":\"America\\/Los_Angeles\",\"last_session_date\":1654140010,\"deviceid\":\"4f15662709a827746ffd49b589ee37206e23e9f0\",\"hash_deviceid\":\"4f15662709a827746ffd49b589ee37206e23e9f0\"}]}}" ] } ] }, { "cell_type": "code", "source": [ "json.loads('{\"status\":0,\"body\":{\"devices\":[{\"type\":\"Sleep Monitor\",\"battery\":\"high\",\"model\":\"Aura Sensor V2\",\"model_id\":63,\"timezone\":\"America\\/Los_Angeles\",\"last_session_date\":1654140010,\"deviceid\":\"4f15662709a827746ffd49b589ee37206e23e9f0\",\"hash_deviceid\":\"4f15662709a827746ffd49b589ee37206e23e9f0\"}]}}')['body']['devices']" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "w3pshfYHu_1Q", "outputId": "4a17ac67-0255-4b5c-e19f-b030061a2256" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[{'battery': 'high',\n", " 'deviceid': '4f15662709a827746ffd49b589ee37206e23e9f0',\n", " 'hash_deviceid': '4f15662709a827746ffd49b589ee37206e23e9f0',\n", " 'last_session_date': 1654140010,\n", " 'model': 'Aura Sensor V2',\n", " 'model_id': 63,\n", " 'timezone': 'America/Los_Angeles',\n", " 'type': 'Sleep Monitor'}]" ] }, "metadata": {}, "execution_count": 22 } ] }, { "cell_type": "code", "source": [ "set(hr_df.model_id)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "kcN1Yg0hu64G", "outputId": "d73166c4-f82e-48aa-93a7-a4aeae47296b" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{1058}" ] }, "metadata": {}, "execution_count": 15 } ] }, { "cell_type": "code", "source": [ "set(np.array(hr_df.model))" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "HHlb_auvuyzS", "outputId": "5f46f297-3b05-4959-94dd-fedac8568cca" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{None}" ] }, "metadata": {}, "execution_count": 14 } ] }, { "cell_type": "code", "source": [ "[x for x in sleeps_df.data]" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "KYG7TGdGw_zp", "outputId": "e1fd5b70-1e15-40c2-efc4-54442ce91a01" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[{'deepsleepduration': 0,\n", " 'durationtosleep': 2820,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 61,\n", " 'hr_max': 69,\n", " 'hr_min': 51,\n", " 'lightsleepduration': 3660,\n", " 'nb_rem_episodes': 0,\n", " 'night_events': '{\"1\":[0],\"2\":[2820],\"3\":[6480],\"4\":[6480]}',\n", " 'out_of_bed_count': 0,\n", " 'remsleepduration': 0,\n", " 'rr_average': 15,\n", " 'rr_max': 19,\n", " 'rr_min': 12,\n", " 'sleep_efficiency': 0.56,\n", " 'sleep_latency': 2820,\n", " 'sleep_score': 20,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 3660,\n", " 'total_timeinbed': 6480,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 0,\n", " 'wakeupduration': 2820,\n", " 'waso': 0},\n", " {'deepsleepduration': 1620,\n", " 'durationtosleep': 1920,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 67,\n", " 'hr_max': 79,\n", " 'hr_min': 53,\n", " 'lightsleepduration': 6300,\n", " 'nb_rem_episodes': 2,\n", " 'night_events': '{\"1\":[0,14760,1440,1980,840],\"2\":[1920,13800,4800],\"3\":[13200,2700,4920],\"4\":[13200,2700,600,2460,1860]}',\n", " 'out_of_bed_count': 4,\n", " 'remsleepduration': 3840,\n", " 'rr_average': 15,\n", " 'rr_max': 23,\n", " 'rr_min': 12,\n", " 'sleep_efficiency': 0.68,\n", " 'sleep_latency': 1920,\n", " 'sleep_score': 20,\n", " 'snoring': 1140,\n", " 'snoringepisodecount': 4,\n", " 'total_sleep_time': 11760,\n", " 'total_timeinbed': 17220,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 2,\n", " 'wakeupduration': 5460,\n", " 'waso': 7140},\n", " {'deepsleepduration': 2940,\n", " 'durationtosleep': 1800,\n", " 'durationtowakeup': 1020,\n", " 'hr_average': 66,\n", " 'hr_max': 81,\n", " 'hr_min': 51,\n", " 'lightsleepduration': 12420,\n", " 'nb_rem_episodes': 4,\n", " 'night_events': '{\"1\":[0,23040],\"2\":[1800,21420],\"3\":[22620,2640],\"4\":[22620,3660]}',\n", " 'out_of_bed_count': 1,\n", " 'remsleepduration': 7500,\n", " 'rr_average': 16,\n", " 'rr_max': 22,\n", " 'rr_min': 10,\n", " 'sleep_efficiency': 0.88,\n", " 'sleep_latency': 1800,\n", " 'sleep_score': 62,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 22860,\n", " 'total_timeinbed': 25860,\n", " 'wakeup_latency': 1020,\n", " 'wakeupcount': 1,\n", " 'wakeupduration': 3000,\n", " 'waso': 600},\n", " {'deepsleepduration': 4200,\n", " 'durationtosleep': 5220,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 73,\n", " 'hr_max': 88,\n", " 'hr_min': 54,\n", " 'lightsleepduration': 12600,\n", " 'nb_rem_episodes': 7,\n", " 'night_events': '{\"1\":[0,20400,660,360,900,1920,9240],\"2\":[5220,15180,1020,900,1920,9300],\"3\":[20220,480,1380,1740,3960,6840],\"4\":[20220,480,600,780,1740,9600,1200]}',\n", " 'out_of_bed_count': 6,\n", " 'remsleepduration': 5280,\n", " 'rr_average': 16,\n", " 'rr_max': 27,\n", " 'rr_min': 10,\n", " 'sleep_efficiency': 0.66,\n", " 'sleep_latency': 5220,\n", " 'sleep_score': 53,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 22080,\n", " 'total_timeinbed': 33240,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 5,\n", " 'wakeupduration': 11160,\n", " 'waso': 7320},\n", " {'deepsleepduration': 0,\n", " 'durationtosleep': 1080,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 66,\n", " 'hr_max': 87,\n", " 'hr_min': 54,\n", " 'lightsleepduration': 19560,\n", " 'nb_rem_episodes': 6,\n", " 'night_events': '{\"1\":[0,25740,780],\"2\":[1080,25440,1920],\"3\":[25380,1680,2280],\"4\":[25380,900,3060]}',\n", " 'out_of_bed_count': 2,\n", " 'remsleepduration': 6180,\n", " 'rr_average': 15,\n", " 'rr_max': 25,\n", " 'rr_min': 11,\n", " 'sleep_efficiency': 0.9,\n", " 'sleep_latency': 1080,\n", " 'sleep_score': 77,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 25740,\n", " 'total_timeinbed': 28740,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 2,\n", " 'wakeupduration': 3000,\n", " 'waso': 2520},\n", " {'deepsleepduration': 840,\n", " 'durationtosleep': 1200,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 72,\n", " 'hr_max': 79,\n", " 'hr_min': 66,\n", " 'lightsleepduration': 4200,\n", " 'nb_rem_episodes': 1,\n", " 'night_events': '{\"1\":[0,6960],\"2\":[1200,6540],\"3\":[5820,2820],\"4\":[5820,2820]}',\n", " 'out_of_bed_count': 1,\n", " 'remsleepduration': 480,\n", " 'rr_average': 15,\n", " 'rr_max': 18,\n", " 'rr_min': 13,\n", " 'sleep_efficiency': 0.74,\n", " 'sleep_latency': 1200,\n", " 'sleep_score': 20,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 5520,\n", " 'total_timeinbed': 7500,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 1,\n", " 'wakeupduration': 1980,\n", " 'waso': 1920},\n", " {'deepsleepduration': 3720,\n", " 'durationtosleep': 840,\n", " 'durationtowakeup': 0,\n", " 'hr_average': 69,\n", " 'hr_max': 84,\n", " 'hr_min': 54,\n", " 'lightsleepduration': 6840,\n", " 'nb_rem_episodes': 2,\n", " 'night_events': '{\"1\":[0],\"2\":[840],\"3\":[15000],\"4\":[15000]}',\n", " 'out_of_bed_count': 0,\n", " 'remsleepduration': 3600,\n", " 'rr_average': 15,\n", " 'rr_max': 19,\n", " 'rr_min': 12,\n", " 'sleep_efficiency': 0.94,\n", " 'sleep_latency': 840,\n", " 'sleep_score': 21,\n", " 'snoring': 0,\n", " 'snoringepisodecount': 0,\n", " 'total_sleep_time': 14160,\n", " 'total_timeinbed': 15000,\n", " 'wakeup_latency': 0,\n", " 'wakeupcount': 0,\n", " 'wakeupduration': 840,\n", " 'waso': 0}]" ] }, "metadata": {}, "execution_count": 29 } ] }, { "cell_type": "code", "source": [ "sleeps_df" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 434 }, "id": "gy07DMHoYrZv", "outputId": "e4702ad7-ac6d-4e7e-86e5-7a08247e44c0" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " id timezone model model_id \\\n", "0 2773588367 America/Los_Angeles 32 63 \n", "1 2775493636 America/Los_Angeles 32 63 \n", "2 2777458259 America/Los_Angeles 32 63 \n", "3 2779030725 America/Los_Angeles 32 63 \n", "4 2780933603 America/Los_Angeles 32 63 \n", "5 2782467827 America/Los_Angeles 32 63 \n", "6 2784302567 America/Los_Angeles 32 63 \n", "\n", " hash_deviceid startdate enddate \\\n", "0 4f15662709a827746ffd49b589ee37206e23e9f0 1653557580 1653564060 \n", "1 4f15662709a827746ffd49b589ee37206e23e9f0 1653640260 1653661080 \n", "2 4f15662709a827746ffd49b589ee37206e23e9f0 1653724920 1653751200 \n", "3 4f15662709a827746ffd49b589ee37206e23e9f0 1653806400 1653841020 \n", "4 4f15662709a827746ffd49b589ee37206e23e9f0 1653891420 1653920760 \n", "5 4f15662709a827746ffd49b589ee37206e23e9f0 1653984480 1653993120 \n", "6 4f15662709a827746ffd49b589ee37206e23e9f0 1654060980 1654075980 \n", "\n", " date data created \\\n", "0 2022-05-26 {'wakeupduration': 2820, 'wakeupcount': 0, 'du... 1653564132 \n", "1 2022-05-27 {'wakeupduration': 5460, 'wakeupcount': 2, 'du... 1653653590 \n", "2 2022-05-28 {'wakeupduration': 3000, 'wakeupcount': 1, 'du... 1653747687 \n", "3 2022-05-29 {'wakeupduration': 11160, 'wakeupcount': 5, 'd... 1653826737 \n", "4 2022-05-30 {'wakeupduration': 3000, 'wakeupcount': 2, 'du... 1653916917 \n", "5 2022-05-31 {'wakeupduration': 1980, 'wakeupcount': 1, 'du... 1653990372 \n", "6 2022-06-01 {'wakeupduration': 840, 'wakeupcount': 0, 'dur... 1654076118 \n", "\n", " modified \n", "0 1653572591 \n", "1 1653668471 \n", "2 1653758589 \n", "3 1653848443 \n", "4 1653928152 \n", "5 1654000512 \n", "6 1654083370 " ], "text/html": [ "\n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idtimezonemodelmodel_idhash_deviceidstartdateenddatedatedatacreatedmodified
02773588367America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165355758016535640602022-05-26{'wakeupduration': 2820, 'wakeupcount': 0, 'du...16535641321653572591
12775493636America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165364026016536610802022-05-27{'wakeupduration': 5460, 'wakeupcount': 2, 'du...16536535901653668471
22777458259America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165372492016537512002022-05-28{'wakeupduration': 3000, 'wakeupcount': 1, 'du...16537476871653758589
32779030725America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165380640016538410202022-05-29{'wakeupduration': 11160, 'wakeupcount': 5, 'd...16538267371653848443
42780933603America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165389142016539207602022-05-30{'wakeupduration': 3000, 'wakeupcount': 2, 'du...16539169171653928152
52782467827America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165398448016539931202022-05-31{'wakeupduration': 1980, 'wakeupcount': 1, 'du...16539903721654000512
62784302567America/Los_Angeles32634f15662709a827746ffd49b589ee37206e23e9f0165406098016540759802022-06-01{'wakeupduration': 840, 'wakeupcount': 0, 'dur...16540761181654083370
\n", "
\n", " \n", " \n", " \n", "\n", " \n", "
\n", "
\n", " " ] }, "metadata": {}, "execution_count": 41 } ] }, { "cell_type": "markdown", "source": [ "Now that we've got dataframes with data, we can see the data they have. In particular, we have a ton of measurements from a non-ScanWatch device, so we'll get rid of those. Note that it is theoretically possible to have another ScanWatch device (and maybe the model name will be `ScanWatch2`, or something different), but we were unable to test this, so if you switch between devices you might want to be careful here." ], "metadata": { "id": "CyvkzE-mSfuR" } }, { "cell_type": "code", "source": [ "hr_df = hr_df.drop(np.where(hr_df.model != 'ScanWatch')[0])\n", "hr_df" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "SNpMhIa7u4Cj", "outputId": "e1023574-b241-4c46-8724-9ce661822da5" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " datetime heart_rate model model_id \\\n", "0 2022-05-17 03:37:55 84 ScanWatch 93 \n", "1 2022-05-17 03:38:25 96 ScanWatch 93 \n", "2 2022-05-17 03:38:57 100 ScanWatch 93 \n", "3 2022-05-17 03:43:43 96 ScanWatch 93 \n", "4 2022-05-17 03:53:08 103 ScanWatch 93 \n", "... ... ... ... ... \n", "2197 2022-05-28 22:53:38 69 ScanWatch 93 \n", "2198 2022-05-28 23:03:13 71 ScanWatch 93 \n", "2199 2022-05-28 23:13:11 80 ScanWatch 93 \n", "2200 2022-05-28 23:43:32 94 ScanWatch 93 \n", "2201 2022-05-28 23:54:01 95 ScanWatch 93 \n", "\n", " deviceid \n", "0 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "1 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "2 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "3 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "4 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "... ... \n", "2197 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "2198 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "2199 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "2200 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "2201 4518d7c4c7ba897fc5c45ed81933452a061606cd \n", "\n", "[2202 rows x 5 columns]" ], "text/html": [ "\n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
datetimeheart_ratemodelmodel_iddeviceid
02022-05-17 03:37:5584ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
12022-05-17 03:38:2596ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
22022-05-17 03:38:57100ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
32022-05-17 03:43:4396ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
42022-05-17 03:53:08103ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
..................
21972022-05-28 22:53:3869ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
21982022-05-28 23:03:1371ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
21992022-05-28 23:13:1180ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
22002022-05-28 23:43:3294ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
22012022-05-28 23:54:0195ScanWatch934518d7c4c7ba897fc5c45ed81933452a061606cd
\n", "

2202 rows × 5 columns

\n", "
\n", " \n", " \n", " \n", "\n", " \n", "
\n", "
\n", " " ] }, "metadata": {}, "execution_count": 404 } ] }, { "cell_type": "markdown", "source": [ "And now we have just 2k rows!" ], "metadata": { "id": "oa_socCEve3m" } }, { "cell_type": "markdown", "source": [ "# 4. Data Visualization\n", "\n", "In this section, you'll expect to see a couple plots from the Health Mate mobile app reproduced.\n", "\n", "# 4.1: Heart rate single day\n", "First, we'll reproduce the below plot you can find in the Health Mate mobile app that displays heart rate data over the course of a single day.\n", "\n", "\n", "\n", "*Above is a plot taken from the official Withings mobile app.*" ], "metadata": { "id": "V-9IFYQAk9JZ" } }, { "cell_type": "code", "source": [ "#@title Enter date\n", "date = \"2022-05-20\" #@param {type:\"date\"}\n", "from matplotlib.ticker import FormatStrFormatter\n", "import matplotlib.dates as dates\n", "import matplotlib.transforms\n", "\n", "from dateutil import tz\n", "from scipy.interpolate import make_interp_spline\n", "from datetime import timedelta\n", "\n", "# measurements are taken every 10 minutes, displayed on app every 30\n", "HEART_RATE_RECORDING_LENGTH = 30 * 60\n", "\n", "with plt.style.context('dark_background'):\n", " # get the start and end times as timestamps by using the datetime library\n", " start_ts = datetime.strptime(date + ' 00:00:00-07:00', '%Y-%m-%d %H:%M:%S%z').timestamp()\n", " #end_ts = datetime.strptime(date + ' 23:59:59-07:00', '%Y-%m-%d %H:%M:%S%z').timestamp()\n", " end_ts = start_ts + 24 * 3600 + 30 * 60 # 00:30 the next day\n", " #end_ts = datetime.strptime('2022-05-25 00:30:00-07:00', '%Y-%m-%d %H:%M:%S%z').timestamp()\n", "\n", " # now find the indices in hr_df.timestamp that match as closely as possible\n", " start_idx = np.argmin(np.abs(hr_df.datetime.apply(lambda x: x.timestamp()) - start_ts))\n", " end_idx = np.argmin(np.abs(hr_df.datetime.apply(lambda x: x.timestamp()) - end_ts))\n", "\n", " x = hr_df.datetime.iloc[start_idx:end_idx]\n", " y = hr_df.heart_rate.iloc[start_idx:end_idx]\n", "\n", " # make it not as bumpy with gaussian filter. note that this does not reproduce\n", " # the curve exactly, as I'm not sure what smoothing algorithm they used...\n", " y = gaussian_filter(y, sigma=3)\n", "\n", " fig = plt.figure(figsize=(18,6), facecolor='black')\n", "\n", " x_timestamp = np.array([x_.timestamp() for x_ in x])\n", "\n", " # get the gaps. we include [6] as well because when you do np.diff,\n", " # it actually leaves out exactly one element\n", " differences = np.concatenate((np.diff(x_timestamp), [60 * 10]))\n", "\n", " # interpret a gap (i.e. when a user takes off the device for some prolonged\n", " # period of time) as any two measurements that are taken more than\n", " # 6 * 2 = 12 seconds apart, to account for minor variations around 6s\n", " gap_idxes = np.where(differences > HEART_RATE_RECORDING_LENGTH * 2)[0]\n", "\n", " # get the sleeps\n", " sleep_idxes = []\n", " for lower, upper in zip(sleeps_df.startdate, sleeps_df.enddate):\n", " if lower < start_ts or upper > end_ts:\n", " continue\n", " # get the location in the timestamp array that is closest to `lower`\n", " lower_idx = np.argmin(np.abs((x_timestamp - lower) - 0))\n", " # get the location in the timestamp array that is closest to `upper`\n", " upper_idx = np.argmin(np.abs((x_timestamp - upper) - 0))\n", "\n", " sleep_idxes.append((lower_idx, upper_idx))\n", "\n", " # first, we just plot the entire thing\n", " plt.plot(x, y, linewidth=3, color='#85a6f7')\n", "\n", " # now we overlay sleeps\n", " for sleep_start, sleep_end in sleep_idxes:\n", " plt.plot(x[sleep_start:sleep_end], y[sleep_start:sleep_end], linewidth=3, color='#3E414C')\n", "\n", " plt.axvline(x=x.iloc[sleep_start], linestyle='--', linewidth=1.5, color='#2F303A')\n", "\n", " plt.axvline(x=x.iloc[sleep_end], linestyle='--', linewidth=1.5, color='#2F303A')\n", "\n", " # now we overlay gaps by overlaying with white\n", " for gap_idx in gap_idxes:\n", " plt.plot(x[gap_idx:gap_idx+2], y[gap_idx:gap_idx+2], linewidth=5, color='black')\n", "\n", " plt.ylim(40, 180)\n", " plt.xlim(x.iloc[0], x.iloc[-1] - pd.Timedelta(minutes=30))\n", "\n", " datetimes = [\n", " datetime.strptime(date + ' 00:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 04:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 08:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 12:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 16:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 20:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime(date + ' 00:00:00-0700', '%Y-%m-%d %H:%M:%S%z') + timedelta(days=1)\n", " ]\n", "\n", " plt.xticks(ticks=datetimes, labels=['12AM', '4AM', '8AM', '12PM', '4PM', '8PM', '12AM'])\n", "\n", " # get the y-axis ticks to appear on the right\n", " plt.gca().yaxis.tick_right()\n", "\n", " #plt.gca().grid(axis='x', color='#2F303A')\n", " plt.gca().grid(axis='y', color='#2F303A', which='major')\n", "\n", " # hide x-axis and make the xtick labels 16 size\n", " plt.tick_params(\n", " axis='x', # changes apply to the x-axis\n", " which='both', # both major and minor ticks are affected\n", " bottom=False, # ticks along the bottom edge are off\n", " labelsize=16,\n", " labelcolor='silver'\n", " )\n", "\n", " # hide y-axis and make the ytick labels 16 size\n", " plt.tick_params(\n", " axis='y', # changes apply to the x-axis\n", " which='both', # both major and minor ticks are affected\n", " right=False, # ticks along the bottom edge are off\n", " labelsize=16,\n", " labelcolor='silver'\n", " )\n", "\n", " # add offset to y-axis tick labels to make them appear above gridlines\n", " # instead of to the side\n", " # https://stackoverflow.com/questions/28615887/how-to-move-a-tick-label-in-matplotlib\n", " dx = -50/72.; dy = 15/72. \n", " offset = matplotlib.transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)\n", " for label in plt.gca().yaxis.get_majorticklabels():\n", " label.set_transform(label.get_transform() + offset)\n", "\n", " # turn off all borders\n", " plt.gca().spines['left'].set_visible(False)\n", " plt.gca().spines['right'].set_visible(False)\n", " plt.gca().spines['top'].set_visible(False)\n", " plt.gca().spines['bottom'].set_visible(False)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 383 }, "cellView": "form", "id": "yuhua2DjadxQ", "outputId": "a46e5b15-8b5f-43f3-b476-b9fca8080a44" }, "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "*Above is a plot we created ourselves!*\n", "\n", "## 4.2: Sleeps over the course of a week\n", "\n", "Now let's try to reproduce the plot below. We can do this by taking creative advantage of matplotlib's boxplot functionality.\n", "\n", "\n", "\n", "*The above plot is taken directly from the mobile app.*" ], "metadata": { "id": "JvSoAF9JntAl" } }, { "cell_type": "code", "source": [ "#@title Insert start of week (must be a Monday) and timezone name\n", "\n", "week_start = \"2022-05-23\" #@param {type:\"date\"}\n", "timezone_name = \"America/Los_Angeles\" #@param {type:\"string\"}\n", "\n", "from matplotlib.ticker import FormatStrFormatter\n", "import matplotlib.dates as dates\n", "import matplotlib.transforms\n", "import matplotlib.patches as patches\n", "from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,\n", " AutoMinorLocator)\n", "\n", "from dateutil import tz\n", "from scipy.interpolate import make_interp_spline\n", "\n", "from datetime import timezone\n", "import pytz\n", "\n", "def timestamp_to_hour_min(timestamp, tz_name='America/Los_Angeles'):\n", " # convert timezone to '%H:%M' in local timezone\n", "\n", " local_tz = pytz.timezone(tz_name)\n", " return datetime.fromtimestamp(int(timestamp)).replace(tzinfo=timezone.utc).astimezone(local_tz).strftime('%H:%M')\n", "\n", "def hour_min_to_vert_pos(hour_min):\n", " num_mins = (int(hour_min.split(':')[0]) * 60 + int(hour_min.split(':')[1]))\n", " vert_dist = (16 * 60 - num_mins) / (16 * 60)\n", "\n", " return vert_dist\n", "\n", "# measurements are taken every 10 minutes, displayed on app every 30\n", "HEART_RATE_RECORDING_LENGTH = 30 * 60\n", "\n", "NUM_DAYS = 7\n", "RECT_WIDTH = 0.02\n", "RECTS_START = 0.1\n", "RECTS_END = 0.9\n", "\n", "# add on timezone\n", "timezone_offset = datetime.now(pytz.timezone(timezone_name)).strftime('%z')\n", "week_start = week_start + timezone_offset\n", "week_start_ts = datetime.strptime(week_start, '%Y-%m-%d%z').timestamp()\n", "\n", "with plt.style.context('dark_background'):\n", " fig, ax = plt.subplots(figsize=(18,6), facecolor='black')\n", "\n", " for day_num in range(NUM_DAYS):\n", " rect_center_pos = RECTS_START + day_num / (NUM_DAYS-1) * (RECTS_END - RECTS_START)\n", " # zorder to ensure it is drawn behind rect\n", " plt.axvline(x=rect_center_pos, linestyle='--', linewidth=1.5, color='#2F303A', zorder=0)\n", "\n", " # rounded rectangles\n", " # https://stackoverflow.com/questions/58425392/bar-chart-with-rounded-corners-in-matplotlib\n", "\n", " day_ts = week_start_ts + day_num * 24 * 3600\n", "\n", " row = sleeps_df[np.logical_and(sleeps_df.startdate > day_ts, sleeps_df.enddate < day_ts + 24 * 3600)]\n", "\n", " # get the one with the longest sleep, this is how withings does it\n", " # source: https://support.withings.com/hc/en-us/community/posts/360026177173-Naps-don-t-count-for-sleep-tracking\n", " try:\n", " row = row.iloc[np.argmax(row.enddate - row.startdate)]\n", " except ValueError:\n", " continue\n", "\n", " start, end = timestamp_to_hour_min(row.startdate, tz_name=timezone_name), timestamp_to_hour_min(row.enddate, tz_name=timezone_name)\n", "\n", " top_pos, bottom_pos = hour_min_to_vert_pos(start), hour_min_to_vert_pos(end)\n", "\n", " if row.data['sleep_score'] < 50:\n", " color = '#ff7455'\n", " elif row.data['sleep_score'] < 75:\n", " color = '#FEDF00'\n", " else:\n", " color = '#24ffa4'\n", "\n", " rect = patches.FancyBboxPatch((rect_center_pos - RECT_WIDTH / 2, bottom_pos),\n", " RECT_WIDTH, top_pos - bottom_pos,\n", " boxstyle=\"round,pad=-0.0040,rounding_size=0.015\",\n", " linewidth=3, fc=color, ec='none')\n", "\n", " ax.add_patch(rect)\n", "\n", " # get the sleeps\n", " sleep_idxes = []\n", " for lower, upper in zip(sleeps_df.startdate, sleeps_df.enddate):\n", " #if sleeps_df.startdate\n", " sleep_idxes.append((lower_idx, upper_idx))\n", "\n", " # now we overlay sleeps\n", " for sleep_start, sleep_end in sleep_idxes:\n", " continue\n", "\n", " datetimes = [\n", " datetime.strptime('2022-05-24 00:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-24 04:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-24 08:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-24 12:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-24 16:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-24 20:00:00-0700', '%Y-%m-%d %H:%M:%S%z'),\n", " datetime.strptime('2022-05-25 00:00:00-0700', '%Y-%m-%d %H:%M:%S%z')\n", " ]\n", "\n", " #plt.xticks(ticks=datetimes, labels=['12AM', '4AM', '8AM', '12PM', '4PM', '8PM', '12AM'])\n", " plt.xticks(ticks=np.linspace(0.1,0.9,NUM_DAYS), labels=['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'])\n", " plt.yticks(ticks=np.linspace(0, 1, 5), labels=['4PM', '12PM', '8AM', '4AM', '12AM'])\n", "\n", " # get the y-axis ticks to appear on the right\n", " plt.gca().yaxis.tick_right()\n", "\n", " #plt.gca().grid(axis='x', color='#2F303A')\n", " #plt.gca().yaxis.set_minor_locator(MultipleLocator(5*4))\n", " #plt.gca().yaxis.set_minor_locator(MultipleLocator(5 * 8))\n", " plt.gca().yaxis.set_minor_locator(AutoMinorLocator(n=2))\n", " plt.gca().grid(axis='y', color='#2F303A', which='both')\n", "\n", " # hide x-axis and make the xtick labels 16 size\n", " plt.tick_params(\n", " axis='x', # changes apply to the x-axis\n", " which='both', # both major and minor ticks are affected\n", " bottom=False, # ticks along the bottom edge are off\n", " labelsize=16,\n", " labelcolor='silver'\n", " )\n", "\n", " # hide y-axis and make the ytick labels 16 size\n", " plt.tick_params(\n", " axis='y', # changes apply to the x-axis\n", " which='both', # both major and minor ticks are affected\n", " right=False, # ticks along the bottom edge are off\n", " labelsize=16,\n", " labelcolor='silver'\n", " )\n", "\n", " # add offset to y-axis tick labels to make them appear above gridlines\n", " # instead of to the side\n", " # https://stackoverflow.com/questions/28615887/how-to-move-a-tick-label-in-matplotlib\n", " dx = -50/72.; dy = 15/72. \n", " offset = matplotlib.transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)\n", " for label in plt.gca().yaxis.get_majorticklabels():\n", " label.set_transform(label.get_transform() + offset)\n", "\n", " start_day_fmted = datetime.strftime(datetime.strptime(week_start, '%Y-%m-%d%z'), '%b %d')\n", " end_day_fmted = datetime.strftime(datetime.strptime(week_start, '%Y-%m-%d%z') + timedelta(days=7), '%b %d')\n", " plt.title(f'{start_day_fmted} - {end_day_fmted}', size=20, pad=40)\n", "\n", " plt.gca().set_axisbelow(True)\n", " # turn off all borders\n", " plt.gca().spines['left'].set_visible(False)\n", " plt.gca().spines['right'].set_visible(False)\n", " plt.gca().spines['top'].set_visible(False)\n", " plt.gca().spines['bottom'].set_visible(False)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 432 }, "cellView": "form", "id": "rQvaa5iHyNpT", "outputId": "88de4b12-8e90-44d9-ab7d-2e1efd1d9233" }, "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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HN4PAHCkkpISvfXWW+rTp4/Kysr05ptvtjh/wIABkk7deq2urq7RtlGjRqlLly6Wz6utrdWrr76q5ORkfetb39Kdd96p0tJSvf76622u+cEHH9SsWbO0ceNGjRs3TseOHWvza0j/Ohze1yv+n02vvvqq6uvr1adPH82dO1f19fUtzh8wYIAKCwu1YsWKJttauvXdH//4R5WXl+uuu+7SLbfcotjYWL322mutfj1fWX0Pd+/erf3792vw4MHq169fk+dMnDhRkrR8+fKzUgMAAOeK7X95YDAYDAajteG9p1+S6dOnj/n2t79trrjiikaPW+3pP32l+bfeeqvR3ISEBPPZZ59Z7qU+8+vU1NSY/Px8Y4xpdAV7X8cvfvELY4wx69atM7Gxsa3Ov+iiiyz3anft2tUsW7bMGNP2q9i3dVjt6ZdkJkyYYL797W+buLi4hsea29P/3nvvGWOMGTFiRKPHT98xobk9/ZLMK6+80vC5e9+twZdx6aWXWj6empra8FmOHz++0bbTe/MXLFhgXC5Xw+Onjwz4/PPPGz3OYDAYDIa/j2ABAOBQ+fn5ys/P92nuunXrtHr1at14441as2aNVq9erZ49e2rixInavn27Dh482OLXWbJkib797W9LOnVhuLb493//dz3xxBOqra3VqlWr9J//+Z9N5uzbt09//OMfG/7/I488oiuvvFJr165VXl6eKioq1KdPH02cOFGxsbFas2aNfvWrX7WpjrMlJyfH57mzZs1SVlaWVq9erQULFqikpESXXHKJrrrqKv3tb3/TzTff3OxzX3jhBU2fPl3JyclavHhxi5+RlWXLluno0aPauHGj8vPzFRwcrLS0NGVlZSkkJETPPvusPvjgg0bPeeaZZzR58mTdfPPN6tevnz788EOlpKTo5ptvVnl5ue644w4ZY9pUBwAAdrP9Lw8MBoPBYLQ2rPb0Nzeau5BfbGysef75583evXuNx+Mxu3btMk8++aSJiIgwe/fubfF2c6f39H766adtrv30HvOW/POf/2z0nOuuu87Mnz/fbN++3RQXF5vq6mpTUFBgcnJyzPTp05tcl6AjRnN7+q1GS7fsmzRpkvnoo4/MyZMnTVFRkcnOzjZXX321mTp1aot7+iWZDRs2GGNOXbCxrfXPmDHDvPPOO2bfvn2mvLzcVFZWmv3795sFCxaYjIyMZp8X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HKmPP/5YkpSVldXs665atUrDhw9XYmJio8czMzO1cuXKs/9GRNMPAAAAAGhFt27x6tYt3u4yzpmQkBDV1taqqqqq0ePl5eVNrpGSkZEht9utefPmaevWrRo/fryCgqxb7a1bt+rw4cOaMGFCw2MJCQm68MILlZ2dffbfiGj6AQAAAACtiO+eqPjuia1PDBDvv/++JGnGjBmKj49XZGSkJk2apJEjR+qtt95qNDczM1P79u3T9u3blZ2drfj4eF1yySXNvnZOTk6jpn/ChAk6duyYNm3a1CHvhaYfAAAAAIAz7N27V/fdd5+uuuoqLVy4UO+++67uu+8+PfPMM1q+fHnDvPPOO099+/bVsmXLJEm5ubmqqqpq8RD/7OxspaSkaOjQoZJONf05OTkd9l6CO+yVAQAAAABwoKSkJD3++OPau3evnnnmGVVVVenKK6/U/fffr+rqan3wwQeSTu3lr6ura2jay8rKtGbNGl155ZXq2rWrysvLm7z24cOHtXXrVk2YMEF1dXXq37+/HnnkkQ57LzT9AAAAAACcYfr06aqtrdXDDz+suro6SdKGDRsUHR2tGTNm6MMPP5Tb7VZ6erq2bdsmj8ejyMhISacu1peenq6xY8dqyZIllq+fnZ2t6dOny+12a9u2bcrPz++w90LTDwAAAADAGVJTU7V79+6Ghv+0r776ShMmTFBsbKyGDx+umJgYjRgxQu+++26T18jKymq26c/NzdWMGTM0adIkPffccx3yHk6j6QcAAAAAtGjf3u12l3BOnThxQgMGDFBwcLBqa2sbHh8yZIiqqqp08uRJZWZmyuPx6Oc//7nq6+sbPT8zM1MTJ05U7969dejQoSavX1ZWptdff10DBw5sdI2AjkDTDwAAAABoUV1dbeuTAsiiRYv02GOP6amnntI//vGPhnP6x48frwULFigyMlKjR49WTk6ONmzY0OT5J06c0MSJE5WRkaH/+7//s/waf/rTnzr4XZzC1fsBAAAAAC2KjUtQbFyC3WWcMytWrNCDDz6o0NBQ/fd//7cef/xxjRgxQr///e/10ksvafz48QoODtbSpUstn5+Xl6etW7cqMzPzHFfeFHv6AQAAAAAtiovrIUkqOnHM5krOnU8//VSffvqp5ba33npLb731VovPnzFjRsN/HzlyRGPHjm31a/oyp63Y0w8AAAAAQICi6QcA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CwsLUV1djQkTJiAnJwcNDQ0AgN27d2PXrl2IjIxESkoKKioqMHDgQIwZMwampqaIjY1FU1MTDh48iDVr1uCTTz5Beno67O3tMWfOnHYN5aOe79y5c9DpdFi9ejXi4uJgZmaGwMBAXtARGZEDBw4gOzsbZ8+exc2bN2FhYQF3d3f4+fkhPT0dZWVlAB4dD2bPno25c+ciPz8f48aNw5QpU1pt759//sHAgQPh5+eHq1evoq6uDsXFxV29W9ROT5MPPD/0fCz6e4CkpCQ8ePAAAQEB2LRpE2pqanD27FnExMSgtrbW0OFRF4uOjoaJiQmCgoIgkUigUCiwa9cubNmyRdDv+PHjKCsrQ2BgILy8vGBqaorbt29DqVSiqKjIQNHT01IqlfDw8NB/69+ssbERly5dgpubm2BdYWEhFi9ejKCgICxbtgwWFhaorKxEQUEBjh07pu+XkpKCfv36wd/fH15eXiguLkZERARfq9NL6XQ6fPrpp/jggw8QHh6O8vJyHDp0CK6urny1H5GR2LdvH9zd3bFgwQJYW1ujoaEBarUasbGxgof7Hjx4EJaWlvD394e5uTny8vLw8ccfC97JDgDJyclwdHTEokWLMGDAAGg0GgQGBnb1blE7tTUfeH4wDhIA4uM/iYiIiIiIiKhHMzF0AERERERERETUOVj0ExERERERERkpFv1ERERERERERopFPxEREREREZGRYtFPREREREREZKRY9BMREREREREZKRb9REREREREREaKRT8RERERERGRkWLRT0RERERERGSk/g8QcsUvOJL8eQAAAABJRU5ErkJggg==\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "Looks like we were able to pretty accurately reproduce the same things you can see in the mobile app by querying the public API!\n", "\n", "# 5. Data Analysis\n", "\n", "Data isn't much without some analysis, so we're going to do some in this section.\n", "\n", "DISCLAIMER: the analyses below may not be 100% biologically or scientifically grounded; the code is here to assist in your process, if you are interested in asking these kinds of questions.\n", "\n", "## 5.1: Heart rate vs. sleep period length\n", "\n", "Maybe the heart rate is correlated with how long a particular sleep period was. Let's see if this hypothesis is true." ], "metadata": { "id": "NXDQ8BW5PbgQ" } }, { "cell_type": "code", "source": [ "#@title Set date range and timezone\n", "start = \"2020-01-01\" #@param {type:\"date\"}\n", "end = \"2022-05-22\" #@param {type:\"date\"}\n", "timezone = \"US/Pacific\" #@param {type:\"string\"}\n", "params_all = {\n", " 'start': f'{start}T00:00:00.000Z',\n", " 'end': f'{end}T00:00:00.000Z'\n", "}" ], "metadata": { "id": "zRpWAQGCQEq4" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "First we get the length of each sleep in hours." ], "metadata": { "id": "w2CMZKfCQvix" } }, { "cell_type": "code", "source": [ "sleeps_df['Length (hours)'] = (sleeps_df.enddate - sleeps_df.startdate) / 3600" ], "metadata": { "id": "HFkRjV-VQGBK" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Next we get the median heart rate for each sleep." ], "metadata": { "id": "KLRbqHa6RwVI" } }, { "cell_type": "code", "source": [ "measurement_timestamps = hr_df.datetime.apply(lambda x: x.timestamp())\n", "\n", "all_heart_rates = []\n", "median_heart_rates = []\n", "\n", "for sleep_start, sleep_end in zip(sleeps_df.startdate, sleeps_df.enddate):\n", " idxes = np.where(np.logical_and(measurement_timestamps > sleep_start, measurement_timestamps < sleep_end))[0]\n", "\n", " heart_rates = np.array(hr_df.iloc[idxes].heart_rate)\n", "\n", " all_heart_rates.append(heart_rates)\n", " median_heart_rates.append(np.median(heart_rates))\n", "\n", "sleeps_df['Median heart rate'] = median_heart_rates" ], "metadata": { "id": "z4RmI5SHRIV2" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Let's make a quick plot to get some intuition. Here we just use [seaborn](https://seaborn.pydata.org/), as it's very quick to get beautiful plots out with minimal effort." ], "metadata": { "id": "fdgqfEL9R0ES" } }, { "cell_type": "code", "source": [ "p = sns.jointplot(x='Length (hours)', y='Median heart rate', data=sleeps_df, kind='reg')" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 441 }, "id": "e1aGy9iBRmJR", "outputId": "0ca5a603-ed76-401b-b999-b9275f826ddb" }, "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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\n" }, "metadata": { "needs_background": "light" } } ] }, { "cell_type": "markdown", "source": [ "As we can see from the scatterplot above, it looks like there might be a correlation there. Let's compute $R^2$ just to see exactly how correlated.\n", "\n", "We'll follow [this documentation](https://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.stats.linregress.html) and perform a linear regression to obtain the coefficient of determination." ], "metadata": { "id": "iTUONdMRSqf3" } }, { "cell_type": "code", "source": [ "from scipy import stats\n", "slope, intercept, r_value, p_value, std_err = stats.linregress(sleeps_df['Length (hours)'], sleeps_df['Median heart rate'])\n", "\n", "print(f'Slope: {slope:.3g}')\n", "print(f'Coefficient of determination: {r_value**2:.3g}')\n", "print(f'p-value: {p_value:.3g}')" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "H5dJPADyRZ5h", "outputId": "3d6b1bec-09e6-423d-aff6-0d36496723b1" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Slope: -0.611\n", "Coefficient of determination: 0.214\n", "p-value: 0.0825\n" ] } ] }, { "cell_type": "markdown", "source": [ "We also see that the p-value, which is determined by scipy to be the two-sided p-value for a hypothesis test whose null hypothesis is that the slope is zero, is not significant (>0.05)." ], "metadata": { "id": "Vjbyo5j_S10f" } } ] }