Статьи

Эй, Фитбит, мои данные принадлежат мне!

Когда вы зайдете на сайт Fitbit и захотите загрузить свои собственные данные Fitbit, вы найдете путь к их утилите экспорта данных на странице их настроек. Как только вы попадаете туда, вас встречает что-то вроде лозунга: «Ваши данные принадлежат вам!» Увидев очень высокоуровневые данные, которые они предоставляют вам, когда вы нажимаете «загрузить», я им не верю.

Как Оруэллиан!

Как Оруэллиан!

Приятно, что они позволяют вам загружать даже такие большие данные (мне очень нравится функция отслеживания сна), и я, по крайней мере, могу представить, что меня интересует моя ежедневная статистика, поскольку я становлюсь более конкурентоспособным с собой, но:

  1. Где данные сердечного ритма? Я получил Fitbit Charge HR на свой день рождения, и мне нравится функция пульсометра!

  2. А как насчет внутридневных данных? Я заинтересован в дальнейшем анализе моего сердечного ритма в зависимости от времени суток.

  3. Как насчет конкретных действий, которые я регистрирую, нажимая кнопку на боковой стороне моего Charge HR и аннотируя / разрешая на веб-сайте?

По этим причинам я убежден, что они на самом деле не верят, что мои данные принадлежат мне. Вот почему было приятно узнать о пакете fitbitScraper Кори Ниссена . В настоящее время он позволяет получать шаги, расстояние, активные минуты, этажи, калории, сожженные с 15-минутным интервалом, и частоту сердечных сокращений с 5-минутным интервалом. Он в основном входит в систему на веб-сайте Fitbit и удаляет ваши данные из исходного кода страницы!

Ниже вы увидите код, который я подготовил и который показывает, как я использовал его пакет для запуска работающего набора данных, охватывающего столько дней, сколько я хочу, о том, что делает мой сердечный ритм в течение каждого дня. Если вы хотите адаптировать этот код для загрузки других доступных категорий данных, обратите внимание, что любые ссылки на частоту сердечных сокращений должны быть изменены соответствующим образом 🙂

Инициализация Dataframe:

library(lubridate)
library(plyr)
library(dplyr)
library(fitbitScraper)

hr_data = list(time = c(), hrate = c())

cookie = login("my@email.com", "mypassword", rememberMe = TRUE)
startdate = as.Date('2015-08-07', format = "%Y-%m-%d")
enddate = today()
s = seq(startdate, enddate, by="days")

for (i in 1:length(s)) {
 df = get_intraday_data(cookie, "heart-rate", date=sprintf("%s",s[i]))
 names(df) = c("time","hrate")
 hr_data = rbind(hr_data, df)
 rm(df)}

Этот код может нуждаться в некотором объяснении (не стесняйтесь пропустить этот текст, если вы понимаете это просто отлично). Сначала вы заметите функцию входа в систему, где вам нужно будет ввести адрес электронной почты и пароль, которые вы зарегистрировали для своей учетной записи Fitbit. Затем вы заметите, что я указал переменную ‘startdate’ и поместил ‘2015-08-07’ в качестве значения внутри функции as.Date. Это был только первый полный день, который я провел с Fitbit, и вы можете поменять его на тот, который был вашим первым Fitbit днем. Затем функция seq удобно создает вектор, содержащий серию отметок даты, начиная с указанной «начальной даты» и заканчивая «конечной датой», которую вы можете видеть, какой бы ни была сегодняшняя дата.

Наконец, у нас есть цикл for, который циклически просматривает индексы, представляющие каждый элемент в последовательности дат ‘s’, так что 5-минутные данные каждого дня могут быть сохранены во временном фрейме данных и добавлены к объекту ‘hr_data’, который преобразует из списка в фрейм данных. После всего, что сказано и сделано, теперь у вас есть первый залп ваших собственных данных Fitbit.

Но подождите, это еще не все! Как насчет того, когда вы хотите загрузить больше данных, через несколько дней? Вам нужно снова запустить этот же код, перезаписывая данные за дни, которые уже были обработаны? Вы можете сделать это, или вы можете использовать более сложный код ниже, который только очищает веб-сайт Fitbit от данных за неполные дни или там вообще!

Код, когда вы хотите больше и больше и больше данных Fitbit!

library(lubridate)
library(plyr)
library(dplyr)
library(fitbitScraper)

cookie = login("my@email.com", "mypassword", rememberMe = TRUE)
startdate = as.Date('2015-08-07', format = "%Y-%m-%d")
enddate = today()
s = seq(startdate, enddate, by="days")

completeness = hr_data %>% group_by(dte = as.Date(time)) %>% summarise(comp = mean(hrate > 0))
incomp.days = which(completeness$comp < .9)
missing.days = which(s %in% completeness$dte == FALSE)
days.to.process = c(incomp.days, missing.days)

for (i in days.to.process) {
  df = get_intraday_data(cookie, "heart-rate", date=sprintf("%s",s[i]))
  names(df) = c("time","hrate")

  # If the newly downloaded data are for a day already in
  # the pre-existing dataframe, then the following applies:

  if (mean(df$time %in% hr_data$time) == 1) {

    # Get pct of nonzero hrate values in the pre-existing dataframe
    # where the timestamp is the same
    # as the current timestamp being processed in the for loop.

    pct.complete.of.local.day = mean(hr_data$hrate[yday(hr_data$time) == yday(s[i])] > 0)

    # Get pct of nonzero hrate values in the temporary dataframe
    # where the timestamp is the same
    # as the current timestamp being processed in the for loop.

    pct.complete.of.server.day = mean(df$hrate[yday(df$time) == yday(s[i])] > 0)

    # If the newly downloaded data are more complete for this day
    # than what is pre-existing, overwrite the heart rate data
    # for that day.

    if (pct.complete.of.local.day < pct.complete.of.server.day) {
      rows = which(hr_data$time %in% df$time)
      hr_data$hrate[rows] = df$hrate}
  }
  else {

    # If the newly downloaded data are for a day not already in
    # the pre-existing dataframe, then use rbind to just add them!

    hr_data = rbind(hr_data, df)
  }
  rm(df)
}

Первое, на что я хотел бы обратить ваше внимание в этом коде, это блок, начинающийся с определения объекта ‘complete’ и заканчивающийся объектом ‘days.to.process’. Что я пытаюсь сделать с помощью этих объектов:

  1. Чтобы получить список, какие дни в моем ранее существующем фрейме данных могли бы выиграть от полного обновления данных из-за слишком большого количества пропущенных данных (вы заметите, что я определил неполный день как день с менее чем 90% ненулевого значения). данные) и …

  2. Данные каких дней мне не хватает, потому что я просто не загружал данные в тот день.

Объект days.to.process — это просто вектор, который объединяет отметки даты любых неполных дней и дней, которые отсутствуют в моем фрейме данных.

В цикле for я перебираю метки даты, представленные в объекте days.to.process, и вначале действую, как раньше, но затем происходит несколько новых вещей:

Я проверяю, находятся ли метки даты и времени во временном фрейме данных в моем ранее существующем фрейме данных. Если это так, я делаю сравнение процентного значения данных, отличных от нуля за этот день, в моем ранее существовавшем фрейме данных (в этом и заключается цель переменной pct.complete.of.local.day) с процент данных, которые ненулевые во временном фрейме данных (отсюда и переменная «pct.complete.of.server.day»).

Если во временном фрейме данных для этого дня больше ненулевых данных, я нахожу индексы строк в ранее существовавшем фрейме данных для рассматриваемого дня, а затем использую их для обновления данных на месте новыми данными из временного фрейма данных. Это конкретное сравнение ненулевых данных в ранее существовавшем и временном фрейме данных, вероятно, является избыточным в большинстве случаев.

Было бы неплохо иметь возможность легко / автоматически получать данные о физических нагрузках, которые я регистрировал через веб-сайт (гребной тренажер, велотренажер, беговая дорожка и т. Д.), Чтобы я мог соотнести их с частотой сердечных сокращений в то время, но я думаю, я должен сделать это вручную сейчас. Со временем меня интересует более глубокий анализ моего сердечного ритма в разное время дня.

По крайней мере, теперь я чувствую, что мои данные принадлежат мне, хотя мне пришлось прибегнуть к использованию чужого очень умного кодирования (спасибо, Кори!), Чтобы сделать это!