Nothing
# Helpers for round-tripping CGM data through the Dexcom readers of other packages
#' Write CGM readings to a Dexcom-style CSV
#'
#' One fixture serves both readers that aidR is tested against:
#'
#' * `cgmquantify::readfile()` picks the columns
#' `Timestamp (YYYY-MM-DDThh:mm:ss)` and `Glucose Value (mg/dL)` (which
#' `read.csv()` mangles into the names it actually indexes) and ends with
#' `tail(df, -11)` to strip Dexcom's metadata block. Exactly 11 header rows
#' are therefore written, so that it returns exactly the readings passed here.
#' * `iglu::read_raw_data(sensor = "dexcom")` drops every row whose
#' `Event Type` is not `"EGV"` instead, and takes the first column matching
#' "glucose" as the glucose value.
#'
#' The glucose values are written with 17 significant digits rather than through
#' `as.character()`, which `write.csv()` would use and which keeps only 15. aidR
#' converts CGM data to mg/dL, so a value can carry conversion noise far below
#' any relevant digit (e.g. 97.999999999999986), and at 15 digits that collapses
#' to "98" - numerically irrelevant, but enough to move a reading across a bin
#' boundary and make e.g. `iglu::hist_roc()` differ.
#'
#' Note that 17 digits still does not make the round-trip bit-exact: R parses
#' decimal strings up to one ulp low (`as.numeric("96.000000000000014")` returns
#' exactly 96), in `read.csv()` and in the parser alike. A CSV round-trip in R
#' therefore cannot preserve an arbitrary double, and the comparisons against
#' the readers are tolerance-based, as `expect_equal()` is by default.
#'
#' @param time A vector of timestamps.
#' @param glucose A numeric vector of glucose values, in mg/dL.
#' @param path Character string with the file to write.
write_dexcom_csv <- function(time, glucose, path){
# The 11 metadata rows of a Dexcom export, which carry no reading
header <- data.frame(
event_type = c("FirstName", "LastName", "Device", "SerialNumber",
"TransmitterId", "TransmitterTime", "AlertSettings",
"AlertSettings", "AlertSettings", "AlertSettings",
"AlertSettings"),
timestamp = NA_character_,
glucose = NA_character_
)
readings <- data.frame(
event_type = "EGV",
timestamp = format(time, "%Y-%m-%dT%H:%M:%S"),
glucose = sprintf("%.17g", as.numeric(glucose))
)
out <- rbind(header, readings)
names(out) <- c("Event Type", "Timestamp (YYYY-MM-DDThh:mm:ss)",
"Glucose Value (mg/dL)")
utils::write.csv(out, path, row.names = FALSE, na = "")
invisible(path)
}
#' Run the session in UTC for the duration of a test
#'
#' `cgmquantify::readfile()` parses the Dexcom timestamps with `as.POSIXct()`
#' without a timezone, i.e. in the session timezone, and then derives `Date` with
#' `as.Date()`, which converts to UTC first. Its `Date` column therefore shifts
#' by a day around midnight whenever the session is not in UTC, while `aidR`
#' derives the date from the local (wall-clock) time. Forcing UTC makes the two
#' directly comparable. Not needed for `iglu::read_raw_data()`, which takes the
#' timezone as an argument.
local_utc <- function(envir = parent.frame()){
old_tz <- Sys.getenv("TZ", unset = NA)
Sys.setenv(TZ = "UTC")
withr::defer({
if (is.na(old_tz)){ Sys.unsetenv("TZ") } else { Sys.setenv(TZ = old_tz) }
}, envir = envir)
}
#' The cgmquantify functions that summarize a CGM data frame
#'
#' All of them take the data frame as their only mandatory argument and return a
#' numeric value or a data frame.
cgmquantify_metrics <- function(){
c("interdaysd", "interdaycv", "intradaysd", "intradaycv",
"TIR", "TOR", "POR", "MGE", "MGN", "J_index",
"LBGI", "HBGI", "LBGI_HBGI", "eA1c", "GMI", "summary_glucose")
}
#' The iglu functions that summarize a CGM data frame
#'
#' All exported iglu functions whose first argument is \code{data}, that run on a
#' single subject without further arguments, and that return a value rather than
#' a plot, i.e. every metric, profile and transformation function.
#'
#' \code{agp()} is not among them: it returns a patchwork of the plots that
#' \code{plot_agp()} and \code{plot_daily()} draw and the numbers that
#' \code{agp_metrics()} reports - all three are compared separately - and its
#' grid grobs are named from a global counter, so two calls never compare equal.
iglu_metrics <- function(){
c("above_percent", "active_percent", "adrr", "agp_metrics",
"all_metrics", "auc", "below_percent", "CGMS2DayByDay", "cogi", "conga",
"cv_glu", "cv_measures", "ea1c", "episode_calculation", "gmi", "grade",
"grade_eugly", "grade_hyper", "grade_hypo", "gri", "gvp", "hbgi",
"hyper_index", "hypo_index", "igc", "in_range_percent",
"iqr_glu", "j_index", "lbgi", "m_value", "mad_glu", "mag", "mage",
"mean_glu", "median_glu", "modd", "pgs", "quantile_glu", "range_glu",
"roc", "sd_glu", "sd_measures", "sd_roc", "summary_glu")
}
#' The iglu functions that draw a CGM data frame
#'
#' Compared through \code{ggplot2::layer_data()}, i.e. on the data they draw,
#' rather than as objects.
iglu_plots <- function(){
c("plot_glu", "plot_daily", "plot_agp", "plot_ranges", "hist_roc")
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.