knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4, message = FALSE, warning = FALSE ) library(cgmguru) iglu_available <- requireNamespace("iglu", quietly = TRUE) ggplot2_available <- requireNamespace("ggplot2", quietly = TRUE)
cgmguru provides high-performance tools for continuous glucose monitoring
(CGM) analysis. It combines two complementary workflows:
This guide expands the package-level vignette into a practical workflow. It is
based on the longer vignette("intro", package = "cgmguru") source and focuses
on how to move from a raw CGM table to subject summaries, event tables, GRID
starts, postprandial peaks, and simple visual checks.
Most cgmguru functions expect a data frame with these columns:
| Column | Meaning |
| --- | --- |
| id | Subject identifier |
| time | POSIXct timestamp |
| gl | Glucose value in mg/dL |
Rows may contain multiple subjects. For speed and reproducibility, it is good
practice to order by id and time before analysis, especially when chaining
several functions.
data(example_data_5_subject, package = "iglu") data(example_data_hall, package = "iglu") cgm_data <- orderfast(example_data_5_subject) data.frame( rows = nrow(cgm_data), subjects = length(unique(cgm_data$id)), first_time = min(cgm_data$time), last_time = max(cgm_data$time), min_glucose = min(cgm_data$gl, na.rm = TRUE), max_glucose = max(cgm_data$gl, na.rm = TRUE) )
cat("The 'iglu' package is not available, so example-data chunks are skipped.\n")
The package intentionally separates event-grid analysis from GRID-family analysis.
Glycemic event functions use an iglu-compatible event grid:
detect_hypoglycemic_events()detect_hyperglycemic_events()detect_all_events()interpolate_cgm()These functions can infer reading_minutes per subject from the median
positive timestamp spacing. They align to a full-day grid, interpolate gaps up
to inter_gap, remove larger gap-masked rows, and classify events by
contiguous segments.
GRID-family functions operate on the rows you pass in:
grid()mod_grid()maxima_grid()find_local_maxima()excursion()They do not automatically call the event-grid interpolation pipeline. If you want GRID analysis on an interpolated series, explicitly pass the interpolated data.
sensor_wear() calculates how much observed CGM data are present. By default it
uses each subject's original timestamp span. Supplying ndays switches to a
fixed retrospective window ending at each subject's last valid timestamp, or at
a common end_date if you provide one.
sensor_wear(cgm_data, reading_minutes = 5) sensor_wear(cgm_data, ndays = 14, reading_minutes = 5)
For a broader one-row-per-subject summary, use detect_all_events().
all_events <- detect_all_events( cgm_data, reading_minutes = 5, sensor_wear_ndays = 14 ) names(all_events) head(all_events$subject_summary) head(all_events$glycemic_event_summary)
The subject_summary table includes CGM summary metrics such as time in range,
time below range, time above range, mean glucose, variability, GMI/uGMI, GRI,
and sensor_wear_percent. By default, these summary glucose metrics are
calculated from the original raw glucose values. Set
summary_metrics_source = "preprocessed" to calculate them from the internal
event grid after interpolation and gap masking.
all_events_preprocessed <- detect_all_events( cgm_data, reading_minutes = 5, summary_metrics_source = "preprocessed" ) head(all_events_preprocessed$subject_summary)
When you need to audit event boundaries, use return_interpolated = TRUE. The
returned interpolated_data is the grid used internally for event
classification.
all_events_with_grid <- detect_all_events( cgm_data, reading_minutes = 5, return_interpolated = TRUE ) names(all_events_with_grid) head(all_events_with_grid$interpolated_data)
The standalone helper interpolate_cgm() is useful when you want to inspect
preprocessing before running event detectors.
event_grid <- interpolate_cgm(cgm_data, reading_minutes = 5) head(event_grid)
Use the standalone event functions when you need detailed event boundaries for
one event type. Presets are available through type = "lv1", "lv2",
"extended", and "lv1_excl".
hyper_lv1 <- detect_hyperglycemic_events( cgm_data, type = "lv1", reading_minutes = 5, return_interpolated = FALSE ) hypo_lv1 <- detect_hypoglycemic_events( cgm_data, type = "lv1", reading_minutes = 5, return_interpolated = FALSE ) hyper_lv1$events_total hypo_lv1$events_total head(hyper_lv1$events_detailed)
detect_all_events() is usually the most convenient interface for reporting,
because it aggregates hypo- and hyperglycemia definitions in one call.
nonzero_events <- all_events$glycemic_event_summary[ all_events$glycemic_event_summary$total_episodes > 0, ] head(nonzero_events)
The GRID (Glucose Rate Increase Detector) workflow detects rapid glucose rises, often used as candidate meal or postprandial start points.
grid_result <- grid(cgm_data, gap = 15, threshold = 130) grid_result$episode_counts head(grid_result$episode_start) head(grid_result$grid_vector)
Lowering the threshold or gap generally makes detection more sensitive.
sensitive_grid <- grid(cgm_data, gap = 10, threshold = 120) head(sensitive_grid$episode_counts)
maxima_grid() combines GRID starts with local maxima to identify likely
postprandial peaks within a time window.
maxima_result <- maxima_grid( cgm_data, threshold = 130, gap = 60, hours = 2 ) maxima_result$episode_counts head(maxima_result$results)
For a more explicit step-by-step version of the workflow, chain the lower-level helpers.
grid_starts <- start_finder(grid_result$grid_vector) mod_grid_result <- mod_grid( cgm_data, grid_starts, hours = 2, gap = 60 ) mod_grid_starts <- start_finder(mod_grid_result$mod_grid_vector) max_after_result <- find_max_after_hours( cgm_data, mod_grid_starts, hours = 2 ) local_maxima <- find_local_maxima(cgm_data) new_maxima <- find_new_maxima( cgm_data, max_after_result$max_index, local_maxima$local_maxima_vector ) mapped_maxima <- transform_df(grid_result$episode_start, new_maxima) between_maxima <- detect_between_maxima(cgm_data, mapped_maxima) head(mapped_maxima) head(between_maxima$results)
excursion() identifies glucose excursions, defined as rises greater than
70 mg/dL within 2 hours, excluding starts preceded by hypoglycemia.
excursion_result <- excursion(cgm_data, gap = 15) excursion_result$episode_counts head(excursion_result$episode_start)
Visual checks are useful after tuning thresholds. The plot below shows one subject's glucose trace, clinical thresholds, and GRID episode starts.
subject_id <- unique(cgm_data$id)[1] subject_data <- cgm_data[cgm_data$id == subject_id, ] subject_grid_starts <- grid_result$episode_start[ grid_result$episode_start$id == subject_id, ] ggplot2::ggplot(subject_data, ggplot2::aes(x = time, y = gl)) + ggplot2::geom_line(linewidth = 0.3, color = "steelblue") + ggplot2::geom_hline(yintercept = c(54, 70), linetype = "dashed", color = "darkorange") + ggplot2::geom_hline(yintercept = c(180, 250), linetype = "dashed", color = "firebrick") + ggplot2::geom_point( data = subject_grid_starts, ggplot2::aes(x = time, y = gl), color = "black", size = 1.4 ) + ggplot2::labs( title = paste("CGM Trace and GRID Starts:", subject_id), x = "Time", y = "Glucose (mg/dL)" ) + ggplot2::theme_minimal()
cat("The 'ggplot2' package is not available, so the plot is skipped.\n")
The same workflow applies to larger multi-subject datasets. The example below
uses example_data_hall from iglu.
hall_data <- orderfast(example_data_hall) hall_summary <- detect_all_events(hall_data, reading_minutes = 5) hall_grid <- grid(hall_data, gap = 15, threshold = 130) data.frame( subjects = length(unique(hall_data$id)), rows = nrow(hall_data), summary_rows = nrow(hall_summary$subject_summary), grid_rows = nrow(hall_grid$grid_vector) )
| Task | Main function |
| --- | --- |
| Order CGM rows | orderfast() |
| Calculate observed data coverage | sensor_wear() |
| Build an event preprocessing grid | interpolate_cgm() |
| Report all event summaries | detect_all_events() |
| Detect one hypo/hyper event type | detect_hypoglycemic_events(), detect_hyperglycemic_events() |
| Detect rapid glucose rises | grid() |
| Detect local peaks | find_local_maxima() |
| Combine GRID starts and peaks | maxima_grid() |
| Find peaks/minima around starts | find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours() |
| Refine and map postprandial peaks | find_new_maxima(), transform_df(), detect_between_maxima() |
| Detect large glucose excursions | excursion() |
For more detailed examples, open the focused vignettes:
browseVignettes("cgmguru") vignette("intro", package = "cgmguru") vignette("detect_all_events", package = "cgmguru") vignette("grid", package = "cgmguru") vignette("maxima_grid", package = "cgmguru")
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.