健康管理や研究目的でのスマートウォッチの利用を目にすることが多くなってきました。スマートウォッチは各社から販売されていますが、マラソンランナーなどに人気のGarmin(ガーミン)で得られるデータをRで読み込むのに便利な「FITfileR」パッケージを紹介します。
GarminはGarmin Connectと連携することで、スマートフォンのアプリやGarmin Connectのサイトで各種データを確認することが出来ます。
Garmin Connectのサイトで入手できるデータは「fitファイル」というバイナリファイルです。
Flexible and Interoperable Data Transfer (FIT) SDK:https://developer.garmin.com/fit/overview/
「fitファイル」のプロトコルが公開されていますので、興味ある方は「readBin」コマンドでファイルを解析してはいかがでしょうか。
なお、実行コマンドでも紹介していますが、「fitファイル」の時間起点は「December 31, 1989 UTC(631065600)」なので、「timestamp_16」は631065600をオフセットに使用することに注意です。
健康管理や研究目的でGarminのスマートウォッチの利用を検討してはいかがでしょうか。使用しているGarminnはVÍVOSMART 5です。睡眠やストレスなどの色々な健康情報を取得できます。
パッケージバージョンは0.1.5。実行コマンドはwindows 11のR version 4.2.1で確認しています。
fitファイルの入手
GarminとGarmin Connectの連携が終了している前提です。連携方法はGarminサイトなどで確認してください。
まずはGarmin Connectにアクセスします。
Garmin Connect:https://connect.garmin.com/signin/
アクセス後「fitファイル」は以下の写真のステップで入手可能です。指定日ごとの「fitファイル」がzipで圧縮されたファイルを入手できます。
パッケージのインストール
下記コマンドを実行してください。
#パッケージのインストール if(!requireNamespace("remotes")) { install.packages("remotes") } remotes::install_github("grimbough/FITfileR")
実行コマンドの紹介
詳細はコマンド、各パッケージのヘルプを確認してください。Garmin Connectから入手したzipファイルを展開して利用してください。展開後のフォルダに含まれる、全WELLNESS.fitを対象に処理して、心拍数、ストレスレベルのデータを入手するコマンド例です。対象「fitファイル」を「listMessageTypes」コマンドでメッセージを確認し、「getMessagesByType」コマンドで目的のデータを入手することが可能です。
#パッケージの読み込み library("FITfileR") #tidyverseパッケージがなければインストール if(!require("tidyverse", quietly = TRUE)){ install.packages("tidyverse");require("tidyverse") } #lubridateパッケージがなければインストール #https://www.karada-good.net/analyticsr/r-467/ if(!require("lubridate", quietly = TRUE)){ install.packages("lubridate");require("lubridate") } #wellnessファイルの読み込み #ReadFit <- readFitFile(file.choose()) #データの確認 #listMessageTypes(ReadFit) #[1] "file_id" "event" "device_info" "software" #[5] "monitoring" "monitoring_info" "ohr_settings" "stress_level" ###連続処理するための準備##### #展開したfitファイルのフォルダを作業ディレクトリにする setwd(choose.dir()) #ファイル名を取得 GetFileNames <- list.files() #_WELLNESS.fitを対象にする AnaFileNames <- str_subset(GetFileNames, "WELLNESS") #データ格納用引数 ActivityData <- NULL HartRateData <- NULL StressData <- NULL ###繰り返し処理##### for(i in seq(AnaFileNames)){ ReadFit <- readFitFile(AnaFileNames[[i]]) if("event" %in% listMessageTypes(ReadFit)){ ###活動内容,心拍数の取得##### MonitoringData <- getMessagesByType(ReadFit, message_type = "monitoring") #日付けの一括変換 for(n in seq(MonitoringData)){ #日付けデータの変換 MonitoringData[[n]] <- MonitoringData[[n]] %>% mutate_if(is.POSIXt, ~with_tz(., tz = "Asia/Tokyo")) } ###timestamp_16の変換##### #参考資料:https://developer.garmin.com/fit/cookbook/datetime/ #fitファイルの時間起点は「December 31, 1989 UTC(631065600)」なので、 #「timestamp_16」は631065600をオフセットに使用する #fitファイル開始時間の取得 TimCre <- as.numeric(as.POSIXct(file_id(ReadFit)[[2]])[[1]]) - 631065600 #変換処理 for(n in seq(MonitoringData)){ #日付けデータの変換 MonitoringData[[n]] <- MonitoringData[[n]] %>% mutate_at(vars(matches("timestamp_16")), function(.){ TimCre + #bbase::it演算:pythonで「X & 0xffff」 bitwAnd((. - TimCre), 0xffff) + 631065600 %>% lubridate::as_datetime() %>% lubridate::with_tz(tz = "Asia/Tokyo")}) } ######## ###アクティビティデータを取得##### #"current_activity_type_intensity"を含む列名のlist番号を取得 ActivityNo <- which(sapply(MonitoringData, function(x) "current_activity_type_intensity" %in% colnames(x))) for(For_ActiNo in seq(length(ActivityNo))){ if("timestamp" %in% colnames(MonitoringData[[ActivityNo[For_ActiNo]]])){ ActivityData <- bind_rows(ActivityData, MonitoringData[[ActivityNo[For_ActiNo]]]) }else{ MonitoringData[[ActivityNo[For_ActiNo]]] %>% select("timestamp_16", "current_activity_type_intensity") %>% bind_rows(ActivityData) }} ###心拍数データを取得##### #"heart_rate"を含む列名のlist番号を取得 HartRateNo <- which(sapply(MonitoringData, function(x) "heart_rate" %in% colnames(x))) #HartRate <- rbind(HartRate, MonitoringData[[HartRateNo]]) HartRateData <- bind_rows(HartRateData, MonitoringData[[HartRateNo]]) ###ストレスレベルの取得##### StressLevelData <- getMessagesByType(ReadFit, message_type = "stress_level") %>% mutate_if(is.POSIXt, ~with_tz(., tz = "Asia/Tokyo")) #StressData <- rbind(StressData, StressLevelData) StressData <- bind_rows(StressData, StressLevelData) } } #列名を整える colnames(ActivityData) <- c("Time_Stamp", "Activity_type_intensity") colnames(HartRateData) <- c("Time_Stamp", "Hart_Rate") colnames(StressData) <- c("Time_Stamp", "Stress_Level") #オブジェクト:"ActivityData","HartRate","StressData"以外を削除 remove(list = ls()[!ls() %in% c("ActivityData", "HartRateData", "StressData")]) ########
出力例
・心拍データをプロットする
#tidyverseパッケージがなければインストール if(!require("tidyverse", quietly = TRUE)){ install.packages("tidyverse");require("tidyverse") } ggplot(HartRateData, aes(x = Time_Stamp, y = Hart_Rate)) + geom_area(fill = "#4b61ba") + labs(title = "Hart_Rate") + theme_minimal()
・ストレスレデータをプロットする
#tidyverseパッケージがなければインストール if(!require("tidyverse", quietly = TRUE)){ install.packages("tidyverse");require("tidyverse") } ggplot(StressData, aes(x = Time_Stamp, y = Stress_Level)) + geom_area(fill = "#a87963") + labs(title = "Stress_Level") + theme_minimal()
少しでも、あなたの解析が楽になりますように!!