Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance NotWorn algorithm to work with both count and raw data #1137

Merged
merged 32 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9f0dd14
improve handling of recordings where sensor was not worn during sleep
vincentvanhees May 23, 2024
e33188f
Merge branch 'master' into issue_aaron
vincentvanhees May 24, 2024
50ae357
ignore rawaccfiles_formats when stored config file.
vincentvanhees May 30, 2024
638c07a
Force all columns other than time to be numeric (did not go well befo…
vincentvanhees May 31, 2024
43203af
handle time series without any sleeponset
vincentvanhees May 31, 2024
7159902
align syntax second HASPT call with first call to ease comparing
vincentvanhees May 31, 2024
d1c1764
update NotWorn algorithm to work for both raw and count data and hand…
vincentvanhees May 31, 2024
49a530a
Update NEWS.md
vincentvanhees May 31, 2024
3b9e2b7
improve handling of NotWorn guider
vincentvanhees May 31, 2024
02f91eb
Merge branch 'issue1134_clarify_relyonguider_vincent' into issue_aaron
vincentvanhees May 31, 2024
605afa3
Merge branch 'master' into issue_aaron
vincentvanhees Jun 1, 2024
5b8130d
Merge branch 'issue_aaron' into issues_1082Date_1089NotWorn
vincentvanhees Jun 1, 2024
e29cc82
rename dur column with overlapGuider because it is an indicator (0 or…
vincentvanhees Jun 1, 2024
59f27a9
Update NEWS.md
vincentvanhees Jun 1, 2024
f07718d
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 4, 2024
8e4d702
Merge branch 'issue1148_wear_percentage_part5' into issues_1082Date_1…
vincentvanhees Jun 6, 2024
750bae3
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 10, 2024
95ab508
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 11, 2024
bcc68fa
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 11, 2024
e311066
sync branch with master by first deleting all html files
vincentvanhees Jun 14, 2024
fb16c3e
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 14, 2024
621f382
Add cookbook vignette with illustration of how to use NotWorn #1137
vincentvanhees Jun 24, 2024
d6a4a34
renaming chapter0_contributing to chapter0_Contributing
vincentvanhees Jun 24, 2024
af8d6a1
minor edits
vincentvanhees Jun 24, 2024
723225d
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 24, 2024
5d3180f
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 24, 2024
b365067
handle data before first noon correctly when recording has only 1 night
vincentvanhees Jun 24, 2024
ea12963
tidy up fix related to #1160
vincentvanhees Jun 24, 2024
fa25fa5
Update NEWS.md
vincentvanhees Jun 24, 2024
60adf83
Merge pull request #1161 from wadpac/test_branch_for_Raul
vincentvanhees Jun 28, 2024
9ba72f2
Merge branch 'master' into issues_1082Date_1089NotWorn
vincentvanhees Jun 28, 2024
e1b0b8e
Add option to specify second guider when working with NotWorn as prim…
vincentvanhees Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ RELEASE_CYCLE.md
codecov.yml
index.md
^inst/doc/*.html
^vignettes/HouseHoldCoanalysis.Rmd
^vignettes/HouseHoldCoanalysis.Rmd
^vignettes/Cookbook.Rmd
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

- Report part 5: fix bug that was introduced on 2024-Feb-19 in the calculation of wear percentage #1148

- Part 3 and 4: Revise NotWorn algorithm to work with both count and raw data with varying degrees of nonwear. Further, when HASPT.algo is set to NotWorn then a second guider can optionally be specified in case the accelerometer unexpectedly worn by the participant. #1089

- Visualreport: Improve handling of recordings where the accelerometer was not worn.

- Part 1: Enable timegap imputation for ad-hoc csv data. #1154

- Part 1: Reduce constraints on value for parameter chunksize #1155.
Expand All @@ -14,6 +18,8 @@

- Vignette: Migrated many sections from main CRAN vignette to wadpac.github.io/GGIR/

- Part 3: Fix handling of recordings with only 1 midnight that start before 4am, #1160

# CHANGES IN GGIR VERSION 3.1-1

- Part 2: Corrected calculation of LXhr and MXhr which had one hour offset when timing was after midnight, #1117
Expand Down
2 changes: 1 addition & 1 deletion R/GGIR.R
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ GGIR = function(mode = 1:5, datadir = c(), outputdir = c(),
"GGIRversion", "dupArgValues", "verbose", "is_GGIRread_installed",
"is_read.gt3x_installed", "is_ActCR_installed",
"is_actilifecounts_installed", "rawaccfiles", "is_readxl_installed",
"checkFormat", "getExt") == FALSE)]
"checkFormat", "getExt", "rawaccfiles_formats") == FALSE)]

config.parameters = mget(LS)
config.matrix = as.data.frame(createConfigFile(config.parameters, GGIRversion))
Expand Down
13 changes: 11 additions & 2 deletions R/HASIB.R
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,17 @@ HASIB = function(HASIB.algo = "vanHees2015", timethreshold = c(), anglethreshold
# around to avoid having to completely redesign GGIR just for those studies.

sib_classification = as.data.frame(matrix(0, Nvalues, 1))
activityThreshold = as.numeric(quantile(x = activity, probs = 0.1) * 2)
activity2 = zoo::rollmax(x = activity, k = 3600 / epochsize, fill = 1)
activity2 = zoo::rollmax(x = activity, k = 300 / epochsize, fill = 1)
# ignore zeros because in ActiGraph with many zeros it skews the distribution
nonzero = which(activity2 != 0)
if (length(nonzero) > 0) {
activityThreshold = sd(activity2[nonzero], na.rm = TRUE) * 0.05
if (activityThreshold < min(activity)) {
activityThreshold = quantile(activity2[nonzero], probs = 0.1)
}
} else {
activityThreshold = 0
}
zeroMovement = which(activity2 <= activityThreshold)
if (length(zeroMovement) > 0) {
sib_classification[zeroMovement, 1] = 1
Expand Down
45 changes: 36 additions & 9 deletions R/HASPT.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ HASPT = function(angle, sptblocksize = 30, spt_max_gap = 60, ws3 = 5,
# threshold = 10th percentile (constrained to 0.13-0.5 if required)
medabsdi = function(angle) {
#50th percentile, do not use mean because that will be outlier sensitive
angvar = stats::median(abs(diff(angle)))
angvar = stats::median(abs(diff(angle)))
return(angvar)
}
k1 = 5 * (60/ws3)
Expand All @@ -39,7 +39,7 @@ HASPT = function(angle, sptblocksize = 30, spt_max_gap = 60, ws3 = 5,
# threshold = 45 degrees
x = abs(angle)
threshold = 45
} else if (HASPT.algo == "NotWorn") {
} else if (HASPT.algo == "NotWorn") {
# When protocol is to not wear sensor during the night,
# and data is collected in count units we do not know angle
# as needed for HorAngle and HDCZA.
Expand All @@ -52,15 +52,20 @@ HASPT = function(angle, sptblocksize = 30, spt_max_gap = 60, ws3 = 5,
# smooth x to 5 minute rolling average to reduce sensitivity to sudden peaks
ma <- function(x, n = 300 / ws3){stats::filter(x, rep(1 / n, n), sides = 2, circular = TRUE)}
x = ma(x)
activityThreshold = sd(x, na.rm = TRUE) * 0.2
# For sensewear external data this will not work as it mostly has values of 1 and up.
if (activityThreshold < min(activity)) {
activityThreshold = quantile(x, probs = 0.1)
nonzero = which(x != 0)
if (length(nonzero) > 0) {
activityThreshold = sd(x[nonzero], na.rm = TRUE) * 0.05
# For sensewear external data this will not work as it mostly has values of 1 and up.
if (activityThreshold < min(activity)) {
activityThreshold = quantile(x, probs = 0.1)
}
} else {
activityThreshold = 0
}
# this algorithm looked for x <= threshold, now a minimum quantity is added
# to the threshold to allow for consistent definition of nomov below
# i.e., x < threshold
threshold = activityThreshold + 0.001
threshold = activityThreshold + 0.001
}
# Now define nomov periods with the selected strategy for invalid time
nomov = rep(0,length(x)) # no movement
Expand Down Expand Up @@ -129,8 +134,8 @@ HASPT = function(angle, sptblocksize = 30, spt_max_gap = 60, ws3 = 5,
if (SPTE_start == 0) SPTE_start = 1
part3_guider = HASPT.algo
if (is.na(HASPT.ignore.invalid)) {
# investigate if invalid time was included in the SPT definition,
# and if so, keep track of that in the guider. This is needed in the
# investigate if invalid time was included in the SPT definition,
# and if so, keep track of that in the guider. This is needed in the
# case that sleeplog is used, to inform part 4 that it should
# trust the sleeplog times for this specific night.
spt_long = rep(0, length(invalid))
Expand All @@ -141,6 +146,28 @@ HASPT = function(angle, sptblocksize = 30, spt_max_gap = 60, ws3 = 5,
}
}

# # Code to help investigate classifications:
# plot(x, col = "black", type = "l")
# abline(v = SPTE_start, col = "green", lwd = 2)
# abline(v = SPTE_end, col = "red", lwd = 2)
# rect(xleft = s1, ybottom = rep(0, length(s1)),
# xright = e1, ytop = rep(0.1, length(s1)),
# col = rgb(0, 0, 255, max = 255, alpha = 50), border = NA)
#
# rect(xleft = s5, ybottom = rep(0.1, length(s1)),
# xright = e5, ytop = rep(1, length(s1)),
# col = rgb(255, 0, 0, max = 255, alpha = 20), border = NA)
# lines(x, col = "black", type = "l")
# abline(h = threshold, col = "purple", lwd = 2)
# inva = which(invalid == 1)
# if (length(inva) > 0) {
# lines(inva, rep(0.1, length(inva)),
# type = "p", pch = 20, lwd = 4, col = "black")
# }
# lines(invalid* 0.05, type = "l", col = "red")
# # graphics.off()
# browser()

} else {
SPTE_end = c()
SPTE_start = c()
Expand Down
12 changes: 6 additions & 6 deletions R/check_params.R
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ check_params = function(params_sleep = c(), params_metrics = c(),

if (length(params_sleep) > 0) {
if (length(params_sleep[["def.noc.sleep"]]) != 2) {
if (params_sleep[["HASPT.algo"]] %in% c("HorAngle", "NotWorn") == FALSE) {
if (params_sleep[["HASPT.algo"]][1] %in% c("HorAngle", "NotWorn") == FALSE) {
params_sleep[["HASPT.algo"]] = "HDCZA"
}
} else if (length(params_sleep[["def.noc.sleep"]]) == 2) {
Expand All @@ -200,13 +200,13 @@ check_params = function(params_sleep = c(), params_metrics = c(),

if (length(params_general) > 0 & length(params_metrics) > 0 & length(params_sleep) > 0) {
if (params_general[["sensor.location"]] == "hip" &&
params_sleep[["HASPT.algo"]] %in% c("notused", "NotWorn") == FALSE) {
params_sleep[["HASPT.algo"]][1] %in% c("notused", "NotWorn") == FALSE) {
if (params_metrics[["do.anglex"]] == FALSE | params_metrics[["do.angley"]] == FALSE | params_metrics[["do.anglez"]] == FALSE) {
warning(paste0("\nWhen working with hip data all three angle metrics are needed,",
"so GGIR now auto-sets arguments do.anglex, do.angley, and do.anglez to TRUE."), call. = FALSE)
params_metrics[["do.anglex"]] = params_metrics[["do.angley"]] = params_metrics[["do.anglez"]] = TRUE
}
if (params_sleep[["HASPT.algo"]] != "HorAngle") {
if (params_sleep[["HASPT.algo"]][1] != "HorAngle") {
warning("\nChanging HASPT.algo value to HorAngle, because sensor.location is set as hip", call. = FALSE)
params_sleep[["HASPT.algo"]] = "HorAngle"; params_sleep[["def.noc.sleep"]] = 1
}
Expand All @@ -230,12 +230,12 @@ check_params = function(params_sleep = c(), params_metrics = c(),
params_sleep[["def.noc.sleep"]] = 1
}

if (params_sleep[["HASPT.algo"]] == "HorAngle" & params_sleep[["sleepwindowType"]] != "TimeInBed") {
if (params_sleep[["HASPT.algo"]][1] == "HorAngle" & params_sleep[["sleepwindowType"]] != "TimeInBed") {
warning("\nHASPT.algo is set to HorAngle, therefore auto-updating sleepwindowType to TimeInBed", call. = FALSE)
params_sleep[["sleepwindowType"]] = "TimeInBed"
}

if (length(params_sleep[["loglocation"]]) == 0 & params_sleep[["HASPT.algo"]] != "HorAngle" & params_sleep[["sleepwindowType"]] != "SPT") {
if (length(params_sleep[["loglocation"]]) == 0 & params_sleep[["HASPT.algo"]][1] != "HorAngle" & params_sleep[["sleepwindowType"]] != "SPT") {
warning("\nAuto-updating sleepwindowType to SPT because no sleeplog used and neither HASPT.algo HorAngle used.", call. = FALSE)
params_sleep[["sleepwindowType"]] = "SPT"
}
Expand Down Expand Up @@ -367,7 +367,7 @@ check_params = function(params_sleep = c(), params_metrics = c(),
}

if (length(params_general) > 0 & length(params_sleep) > 0) {
if (params_sleep[["HASPT.algo"]] == "HorAngle") {
if (params_sleep[["HASPT.algo"]][1] == "HorAngle") {
# Not everywhere in the code we check that when HASPT.algo is HorAngle, sensor.location is hip.
# However, the underlying assumption is that they are linked. Even if a study would
# use a waist or chest worn sensor we refer to it as hip as the orientation and need
Expand Down
3 changes: 3 additions & 0 deletions R/convertEpochData.R
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@ convertEpochData = function(datadir = c(), metadatadir = c(),
M$metashort = as.data.frame(cbind(time_shortEp_8601,
D[1:length(time_shortEp_8601), ]))
colnames(M$metashort) = c("timestamp", colnames(D))
for (ic in 2:ncol(M$metashort)) {
M$metashort[, ic] <- as.numeric(M$metashort[, ic])
}
}
if (length(which(is.na(M$metashort$ZCY) == TRUE)) > 0) {
# impute missing data by zero
Expand Down
Loading
Loading