Skip to content

Commit

Permalink
add Fitbit json read functionalty #68
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentvanhees committed Sep 27, 2024
1 parent e452535 commit 207d712
Show file tree
Hide file tree
Showing 9 changed files with 586 additions and 3 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ URL: https://github.com/wadpac/GGIRread/
BugReports: https://github.com/wadpac/GGIRread/issues
License: Apache License (== 2.0)
Suggests: testthat
Imports: matlab, bitops, Rcpp (>= 0.12.10), data.table, readxl
Imports: matlab, bitops, Rcpp (>= 0.12.10), data.table, readxl, jsonlite
Depends: stats, utils, R (>= 3.5.0)
NeedsCompilation: yes
LinkingTo: Rcpp
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- Migrate read function for ActiGraph (csv) and Actiwatch (csv/awd) count data to GGIRread #68.
- Add function for reading Actical (csv) count data #68.
- Add function to read and merge Philips Health Band file pairs (xlsx) #68.

- Add function for reading Fitbit (json) with sleep, steps or calories #68.

# Changes in version 1.0.1 (release date:03-06-2024)

Expand Down
85 changes: 85 additions & 0 deletions R/readFitbit.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
readFitbit = function(filename = NULL) {
# Assumptions made:
# - sleep is sampled at 30 second resolution
# - steps are sampled at 60 second resolution
# - timestamp format is always the same per data type

# Declare local functions
handleTimeGaps = function(df, epochSize) {
timeRange = range(df$dateTime)
startTime = timeRange[1]
endTime = timeRange[2]
timeFrame = data.frame(dateTime = seq(startTime, endTime, by = epochSize))
df = merge(df, timeFrame, by = c("dateTime"), all.x = TRUE)
return(df)
}

#-------------------------------------------------
# Main code

D = jsonlite::read_json(path = filename,
simplifyVector = FALSE,
flatten = FALSE)

# extract dataType as json structure differs between types
dataType = tolower(unlist(strsplit(basename(filename), "-"))[1])

if (dataType == "sleep") {
epochSize = 30
# Put all data in data.frame
for (i in 1:length(D)) {
tmp = D[[i]][15]$levels
data = as.data.frame(data.table::rbindlist(tmp$data, fill = TRUE))
data$dateTime = as.POSIXct(data$dateTime, format = "%Y-%m-%dT%H:%M:%S")
if (i == 1) {
all_data = data
} else {
all_data = rbind(all_data, data)
}
if ("shortData" %in% names(tmp)) {
shortData = data.table::rbindlist(tmp$shortData, fill = TRUE)
shortData$dateTime = as.POSIXct(shortData$dateTime, format = "%Y-%m-%dT%H:%M:%S")
if (i == 1) {
all_shortData = shortData
} else {
all_shortData = rbind(all_shortData, shortData)
}
}
}
# Expand to full time series
D = as.data.frame(lapply(all_data, rep, all_data$seconds/epochSize))
D$dateTime = seq(from = D$dateTime[1], length.out = nrow(D), by = epochSize)
D$seconds = epochSize
D = handleTimeGaps(D, epochSize) # Handle time gaps, if any

S = as.data.frame(lapply(all_shortData, rep, all_shortData$seconds/30))
S$dateTime = seq(from = S$dateTime[1], length.out = nrow(S), by = 30)
S$seconds = epochSize

# merge in shortData (S)
matching_times = which(S$dateTime %in% D$dateTime == TRUE)
non_matching_times = which(S$dateTime %in% D$dateTime == FALSE)
if (length(matching_times) > 0) {
times_to_replace = S$dateTime[matching_times]
D[which(D$dateTime %in% times_to_replace), ] = S[matching_times,]
}
if (length(non_matching_times) > 0) {
D = rbind(D, S[non_matching_times,])
}
D = handleTimeGaps(D, epochSize) # Handle new time gaps, if any

# Order time stamps
D = D[order(D$dateTime), ]
colnames(D)[2] = "sleeplevel"
} else if (dataType == "steps" || dataType == "calories") {
epochSize = 60
data = as.data.frame(data.table::rbindlist(D, fill = TRUE))
data$dateTime = as.POSIXct(data$dateTime, format = "%m/%d/%y %H:%M:%S")
D = handleTimeGaps(data, epochSize = 60)
D$value = as.numeric(D$value)
colnames(D)[2] = dataType
} else {
stop("File type not recognised")
}
return(D)
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ ActivInsights Ltd https://activinsights.com/ | GENEActiv Original and Sleep | .b
Unilever Discover Ltd | Genea (no longer manufactured) | .bin | raw gravitational units | readGenea
ActiGraph | ??? | .csv | count data | readActigraph
Actiwatch | ??? | .csv and .awd | count data | readActiwatch
Actical | ??? | .csv | count data | readActical
Actical | ??? | .csv | count data | readActical.R
Philips Health Band | ??? | .xlsx | count data | mergePHBfilePairs.R
Fitbit | ??? | .json | sleep, steps or calories data | readFitbit.R
142 changes: 142 additions & 0 deletions inst/testfiles/calories-1995-06-23_Fitbit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
[{
"dateTime" : "06/23/95 00:00:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:01:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:02:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:03:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:04:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:05:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:06:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:07:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:08:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:09:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:10:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:11:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:12:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:13:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:14:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:15:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:16:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:17:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:18:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:19:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:20:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:21:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:22:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:23:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:24:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:25:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:26:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:27:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:28:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:29:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:30:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:31:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:32:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:33:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:34:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:35:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:36:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:37:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:38:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:39:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:40:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:41:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:42:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:43:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:44:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:45:00",
"value" : "1.48"
},{
"dateTime" : "06/23/95 00:46:00",
"value" : "1.48"
}]
Loading

0 comments on commit 207d712

Please sign in to comment.