Measuring the mammalian diving reflex with Apple Watch

I like ecophysiology, data and R. This blog post is a niche intersection between those three interests and I like that. There are two functions of this post1

  1. To try out the RWordPress package for publishing Rmarkdown directly to WordPress
    • Since you are reading this, it worked! (Not really – images are troublesome, I had to add them after)
    • Instructions here but see the end of this post
  2. To analyse heart rate data from Apple Health
    • Specifically, to see whether an Apple Watch can detect the mammalian diving reflex

1 NB – This is blog post No. 50!

The mammalian diving reflex

Diving triggers a range of physiological responses in mammals. The one we are interested in is bradycardia – the decrease of heart rate. It’s a fun party trick. Diving bradycardia is detectable using standard heart rate monitors for exercising but can bradycardia also be measured by wearable tech?

Recording heart rate

While you wear one, Apple Watch measures your heart rate in the background at irregular intervals that depend on your detected activity. Unfortunately, the interval is not short enough to record data at the frequency we want – unless you can hold your breath for hours; I certainly can’t. Fortunately, Apple Watch records heart rate continuously about every 5 seconds while logging an exercise. There isn’t an exercise option for “Science” but there’s “Other” if you don’t want to mess up your exercise categories.

To measure your diving bradycardia:

  1. First take a baseline measurement while at rest. Controls are important.
  2. Fill a sink or bucket large enough to submerge your face with cold water. I think the temperature is important but that’s something you could test.
  3. Hold your breath and submerge your face. You don’t have to dunk your whole head. The relevant receptors are apparently in your nostrils. Stay there for as long as you can.
    • Apple records in beats min-1 because it aggregates the data and it logs about every 5 seconds. You could quickly come up for air to extend time submerged.
  4. Take another baseline measurement as control.
  5. Stop logging

Exporting data from iOS

Your heart rate data is stored in Apple Health. It’s simple enough to download the data but Apple doesn’t prepare the data in any usable format out of the box 😦

To download the data, go to Apple Health, access your profile (your picture/initials) in the top right of the main screen, scroll down and select “Export All Health Data”. It will take some time for iOS to create a zip file that you can save to cloud storage or email. Note that it means ALL health data so if you use Apple Health a lot, that could be a hefty file. Data is exported in an .XML file within the .ZIP. You can also use third party health apps that apparently let you select what to export and exports the data in a sensible structure.

Importing data into R

Luckily there are R tools for directly importing XML files! The package XML is key here.

install.packages("XML")

There’s even an R package for analysing Apple Health data but I’m following this guide.

library(tidyverse) # Yes I use tidyverse - Sorry!
library(XML)
library(lubridate)
#load apple health export.xml file
heart_rate <- xmlParse("apple_health_export/export.xml")

#transform xml file to data frame - select the Record rows from the xml file
heart_rate <- XML:::xmlAttrsToDataFrame(heart_rate["//Record"])

heart_rate <- heart_rate %>% 
  # make value variable numeric
  mutate(value = as.numeric(value),
         # I'm using R 4.0.3 so you may need to convert to character first if using < 4.0
         # make endDate in a date time variable POSIXct using lubridate
         time = ymd_hms(endDate,tz="Europe/Dublin"),
         day = as_date(endDate)) %>% 
  # filter just heart rate measured today, we don't need the rest
  filter(type == "HKQuantityTypeIdentifierHeartRate",
         day == today()) #2020-10-17

Results

It works! You can see the decrease in heart rate while submerged. That’s cool.

My heart rate dropped to 67 from a mean of 101 beforehand.

# Time series of heart rate
heart_rate %>% 
  ggplot(aes(time, value)) +
  # Dive 1
  annotate("rect", fill = "lightgrey", alpha = 0.7, 
           xmin = as.POSIXct("2020-10-17 11:05:00"), xmax = as.POSIXct("2020-10-17 11:05:30"),
           ymin = -Inf, ymax = Inf) +
  # Dive 2
  annotate("rect", fill = "lightgrey", alpha = 0.7, 
           xmin = as.POSIXct("2020-10-17 11:07:12"), xmax = as.POSIXct("2020-10-17 11:07:50"),
           ymin = -Inf, ymax = Inf) +
  geom_point() +
  geom_line() +
  annotate("text", label = "Dives", x = as.POSIXct("2020-10-17 11:10"), y = 75) +
  annotate("rect", fill = "lightgrey", alpha = 0.7, 
           xmin = as.POSIXct("2020-10-17 11:10:40"), xmax = as.POSIXct("2020-10-17 11:11:10"),
           ymin = 73, ymax = 77) +
  theme_classic() +
  labs(x = "Time", y = expression("Heart rate "("Beats min"^-1))) +
  scale_x_datetime(limits = as.POSIXct(strptime(c("2020-10-17 11:00:34", "2020-10-17 11:13:00"), format = "%Y-%m-%d %H:%M:%S"))) +
  ylim(c(50, 125))

results-1

Thanks to Nicholas Wu for the inspiration and Craig White for demonstrating diving reflexes in class.


Bonus! Mini guide to RWordPress

Install RWordPress and XMLRPC

devtools::install_github(c("duncantl/XMLRPC", "duncantl/RWordPress"))

I used a separate R script to render the rmd and kept all the files in the same working directory. Some of the markdown features didn’t render properly, like footnotes and subscripts.

library(RWordPress)
library(knitr)

# Note Case - In the RWordPress package WordPress... is lower case, on many online guides it's written as WordPress...

options(WordPressLogin = c(<your username> = "<your password>"),
        WordPressURL = "<https://your website/xmlrpc.php>")
# e.g. https://jacintakongresearch.wordpress.com/xmlrpc.php

knit2wp('heart_rate.Rmd',
        title = 'Measuring the mammalian diving reflex with Apple Watch',
        publish = TRUE,
        action =  "newPost", 
        mt_keywords = c('code', 'R'),
        categories = c('Fun'))

Here’s another guide to RWordpress that describes some of the other functions.

Dealing with images

Apparently rendering images directly are a problem. One solution is to tell Knitr to upload them to an image hosting website like Imgur. I uploaded the images to WordPress manually, then inserted them via the WP editor.

Leave a comment