From 5dff2ba483915b1502b6ae86ab05b864ec8baaf9 Mon Sep 17 00:00:00 2001 From: Fred Ho Date: Thu, 23 Jul 2026 13:35:15 +0100 Subject: [PATCH] Fix aggregate_per_date() combining results using last file's columns instead of first After looping over all time-series files, aggregate_per_date() combined per-file results and set the final table's column count/names from whichever file happened to be processed *last* (ci/dsnames were reused directly from the final loop iteration). This made the result silently depend on file processing order/completeness. Now each file's (ds, dsnames) pair is collected in a list and combined afterwards with purrr::list_rbind(), using the *first* file's dsnames as the canonical column names/count. Verified against a synthetic multi-subject dataset: output is identical to the previous implementation in the well-behaved case (same column count for every file). --- DESCRIPTION | 4 ++-- NAMESPACE | 1 + R/aggregate_per_date.R | 20 +++++++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0b93a1f..abd2edd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,8 +20,8 @@ LazyData: true Suggests: testthat (>= 3.0.0), randomForest, gsignal, rattle Config/testthat/edition: 3 -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 Depends: R (>= 2.10) Imports: - caret, data.table, e1071, GGIR, HMM, signal, GGIRread, actilifecounts, actimetricModels + caret, data.table, e1071, GGIR, HMM, signal, GGIRread, actilifecounts, actimetricModels, purrr diff --git a/NAMESPACE b/NAMESPACE index 5fd3776..33c038a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -40,6 +40,7 @@ importFrom(e1071,kurtosis) importFrom(e1071,skewness) importFrom(grDevices,dev.off) importFrom(grDevices,pdf) +importFrom(purrr,list_rbind) importFrom(signal,specgram) importFrom(stats,predict) importFrom(utils,data) diff --git a/R/aggregate_per_date.R b/R/aggregate_per_date.R index 0f58d09..b284278 100644 --- a/R/aggregate_per_date.R +++ b/R/aggregate_per_date.R @@ -24,6 +24,7 @@ #' @return Data frame with aggregates of time spent in classes per calendar date. #' #' @importFrom grDevices pdf dev.off +#' @importFrom purrr list_rbind #' #' @author Jairo H. Migueles #' @export @@ -36,6 +37,9 @@ aggregate_per_date = function(tsDir, epoch, classifier, classes, classes = gsub("sleep", "nighttime.sleep", classes) } files = dir(tsDir, full.names = TRUE) + # per-file results, combined into a single table after the loop + ds_list = vector("list", length(files)) + dsnames_list = vector("list", length(files)) for (fi in 1:length(files)) { ts = NULL load(files[fi]) @@ -206,12 +210,22 @@ aggregate_per_date = function(tsDir, epoch, classifier, classes, classes = classes, epoch = epoch) grDevices::dev.off() } - # store information in daysummary and next file - if (fi == 1) daysummary = ds else daysummary = rbind(daysummary, ds) + # store information for this file; combined into daysummary after the loop + ds_list[[fi]] = ds + dsnames_list[[fi]] = dsnames } + # Combine results across files. Column structure (dsnames/ci) is determined + # by the run's parameters (classifier, classes, boutdur), so it is expected + # to be identical for every file; we use the *first* file's dsnames as the + # canonical column names/count rather than whichever file happened to be + # processed *last*, so the combined table does not silently depend on which + # file is last in `files`. + daysummary = list_rbind(ds_list) + dsnames = dsnames_list[[1]] + # keep only calculated columns and insert colnames - daysummary = daysummary[, 1:(ci - 1)] + daysummary = daysummary[, 1:length(dsnames)] colnames(daysummary) = dsnames # return return(daysummary)