diff --git a/NEWS.md b/NEWS.md index c32e1c765..a59765066 100644 --- a/NEWS.md +++ b/NEWS.md @@ -25,6 +25,8 @@ relative to start and end of the most active time window as identified. #905 - Fix recently introduced bug where GGIR environment was not exported to cluster in GGIR part 1, 2, 3, and 5 #910 +- Part 5: Implemented sleep onset to sleep onset timewindow ("OO") #931 + # CHANGES IN GGIR VERSION 2.10-3 - Part 1: Fixed minor bug in ismovisens that failed when datadir started with "./" #897 diff --git a/R/g.part5.R b/R/g.part5.R index bd5fc401b..89b45113a 100644 --- a/R/g.part5.R +++ b/R/g.part5.R @@ -410,6 +410,7 @@ g.part5 = function(datadir = c(), metadatadir = c(), f0=c(), f1=c(), #------------------------------- # ignore all nights in 'inights' before the first waking up and after the last waking up FM = which(diff(ts$diur) == -1) + SO = which(diff(ts$diur) == 1) # now 0.5+6+0.5 midnights and 7 days for (timewindowi in params_output[["timewindow"]]) { nightsi = nightsi_bu @@ -419,6 +420,11 @@ g.part5 = function(datadir = c(), metadatadir = c(), f0=c(), f1=c(), # ignore first and last midnight because we did not do sleep detection on it nightsi = nightsi[nightsi > FM[1] & nightsi < FM[length(FM)]] } + } else if (timewindowi == "OO") { + if (length(SO) > 0) { + # ignore data before the first sleep onset and after the last sleep onset + nightsi = nightsi[nightsi > SO[1] & nightsi < SO[length(SO)]] + } } else { # if first night is missing then nights needs to align with diur startend_sleep = which(abs(diff(ts$diur)) == 1) @@ -428,8 +434,10 @@ g.part5 = function(datadir = c(), metadatadir = c(), f0=c(), f1=c(), } if (timewindowi == "MM") { Nwindows = length(nightsi) + 1 - } else { + } else if (timewindowi == "WW") { Nwindows = length(which(diff(ts$diur) == -1)) + } else if (timewindowi == "OO") { + Nwindows = length(which(diff(ts$diur) == 1)) } indjump = 1 qqq_backup = c() @@ -442,7 +450,6 @@ g.part5 = function(datadir = c(), metadatadir = c(), f0=c(), f1=c(), } for (wi in 1:Nwindows) { #loop through 7 windows (+1 to include the data after last awakening) # Define indices of start and end of the day window (e.g. midnight-midnight, or waking-up or wakingup - defdays = g.part5.definedays(nightsi, wi, indjump, nightsi_bu, epochSize = ws3new, qqq_backup, ts, timewindowi, Nwindows, qwindow = params_247[["qwindow"]], diff --git a/R/g.part5.definedays.R b/R/g.part5.definedays.R index d968b99c3..6e83bd90a 100644 --- a/R/g.part5.definedays.R +++ b/R/g.part5.definedays.R @@ -156,6 +156,25 @@ g.part5.definedays = function(nightsi, wi, indjump, nightsi_bu, names(segments) = paste(start, end, sep = "-") segments_names = "WW" } + } else if (timewindowi == "OO") { + if (wi <= (Nwindows - 1)) { # all full wake to wake days + qqq[1] = which(diff(ts$diur) == 1)[wi] + 1 + qqq[2] = which(diff(ts$diur) == 1)[wi + 1] + } else { + # time after last reliable waking up (this can be more than 24 hours) + # ignore this day, because if the night was ignored for sleep analysis + # then the description of the day in part 5 including that night is + # not informative. + qqq = c(NA, NA) + } + # build up segments + if (!is.na(qqq[1]) & !is.na(qqq[2])) { + segments = list(qqq) + start = substr(ts$time[qqq[1]], 12, 19) + end = substr(ts$time[qqq[2]], 12, 19) + names(segments) = paste(start, end, sep = "-") + segments_names = "OO" + } } return(invisible(list(qqq = qqq, qqq_backup = qqq_backup, segments = segments, segments_names = segments_names))) diff --git a/R/g.report.part5.R b/R/g.report.part5.R index 1469b4efd..215623030 100644 --- a/R/g.report.part5.R +++ b/R/g.report.part5.R @@ -45,7 +45,7 @@ g.report.part5 = function(metadatadir = c(), f0 = c(), f1 = c(), loglocation = c } # Note: Below we intentionally only sets a criteria on daytime, because for # the night time we only need start and end of the SPT window. - if (window == "WW") { + if (window == "WW" | window == "OO") { indices = which(x$nonwear_perc_day <= maxpernwday & x$dur_spt_min > 0 & x$dur_day_min > 0 & include_window == TRUE) } else if (window == "MM") { @@ -153,8 +153,8 @@ g.report.part5 = function(metadatadir = c(), f0 = c(), f1 = c(), loglocation = c # split results to different spreadsheets in order to minimize individual # filesize and to ease organising dataset uwi = as.character(unique(outputfinal$window)) - if (!all(uwi %in% c("MM", "WW"))) { - uwi = c(uwi[uwi %in% c("MM", "WW")], "Segments") + if (!all(uwi %in% c("MM", "WW", "OO"))) { + uwi = c(uwi[uwi %in% c("MM", "WW", "OO")], "Segments") } uTRLi = as.character(unique(outputfinal$TRLi)) uTRMi = as.character(unique(outputfinal$TRMi)) @@ -197,7 +197,7 @@ g.report.part5 = function(metadatadir = c(), f0 = c(), f1 = c(), loglocation = c "-", uTRVi[h3], "-", usleepparam[h4])) } select_window = as.character(outputfinal$window) == uwi[j] - if (!(uwi[j] %in% c("MM", "WW"))) select_window = !(as.character(outputfinal$window) %in% c("MM", "WW")) + if (!(uwi[j] %in% c("MM", "WW", "OO"))) select_window = !(as.character(outputfinal$window) %in% c("MM", "WW", "OO")) seluwi = which(select_window & as.character(outputfinal$TRLi) == uTRLi[h1] & as.character(outputfinal$TRMi) == uTRMi[h2] & diff --git a/man/GGIR.Rd b/man/GGIR.Rd index 66ec86f38..9c1a10b75 100755 --- a/man/GGIR.Rd +++ b/man/GGIR.Rd @@ -1278,7 +1278,7 @@ GGIR(mode = 1:5, Character (default = c("MM", "WW")). In \link{g.part5}: Timewindow over which summary statistics are derived. Value can be "MM" (midnight to midnight), "WW" (waking time to waking time), - or both c("MM","WW").} + "OO" (sleep onset to sleep onset), or any combination of them.} \item{save_ms5rawlevels}{ Boolean (default = FALSE). diff --git a/tests/testthat/test_chainof5parts.R b/tests/testthat/test_chainof5parts.R index 585952393..ace2f516d 100644 --- a/tests/testthat/test_chainof5parts.R +++ b/tests/testthat/test_chainof5parts.R @@ -201,7 +201,7 @@ test_that("chainof5parts", { overwrite = TRUE, excludefirstlast = FALSE, do.parallel = do.parallel, frag.metrics = "all", save_ms5rawlevels = TRUE, part5_agg2_60seconds = TRUE, do.sibreport = TRUE, nap_model = "hip3yr", - iglevels = 1) + iglevels = 1, timewindow = c("MM", "WW", "OO")) sibreport_dirname = "output_test/meta/ms5.outraw/sib.reports" expect_true(dir.exists(sibreport_dirname)) expect_true(file.exists(paste0(sibreport_dirname, "/sib_report_123A_testaccfile_T5A5.csv"))) @@ -214,9 +214,11 @@ test_that("chainof5parts", { expect_true(dir.exists(dirname)) expect_true(file.exists(rn[1])) - expect_that(nrow(output),equals(4)) + expect_that(nrow(output),equals(5)) # changed because OO window is exported expect_that(ncol(output),equals(197)) expect_that(round(as.numeric(output$wakeup[2]), digits = 4), equals(36)) + expect_that(as.numeric(output$dur_day_spt_min[4]), equals(1150)) # WW window duration + expect_that(as.numeric(output$dur_day_spt_min[5]), equals(1680)) # OO window duration dirname_raw = "output_test/meta/ms5.outraw/40_100_400" rn2 = dir(dirname_raw,full.names = TRUE, recursive = T) expect_true(file.exists(rn2[1])) @@ -240,6 +242,9 @@ test_that("chainof5parts", { expect_true(file.exists("output_test/results/part4_nightsummary_sleep_cleaned.csv")) expect_true(file.exists("output_test/results/part4_summary_sleep_cleaned.csv")) expect_true(file.exists("output_test/results/file summary reports/Report_123A_testaccfile.csv.pdf")) + expect_true(file.exists("output_test/results/part5_daysummary_MM_L40M100V400_T5A5.csv")) + expect_true(file.exists("output_test/results/part5_daysummary_WW_L40M100V400_T5A5.csv")) + expect_true(file.exists("output_test/results/part5_daysummary_OO_L40M100V400_T5A5.csv")) dn = "output_test" #======================= diff --git a/vignettes/GGIR.Rmd b/vignettes/GGIR.Rmd index 15cd603e4..6c3218ab5 100644 --- a/vignettes/GGIR.Rmd +++ b/vignettes/GGIR.Rmd @@ -482,8 +482,8 @@ For an explanation on how time use analysis is performed see section Here, the full 25 minutes would count towards the duration of the MVPA bout. - `timewindow` to specify whether days should be defined from midnight - to midnight `"MM"`, from waking-up to waking-up `"WW"`, or both - `c("MM","WW")`. + to midnight `"MM"`, from waking-up to waking-up `"WW"`, from sleep onset + to sleep onset `"OO"`, or any combination of them. - Configure durations of bouts: `boutdur.mvpa`, `boutdur.in`, and `boutdur.lig`. Note that this can be a vector of multiple values indicating the minimum and maximum duration of subsequent bout @@ -1618,7 +1618,7 @@ correspond to the date of analysis. | SleepPeriodTime | Is 1 if SPT is detected, 0 if not. Note that this refers to the combined usage of guider and detected sustained inactivity bouts (rest periods).| | invalidepoch | Is 1 if epoch was detect as invalid (e.g. non-wear), 0 if not. | | guider | Is 1 if guider method detect epoch as SPT (e.g. sleeplog, HDCZA), 0 if not. You will not find here which guider is used, for this see other GGIR output. | -| window | Numeric indicator of the analysis window in the recording. If timewindow = "MM" then these correspond to calendar days, if timewindow = "WW" then these correspond to which wakingup-wakingup window in the recording. So, in a recording of one week you may find window numbers 1, 2, 3, 4, 5 and 6.| +| window | Numeric indicator of the analysis window in the recording. If timewindow = "MM" then these correspond to calendar days, if timewindow = "WW" then these correspond to which wakingup-wakingup window in the recording, if timewindow = "OO" then these correspond to which sleeponset-sleeponset window in the recording. So, in a recording of one week you may find window numbers 1, 2, 3, 4, 5 and 6.| | class_id | The behavioural class codes are documented in the exported csv file meta/ms5outraw/behaviouralcodes.csv. Class codes above class 8 will be analysis specific, because it depends on the number time variants of the bouts used. For example, if you look at MVPA lasting 1-10, 10-20, 30-40 then all of them will have their own class_id. | | invalid_fullwindow | Fraction of the window (see above) that represents invalid data. I added this to make it easier to filter the timeseries based on whether days are valid or not. | | invalid_sleepperiod | Fraction of SPT within the current window that represents invalid data. |