Package {cgmguru}


Type: Package
Title: Advanced Continuous Glucose Monitoring Analysis with High-Performance C++ Backend
Version: 0.2.0
Maintainer: Sang Ho Park <shstat1729@gmail.com>
Description: Tools for advanced analysis of continuous glucose monitoring (CGM) time-series, implementing GRID (Glucose Rate Increase Detector) and GRID-based algorithms for postprandial peak detection, and detection of hypoglycemic and hyperglycemic episodes (Levels 1/2/Extended) aligned with international consensus CGM metrics. Core algorithms are implemented in optimized C++ using 'Rcpp' to provide accurate and fast analysis on large datasets.
License: MIT + file LICENSE
Encoding: UTF-8
RoxygenNote: 7.3.3
LinkingTo: Rcpp
Imports: Rcpp
Suggests: testthat (≥ 3.0.0), knitr, rmarkdown, iglu, dplyr, covr, ggplot2, microbenchmark
VignetteBuilder: knitr
URL: https://github.com/shstat1729/cgmguru
BugReports: https://github.com/shstat1729/cgmguru/issues
Config/testthat/edition: 3
NeedsCompilation: yes
Packaged: 2026-05-07 05:49:21 UTC; bagsangho
Author: Sang Ho Park [aut, cre], Rosa Oh [aut, ctb], Sang-Man Jin [aut, ctb]
Repository: CRAN
Date/Publication: 2026-05-07 06:20:02 UTC

Advanced Continuous Glucose Monitoring Analysis and GRID-Based Event Detection

Description

A high-performance R package for comprehensive Continuous Glucose Monitoring (CGM) data analysis with optimized C++ implementations. The package provides advanced tools for CGM data analysis with two primary capabilities: GRID and postprandial peak detection, and extended glycemic events detection aligned with international consensus CGM metrics.

Details

The package implements several key algorithms for CGM analysis:

Core algorithms are implemented in optimized C++ via 'Rcpp' for accurate and fast analysis on large datasets, making the package suitable for both research and clinical applications.

Main Functions

grid

GRID algorithm for detecting rapid glucose rate increases

maxima_grid

Combined maxima detection and GRID analysis for postprandial peaks

detect_hyperglycemic_events

Hyperglycemic event detection (Level 1/2/Extended)

detect_hypoglycemic_events

Hypoglycemic event detection (Level 1/2/Extended)

detect_all_events

Comprehensive detection of all glycemic event types

find_local_maxima

Local maxima identification in glucose time series

orderfast

Fast dataframe ordering utility

Data Requirements

Input dataframes should contain:

All function arguments and return values are expected to be in tibble format. For convenience, single-column parameters can be passed as vectors in R, which will be automatically converted to single-column tibbles.

Examples

# Basic GRID analysis
result <- grid(cgm_data, gap = 15, threshold = 130)

# Postprandial peak detection (GRID-based)
maxima <- maxima_grid(cgm_data, threshold = 130, gap = 60, hours = 2)

# Level 1 Hyperglycemic event detection
events <- detect_hyperglycemic_events(cgm_data, type = "lv1")

# Comprehensive event detection
all_events <- detect_all_events(cgm_data, reading_minutes = 5)

Author(s)

Sang Ho Park shstat1729@gmail.com

References

For more information about the GRID algorithm and CGM analysis methodologies, see the package vignette: vignette("intro", package = "cgmguru")

See Also

grid, maxima_grid, detect_hyperglycemic_events, detect_all_events


Detect All Glycemic Events

Description

Comprehensive function to detect all types of glycemic events aligned with international consensus CGM metrics (Battelino et al., 2023). This function provides a unified interface for detecting multiple event types including Level 1/2/Extended hypo- and hyperglycemia, and Level 1 excluded events. Events are counted only after the required recovery condition is confirmed; duration summaries use the event boundary immediately before recovery starts.

Usage

detect_all_events(df, reading_minutes = NULL)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

reading_minutes

Time interval between readings in minutes (optional). Can be a single integer/numeric value (applied to all subjects) or a vector matching data length (different intervals per subject)

Value

A tibble containing comprehensive event analysis with columns:

Event types

- Hypoglycemia: lv1 (< 70 mg/dL, \geq 15 min), lv2 (< 54 mg/dL, \geq 15 min), extended (< 70 mg/dL, \geq 120 min). - Hyperglycemia: lv1 (> 180 mg/dL, \geq 15 min), lv2 (> 250 mg/dL, \geq 15 min), extended (> 250 mg/dL, \geq 90 min in 120 min, end \leq 180 mg/dL for \geq 15 min).

References

Battelino, T., et al. (2023). Continuous glucose monitoring and metrics for clinical trials: an international consensus statement. The Lancet Diabetes & Endocrinology, 11(1), 42-57.

See Also

detect_hyperglycemic_events, detect_hypoglycemic_events

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Detect all glycemic events with 5-minute reading intervals
all_events <- detect_all_events(example_data_5_subject, reading_minutes = 5)
print(all_events)

# Detect all events on larger dataset
large_all_events <- detect_all_events(example_data_hall, reading_minutes = 5)
print(paste("Total event types analyzed:", nrow(large_all_events)))

# Filter for specific event types
hyperglycemia_events <- all_events[all_events$type == "hyper", ]
hypoglycemia_events <- all_events[all_events$type == "hypo", ]

print("Hyperglycemia events:")
print(hyperglycemia_events)
print("Hypoglycemia events:")
print(hypoglycemia_events)

Detect Events Between Maxima

Description

Identifies and analyzes events occurring between detected maxima points, providing detailed episode information for GRID analysis. This function helps characterize the glucose dynamics between identified peaks.

Usage

detect_between_maxima(df, transform_df)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

transform_df

A dataframe containing summary information from previous transformations

Value

A list containing:

See Also

grid, mod_grid, find_new_maxima, transform_df

Other GRID pipeline: find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Complete pipeline to get transform_df
grid_result <- grid(example_data_5_subject, gap = 60, threshold = 130)
maxima_result <- find_local_maxima(example_data_5_subject)
mod_result <- mod_grid(example_data_5_subject, grid_result$grid_vector, hours = 2, gap = 60)
max_after <- find_max_after_hours(example_data_5_subject, mod_result$mod_grid_vector, hours = 2)
new_maxima <- find_new_maxima(example_data_5_subject,
                              max_after$max_index,
                              maxima_result$local_maxima_vector)
transformed <- transform_df(grid_result$episode_start, new_maxima)

# Detect events between maxima
between_events <- detect_between_maxima(example_data_5_subject, transformed)
print(paste("Events between maxima:", length(between_events)))

# Analysis on larger dataset
large_grid <- grid(example_data_hall, gap = 60, threshold = 130)
large_maxima <- find_local_maxima(example_data_hall)
large_mod <- mod_grid(example_data_hall, large_grid$grid_vector, hours = 2, gap = 60)
large_max_after <- find_max_after_hours(example_data_hall, large_mod$mod_grid_vector, hours = 2)
large_new_maxima <- find_new_maxima(example_data_hall,
                                    large_max_after$max_index,
                                    large_maxima$local_maxima_vector)
large_transformed <- transform_df(large_grid$episode_start, large_new_maxima)
large_between <- detect_between_maxima(example_data_hall, large_transformed)
print(paste("Events between maxima in larger dataset:", length(large_between)))

Detect Hyperglycemic Events

Description

Identifies and segments hyperglycemic events in CGM data based on international consensus CGM metrics (Battelino et al., 2023). Use type to select one of three event definitions:

Events are counted only after glucose remains at or below the end threshold for the specified end length. In events_detailed, end_time, end_glucose, and end_index report the last hyperglycemic reading immediately before that confirmed recovery period starts.

Usage

detect_hyperglycemic_events(df, ..., type = "extended",
 reading_minutes = NULL)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

...

Custom event criteria supplied by name. Prefer type for standard Level 1, Level 2, and Extended events. Supported custom criteria are:

  • dur_length: Minimum event duration in minutes required for an event to qualify.

  • end_length: Required recovery duration in minutes before event termination is confirmed.

  • start_gl: Glucose threshold in mg/dL used to qualify hyperglycemic readings. Hyperglycemic readings are above this value.

  • end_gl: Glucose threshold in mg/dL used to confirm recovery. Hyperglycemic events end after glucose remains at or below this value for end_length minutes.

type

Hyperglycemia event definition. One of "extended" (default), "lv1", or "lv2".

reading_minutes

Time interval between readings in minutes (optional)

Value

A list containing:

Methods

Hyperglycemic events can be detected using either the recommended type argument or named custom threshold and duration criteria.

1. Preset method using type (recommended): Use type when you want the standard Level 1, Level 2, or Extended hyperglycemia definitions without manually entering thresholds.

2. Custom criteria method: Supply start_gl, dur_length, end_length, and end_gl directly when using a custom definition, for example detect_hyperglycemic_events(df, start_gl = 180, dur_length = 15, end_length = 15, end_gl = 180) for Level 1 hyperglycemia. If an explicit type is supplied together with custom numeric criteria, the function returns results based on type; the custom criteria are ignored and a warning is issued.

Units and sampling

- reading_minutes can be a scalar (all rows) or a vector per-row. - If reading_minutes is NULL, duration is computed from time deltas.

References

Battelino, T., et al. (2023). Continuous glucose monitoring and metrics for clinical trials: an international consensus statement. The Lancet Diabetes & Endocrinology, 11(1), 42-57.

See Also

detect_all_events

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Level 1 Hyperglycemia Event (>=15 consecutive min of >180 mg/dL and event
# ends when there is >=15 consecutive min with a CGM sensor value of <=180 mg/dL)
hyper_lv1 <- detect_hyperglycemic_events(example_data_5_subject, type = "lv1")
print(hyper_lv1$events_total)

# Level 2 Hyperglycemia Event (>=15 consecutive min of >250 mg/dL and event
# ends when there is >=15 consecutive min with a CGM sensor value of <=250 mg/dL)
hyper_lv2 <- detect_hyperglycemic_events(example_data_5_subject, type = "lv2")
print(hyper_lv2$events_total)

# Extended Hyperglycemia Event (>250 mg/dL lasting >=90 cumulative min within a
# 120-min period, ends when glucose returns to <=180 mg/dL for >=15 consecutive
# min after)
hyper_extended <- detect_hyperglycemic_events(example_data_5_subject, type = "extended")
print(hyper_extended$events_total)

# Custom criteria method for the same standard definitions
hyper_lv1_custom <- detect_hyperglycemic_events(
  example_data_5_subject,
  start_gl = 180,
  dur_length = 15,
  end_length = 15,
  end_gl = 180
)
hyper_lv2_custom <- detect_hyperglycemic_events(
  example_data_5_subject,
  start_gl = 250,
  dur_length = 15,
  end_length = 15,
  end_gl = 250
)
hyper_extended_custom <- detect_hyperglycemic_events(
  example_data_5_subject,
  start_gl = 250,
  dur_length = 120,
  end_length = 15,
  end_gl = 180
)

# Compare event rates across levels
cat("Level 1 events:", sum(hyper_lv1$events_total$total_events), "\n")
cat("Level 2 events:", sum(hyper_lv2$events_total$total_events), "\n")
cat("Extended events:", sum(hyper_extended$events_total$total_events), "\n")

# Analysis on larger dataset with Level 1 criteria
large_hyper <- detect_hyperglycemic_events(example_data_hall, type = "lv1")
print(large_hyper$events_total)

# Analysis on larger dataset with Level 2 criteria
large_hyper_lv2 <- detect_hyperglycemic_events(example_data_hall, type = "lv2")
print(large_hyper_lv2$events_total)

# Analysis on larger dataset with Extended criteria
large_hyper_extended <- detect_hyperglycemic_events(example_data_hall, type = "extended")
print(large_hyper_extended$events_total)

# View detailed events for specific subject
if(nrow(hyper_lv1$events_detailed) > 0) {
  first_subject <- hyper_lv1$events_detailed$id[1]
  subject_events <- hyper_lv1$events_detailed[hyper_lv1$events_detailed$id == first_subject, ]
  head(subject_events)
}

Detect Hypoglycemic Events

Description

Identifies and segments hypoglycemic events in CGM data based on international consensus CGM metrics (Battelino et al., 2023). Use type to select one of three event definitions:

Events are counted only after glucose remains at or above the recovery threshold for the specified end length. In events_detailed, end_time, end_glucose, and end_index report the last hypoglycemic reading immediately before that confirmed recovery period starts.

Usage

detect_hypoglycemic_events(df, ..., type = "extended",
 reading_minutes = NULL)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

...

Custom event criteria supplied by name. Prefer type for standard Level 1, Level 2, and Extended events. Supported custom criteria are:

  • dur_length: Minimum event duration in minutes required for an event to qualify.

  • end_length: Required recovery duration in minutes before event termination is confirmed.

  • start_gl: Glucose threshold in mg/dL used to qualify hypoglycemic readings. Hypoglycemic readings are below this value, and recovery is confirmed after glucose remains at or above this value for end_length minutes.

type

Hypoglycemia event definition. One of "extended" (default), "lv1", or "lv2".

reading_minutes

Time interval between readings in minutes (optional)

Value

A list containing:

Methods

Hypoglycemic events can be detected using either the recommended type argument or named custom threshold and duration criteria.

1. Preset method using type (recommended): Use type when you want the standard Level 1, Level 2, or Extended hypoglycemia definitions without manually entering thresholds.

2. Custom criteria method: Supply start_gl, dur_length, and end_length directly when using a custom definition, for example detect_hypoglycemic_events(df, start_gl = 70, dur_length = 15, end_length = 15) for Level 1 hypoglycemia. If an explicit type is supplied together with custom numeric criteria, the function returns results based on type; the custom criteria are ignored and a warning is issued.

Units and sampling

- reading_minutes can be a scalar (all rows) or a vector per-row. - If reading_minutes is NULL, duration is computed from time deltas.

References

Battelino, T., et al. (2023). Continuous glucose monitoring and metrics for clinical trials: an international consensus statement. The Lancet Diabetes & Endocrinology, 11(1), 42-57.

See Also

detect_all_events

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Level 1 Hypoglycemia Event (>=15 consecutive min of <70 mg/dL and event
# ends when there is >=15 consecutive min with a CGM sensor value of >=70 mg/dL)
hypo_lv1 <- detect_hypoglycemic_events(example_data_5_subject, type = "lv1")
print(hypo_lv1$events_total)

# Level 2 Hypoglycemia Event (>=15 consecutive min of <54 mg/dL and event
# ends when there is >=15 consecutive min with a CGM sensor value of >=54 mg/dL)
hypo_lv2 <- detect_hypoglycemic_events(example_data_5_subject, type = "lv2")

# Extended Hypoglycemia Event (>120 consecutive min of <70 mg/dL and event
# ends when there is >=15 consecutive min with a CGM sensor value of >=70 mg/dL)
hypo_extended <- detect_hypoglycemic_events(example_data_5_subject, type = "extended")
print(hypo_extended$events_total)

# Custom criteria method for the same standard definitions
hypo_lv1_custom <- detect_hypoglycemic_events(
  example_data_5_subject,
  start_gl = 70,
  dur_length = 15,
  end_length = 15
)
hypo_lv2_custom <- detect_hypoglycemic_events(
  example_data_5_subject,
  start_gl = 54,
  dur_length = 15,
  end_length = 15
)
hypo_extended_custom <- detect_hypoglycemic_events(
  example_data_5_subject,
  start_gl = 70,
  dur_length = 120,
  end_length = 15
)

# Compare event rates across levels
cat("Level 1 events:", sum(hypo_lv1$events_total$total_events), "\n")
cat("Level 2 events:", sum(hypo_lv2$events_total$total_events), "\n")
cat("Extended events:", sum(hypo_extended$events_total$total_events), "\n")

# Analysis on larger dataset with Level 1 criteria
large_hypo <- detect_hypoglycemic_events(example_data_hall, type = "lv1")
print(large_hypo$events_total)

# Analysis on larger dataset with Level 2 criteria
large_hypo_lv2 <- detect_hypoglycemic_events(example_data_hall, type = "lv2")
print(large_hypo_lv2$events_total)

# Analysis on larger dataset with Extended criteria
large_hypo_extended <- detect_hypoglycemic_events(example_data_hall, type = "extended")
print(large_hypo_extended$events_total)

Calculate Glucose Excursions

Description

Calculates glucose excursions in CGM data. An excursion is defined as a > 70 mg/dL (> 3.9 mmol/L) rise within 2 hours, not preceded by a value < 70 mg/dL (< 3.9 mmol/L).

Usage

excursion(df, gap = 15)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

gap

Gap threshold in minutes for excursion calculation (default: 15). This parameter defines the minimum time interval between consecutive GRID events.

Value

A list containing:

Notes

- gap is minutes; change to enforce minimum separation between excursions.

References

Edwards, S., et al. (2022). Use of connected pen as a diagnostic tool to evaluate missed bolus dosing behavior in people with type 1 and type 2 diabetes. Diabetes Technology & Therapeutics, 24(1), 61-66.

See Also

grid

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Calculate glucose excursions
excursion_result <- excursion(example_data_5_subject, gap = 15)
print(paste("Excursion vector length:", length(excursion_result$excursion_vector)))
print(excursion_result$episode_counts)

# Excursion analysis with different gap
excursion_30min <- excursion(example_data_5_subject, gap = 30)

# Analysis on larger dataset
large_excursion <- excursion(example_data_hall, gap = 15)
print(paste("Excursion vector length in larger dataset:", length(large_excursion$excursion_vector)))
print(paste("Total episodes:", sum(large_excursion$episode_counts$episode_counts)))

Find Local Maxima in Glucose Time Series

Description

Identifies local maxima (peaks) in glucose concentration time series data. Uses a difference-based algorithm to detect peaks where glucose values increase or remain constant for two consecutive points before the peak point, and decrease or remain constant for two consecutive points after the peak point.

Usage

find_local_maxima(df)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

Value

A list containing:

See Also

grid, mod_grid, find_new_maxima

Other GRID pipeline: detect_between_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Find local maxima
maxima_result <- find_local_maxima(example_data_5_subject)
print(paste("Found", nrow(maxima_result$local_maxima_vector), "local maxima"))

# Find maxima on larger dataset
large_maxima <- find_local_maxima(example_data_hall)
print(paste("Found", nrow(large_maxima$local_maxima_vector), "local maxima in larger dataset"))

# View first few maxima
head(maxima_result$local_maxima_vector)

# View merged results
head(maxima_result$merged_results)

Find Maximum Glucose After Specified Hours

Description

Identifies the maximum glucose value occurring within a specified time window after a given start point. This function is useful for analyzing glucose patterns following specific events or time points.

Usage

find_max_after_hours(df, start_point_df, hours)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

start_point_df

A dataframe with column start_index (R-based index into df)

hours

Number of hours to look ahead from the start point

Value

A list containing:

Notes

- start_index must be valid row numbers in df (1-indexed). - The search window is (0, hours] hours after each start index.

See Also

mod_grid, find_local_maxima, find_new_maxima, transform_df

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Create start points for demonstration (using row index)
start_index <- seq(1, nrow(example_data_5_subject), by = 100)
start_points <- data.frame(start_index = start_index)

# Find maximum glucose in next 2 hours
max_after <- find_max_after_hours(example_data_5_subject, start_points, hours = 2)
print(paste("Found", length(max_after$max_index), "maximum points"))

# Find maximum glucose in next 1 hour
max_after_1h <- find_max_after_hours(example_data_5_subject, start_points, hours = 1)

# Analysis on larger dataset
large_start_index <- seq(1, nrow(example_data_hall), by = 200)
large_start_points <- data.frame(start_index = large_start_index)
large_max_after <- find_max_after_hours(example_data_hall, large_start_points, hours = 2)
print(paste("Found", length(large_max_after$max_index), "maximum points in larger dataset"))

Find Maximum Glucose Before Specified Hours

Description

Identifies the maximum glucose value occurring within a specified time window before a given start point. This function is useful for analyzing glucose patterns preceding specific events or time points.

Usage

find_max_before_hours(df, start_point_df, hours)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

start_point_df

A dataframe with column start_index (R-based index into df)

hours

Number of hours to look back from the start point

Value

A list containing:

Notes

- The search window is [hours, 0) hours before each start index.

See Also

mod_grid, find_local_maxima, find_new_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Create start points for demonstration (using row index)
start_index <- seq(1, nrow(example_data_5_subject), by = 100)
start_points <- data.frame(start_index = start_index)

# Find maximum glucose in previous 2 hours
max_before <- find_max_before_hours(example_data_5_subject, start_points, hours = 2)
print(paste("Found", length(max_before$max_index), "maximum points"))

# Find maximum glucose in previous 1 hour
max_before_1h <- find_max_before_hours(example_data_5_subject, start_points, hours = 1)

# Analysis on larger dataset
large_start_index <- seq(1, nrow(example_data_hall), by = 200)
large_start_points <- data.frame(start_index = large_start_index)
large_max_before <- find_max_before_hours(example_data_hall, large_start_points, hours = 2)
print(paste("Found", length(large_max_before$max_index), "maximum points in larger dataset"))

Find Minimum Glucose After Specified Hours

Description

Identifies the minimum glucose value occurring within a specified time window after a given start point. This function is useful for analyzing glucose patterns following specific events or time points.

Usage

find_min_after_hours(df, start_point_df, hours)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

start_point_df

A dataframe with column start_index (R-based index into df)

hours

Number of hours to look ahead from the start point

Value

A list containing:

See Also

mod_grid, find_local_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Create start points for demonstration (using row index)
start_index <- seq(1, nrow(example_data_5_subject), by = 100)
start_points <- data.frame(start_index = start_index)

# Find minimum glucose in next 2 hours
min_after <- find_min_after_hours(example_data_5_subject, start_points, hours = 2)
print(paste("Found", length(min_after$min_index), "minimum points"))

# Find minimum glucose in next 1 hour
min_after_1h <- find_min_after_hours(example_data_5_subject, start_points, hours = 1)

# Analysis on larger dataset
large_start_index <- seq(1, nrow(example_data_hall), by = 200)
large_start_points <- data.frame(start_index = large_start_index)
large_min_after <- find_min_after_hours(example_data_hall, large_start_points, hours = 2)
print(paste("Found", length(large_min_after$min_index), "minimum points in larger dataset"))

Find Minimum Glucose Before Specified Hours

Description

Identifies the minimum glucose value occurring within a specified time window before a given start point. This function is useful for analyzing glucose patterns preceding specific events or time points.

Usage

find_min_before_hours(df, start_point_df, hours)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

start_point_df

A dataframe with column start_index (R-based index into df)

hours

Number of hours to look back from the start point

Value

A list containing:

See Also

mod_grid, find_local_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Create start points for demonstration (using row index)
start_index <- seq(1, nrow(example_data_5_subject), by = 100)
start_points <- data.frame(start_index = start_index)

# Find minimum glucose in previous 2 hours
min_before <- find_min_before_hours(example_data_5_subject, start_points, hours = 2)
print(paste("Found", length(min_before$min_index), "minimum points"))

# Find minimum glucose in previous 1 hour
min_before_1h <- find_min_before_hours(example_data_5_subject, start_points, hours = 1)

# Analysis on larger dataset
large_start_index <- seq(1, nrow(example_data_hall), by = 200)
large_start_points <- data.frame(start_index = large_start_index)
large_min_before <- find_min_before_hours(example_data_hall, large_start_points, hours = 2)
print(paste("Found", length(large_min_before$min_index), "minimum points in larger dataset"))

Find New Maxima Around Grid Points

Description

Identifies new maxima in the vicinity of previously identified grid points, useful for refining maxima detection in GRID analysis. This function helps improve the accuracy of peak detection by searching around known event points.

Usage

find_new_maxima(df, mod_grid_max_point_df, local_maxima_df)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

mod_grid_max_point_df

A dataframe with column index (candidate maxima index)

local_maxima_df

A dataframe with column local_maxima (index of local peaks)

Value

A tibble with updated maxima information containing columns (id, time, gl, index) The index column contains R-based (1-indexed) row number(s) in df; thus, time == df$time[index] and gl == df$gl[index].

See Also

find_local_maxima, find_max_after_hours, transform_df

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), grid(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# First, get grid points and local maxima
grid_result <- grid(example_data_5_subject, gap = 15, threshold = 130)
maxima_result <- find_local_maxima(example_data_5_subject)

# Create modified grid points (simplified for example)
mod_grid_indices <- data.frame(index = grid_result$episode_start$index[1:10])

# Find new maxima around grid points
new_maxima <- find_new_maxima(example_data_5_subject, 
                              mod_grid_indices, 
                              maxima_result$local_maxima_vector)
print(paste("Found", nrow(new_maxima), "new maxima"))

# Analysis on larger dataset
large_grid <- grid(example_data_hall, gap = 15, threshold = 130)
large_maxima <- find_local_maxima(example_data_hall)
large_mod_grid <- data.frame(index = large_grid$episode_start$index[1:20])
large_new_maxima <- find_new_maxima(example_data_hall, 
                                    large_mod_grid, 
                                    large_maxima$local_maxima_vector)
print(paste("Found", nrow(large_new_maxima), "new maxima in larger dataset"))

GRID Algorithm for Glycemic Event Detection

Description

Implements the GRID (Glucose Rate Increase Detector) algorithm for detecting rapid glucose rate increases in continuous glucose monitoring (CGM) data. This algorithm identifies rapid glucose changes using specific rate-based criteria, and is commonly applied for meal detection. Meals are detected when the CGM value is \geq 7.2 mmol/L (\geq 130 mg/dL) and the rate-of-change is \geq 5.3 mmol/L/h [\geq 95 mg/dL/h] for the last two consecutive readings, or \geq 5.0 mmol/L/h [\geq 90 mg/dL/h] for two of the last three readings.

Usage

grid(df, gap = 15, threshold = 130)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

gap

Gap threshold in minutes for event detection (default: 15). This parameter defines the minimum time interval between consecutive GRID events. For example, if gap is set to 60, only one GRID event can be detected within any one-hour window; subsequent events within the gap interval are not counted as new events.

threshold

GRID slope threshold in mg/dL/hour for event classification (default: 130)

Value

A list containing:

Algorithm

- Flags points where gl >= 130 mg/dL and rate-of-change meets the GRID criteria (see references). - Enforces a minimum gap in minutes between detected events to avoid duplicates.

Units and sampling

- gl is mg/dL; time is POSIXct; gap is minutes. - The effective sampling interval is derived from time deltas.

References

Harvey, R. A., et al. (2014). Design of the glucose rate increase detector: a meal detection module for the health monitoring system. Journal of Diabetes Science and Technology, 8(2), 307-320.

Adolfsson, Peter, et al. "Increased time in range and fewer missed bolus injections after introduction of a smart connected insulin pen." Diabetes technology & therapeutics 22.10 (2020): 709-718.

See Also

mod_grid, maxima_grid, find_local_maxima, detect_between_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), maxima_grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Basic GRID analysis on smaller dataset
grid_result <- grid(example_data_5_subject, gap = 15, threshold = 130)
print(grid_result$episode_counts)
print(grid_result$episode_start)
print(grid_result$grid_vector)

# More sensitive GRID analysis
sensitive_result <- grid(example_data_5_subject, gap = 10, threshold = 120)

# GRID analysis on larger dataset
large_grid <- grid(example_data_hall, gap = 15, threshold = 130)
print(paste("Detected", sum(large_grid$episode_counts$episode_counts), "episodes"))
print(large_grid$episode_start)
print(large_grid$grid_vector)


Combined Maxima Detection and GRID Analysis

Description

Fast method for postprandial glucose peak detection combining GRID algorithm with local maxima analysis. Detects meal-induced glucose peaks by identifying GRID events (rapid glucose increases) and mapping them to corresponding local maxima within a search window. Local maxima are defined as points where glucose values increase or remain constant for two consecutive points before the peak, and decrease or remain constant for two consecutive points after the peak.

The 7-step algorithm: (1) finds GRID points indicating meal starts (2) identifies modified GRID points after minimum duration (3) locates maximum glucose within the subsequent time window (4) detects all local maxima using the two-consecutive-point criteria (5) refines peaks from local maxima candidates (6) maps GRID points to peaks within 4-hour constraint (7) redistributes overlapping peaks.

Usage

maxima_grid(df, threshold = 130, gap = 60, hours = 2)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

threshold

GRID slope threshold in mg/dL/hour for event classification (default: 130)

gap

Gap threshold in minutes for event detection (default: 60). This parameter defines the minimum time interval between consecutive GRID events.

hours

Time window in hours for maxima analysis (default: 2)

Value

A list containing:

Algorithm (7 steps)

1) GRID -> 2) modified GRID -> 3) window maxima -> 4) local maxima -> 5) refine peaks -> 6) map GRID to peaks (\leq 4h) -> 7) redistribute overlapping peaks.

References

Park, Sang Ho, et al. "Identification of clinically meaningful automatically detected postprandial glucose excursions in individuals with type 1 diabetes using personal continuous glucose monitoring." Diabetes Research and Clinical Practice (2025): 112951.

Park, Soojin, et al. "High-Amplitude and Prolonged Glucose Excursions as a Key Determinant of Discordance Between Glucose Management Indicator and Glycated Hemoglobin in Type 1 Diabetes." Diabetes Care (2026): dc252820. https://doi.org/10.2337/dc25-2820

See Also

grid, mod_grid, find_local_maxima, find_new_maxima, transform_df

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), mod_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Combined analysis on smaller dataset
maxima_result <- maxima_grid(example_data_5_subject, threshold = 130, gap = 60, hours = 2)
print(maxima_result$episode_counts)
print(maxima_result$results)

# More sensitive analysis
sensitive_maxima <- maxima_grid(example_data_5_subject, threshold = 120, gap = 30, hours = 1)
print(sensitive_maxima$episode_counts)
print(sensitive_maxima$results)

# Analysis on larger dataset
large_maxima <- maxima_grid(example_data_hall, threshold = 130, gap = 60, hours = 2)
print(large_maxima$episode_counts)
print(large_maxima$results)

Modified GRID Analysis

Description

Constructs a modified GRID series by reapplying the GRID logic with a designated gap (e.g., 60 minutes) and analysis window in hours (e.g., 2 hours). It reassigns GRID events under these constraints to produce a modified grid suitable for downstream maxima mapping and episode analysis.

Usage

mod_grid(df, grid_point_df, hours = 2, gap = 15)

Arguments

df

A dataframe containing continuous glucose monitoring (CGM) data. Must include columns:

  • id: Subject identifier (string or factor)

  • time: Time of measurement (POSIXct)

  • gl: Glucose value (integer or numeric, mg/dL)

grid_point_df

A dataframe with column start_index (start points for re-applied GRID)

hours

Time window in hours for analysis (default: 2)

gap

Gap threshold in minutes for event detection (default: 15). This parameter defines the minimum time interval between consecutive GRID events.

Value

A list containing:

Units and sampling

- gap is minutes; hours is hours; time is POSIXct.

References

Park, Sang Ho, et al. "Identification of clinically meaningful automatically detected postprandial glucose excursions in individuals with type 1 diabetes using personal continuous glucose monitoring." Diabetes Research and Clinical Practice (2025): 112951.

Park, Soojin, et al. "High-Amplitude and Prolonged Glucose Excursions as a Key Determinant of Discordance Between Glucose Management Indicator and Glycated Hemoglobin in Type 1 Diabetes." Diabetes Care (2026): dc252820. https://doi.org/10.2337/dc25-2820

See Also

grid, find_max_after_hours, find_new_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), start_finder(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# First, get grid points
grid_result <- grid(example_data_5_subject, gap = 60, threshold = 130)

# Perform modified GRID analysis
mod_result <- mod_grid(example_data_5_subject, grid_result$grid_vector, hours = 2, gap = 60)
print(paste("Modified grid points:", nrow(mod_result$mod_grid_vector)))

# Modified analysis with different parameters
mod_result_1h <- mod_grid(example_data_5_subject, grid_result$grid_vector, hours = 1, gap = 40)

# Analysis on larger dataset
large_grid <- grid(example_data_hall, gap = 60, threshold = 130)
large_mod_result <- mod_grid(example_data_hall, large_grid$grid_vector, hours = 2, gap = 60)
print(paste("Modified grid points in larger dataset:", nrow(large_mod_result$mod_grid_vector)))

Fast Ordering Function

Description

Orders a dataframe by id and time columns efficiently using base R's order. Optimized for large CGM datasets, it returns the input with rows sorted by subject then timestamp while preserving all columns.

Orders a dataframe by id and time columns

Usage

orderfast(df)

Arguments

df

A dataframe with 'id' and 'time' columns

Value

A dataframe ordered by id and time

A dataframe ordered by id and time

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Shuffle without replacement, then order and compare to baseline
set.seed(123)
shuffled <- example_data_5_subject[sample(seq_len(nrow(example_data_5_subject)),
                                          replace = FALSE), ]
baseline <- orderfast(example_data_5_subject)
ordered_shuffled <- orderfast(shuffled)

# Compare results
print(paste("Identical after ordering:", identical(baseline, ordered_shuffled)))
head(baseline[, c("id", "time", "gl")])
head(ordered_shuffled[, c("id", "time", "gl")])

# Order larger dataset
ordered_large <- orderfast(example_data_hall)
print(paste("Ordered", nrow(ordered_large), "rows in larger dataset"))
df <- data.frame(id = c("b", "a", "a"), time = as.POSIXct(
  c("2024-01-01 01:00:00", "2024-01-01 00:00:00", "2024-01-01 01:00:00"), tz = "UTC"
))
orderfast(df)

Find Start Points for Event Analysis

Description

Finds R-based (1-indexed) positions where the value is 1 in an integer vector of 0s and 1s, specifically identifying episode start points. This function looks for positions where a 1 follows a 0 or is at the beginning of the vector, which is useful for identifying the start of glycemic events or episodes.

Usage

start_finder(df)

Arguments

df

A dataframe with the first column containing an integer vector of 0s and 1s

Value

A tibble containing start_index with R-based (1-indexed) positions where episodes start Note: These index refer to positions in the provided input vector/dataframe, not necessarily rows of the original CGM df unless that vector was derived directly from df in row order.

Notes

- Returns R-based start_index positions relative to the provided input vector/dataframe. - If used on vectors derived from a CGM df, index map directly to df rows.

See Also

grid, mod_grid

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), transform_df()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Create a binary vector indicating episode starts
binary_vector <- c(0, 0, 1, 1, 0, 1, 0, 0, 1, 1)
df <- data.frame(episode_starts = binary_vector)

# Find R-based index where episodes start
start_points <- start_finder(df)
print(paste("Start index:", paste(start_points$start_index, collapse = ", ")))

# Use with actual GRID results
grid_result <- grid(example_data_5_subject, gap = 15, threshold = 130)
grid_starts <- start_finder(grid_result$grid_vector)
print(paste("GRID episode starts:", length(grid_starts$start_index)))

# Analysis on larger dataset
large_grid <- grid(example_data_hall, gap = 15, threshold = 130)
large_starts <- start_finder(large_grid$grid_vector)
print(paste("GRID episode starts in larger dataset:", length(large_starts$start_index)))

Transform Dataframe for Analysis

Description

Performs data transformations required for GRID analysis, including mapping GRID episode starts to maxima within a 4-hour window and merging grid and maxima information. This function prepares data for downstream analysis by combining these results.

Usage

transform_df(grid_df, maxima_df)

Arguments

grid_df

A dataframe containing grid analysis results

maxima_df

A dataframe containing maxima detection results

Value

A tibble with transformed data containing columns (id, grid_time, grid_gl, maxima_time, maxima_gl)

See Also

grid, find_new_maxima, detect_between_maxima

Other GRID pipeline: detect_between_maxima(), find_local_maxima(), find_max_after_hours(), find_max_before_hours(), find_min_after_hours(), find_min_before_hours(), find_new_maxima(), grid(), maxima_grid(), mod_grid(), start_finder()

Examples

# Load sample data
library(iglu)
data(example_data_5_subject)
data(example_data_hall)

# Complete pipeline example with smaller dataset
threshold <- 130
gap <- 60
hours <- 2
# 1) Find GRID points
grid_result <- grid(example_data_5_subject, gap = gap, threshold = threshold)
# 2) Find modified GRID points before 2 hours minimum
mod_grid <- mod_grid(example_data_5_subject,
                     start_finder(grid_result$grid_vector),
                     hours = hours,
                     gap = gap)

# 3) Find maximum point 2 hours after mod_grid point
mod_grid_maxima <- find_max_after_hours(example_data_5_subject,
                                        start_finder(mod_grid$mod_grid_vector),
                                        hours = hours)

# 4) Identify local maxima around episodes/windows
local_maxima <- find_local_maxima(example_data_5_subject)

# 5) Among local maxima, find maximum point after two hours
final_maxima <- find_new_maxima(example_data_5_subject,
                                mod_grid_maxima$max_index,
                                local_maxima$local_maxima_vector)

# 6) Map GRID points to maximum points (within 4 hours)
transform_maxima <- transform_df(grid_result$episode_start, final_maxima)

# 7) Redistribute overlapping maxima between GRID points
final_between_maxima <- detect_between_maxima(example_data_5_subject, transform_maxima)
# Complete pipeline example with larger dataset (example_data_hall)
# This demonstrates the same workflow on a more comprehensive dataset
hall_threshold <- 130
hall_gap <- 60
hall_hours <- 2

# 1) Find GRID points on larger dataset
hall_grid_result <- grid(example_data_hall, gap = hall_gap, threshold = hall_threshold)

# 2) Find modified GRID points
hall_mod_grid <- mod_grid(example_data_hall,
                         start_finder(hall_grid_result$grid_vector),
                         hours = hall_hours,
                         gap = hall_gap)

# 3) Find maximum points after mod_grid
hall_mod_grid_maxima <- find_max_after_hours(example_data_hall,
                                            start_finder(hall_mod_grid$mod_grid_vector),
                                            hours = hall_hours)

# 4) Identify local maxima
hall_local_maxima <- find_local_maxima(example_data_hall)

# 5) Find new maxima
hall_final_maxima <- find_new_maxima(example_data_hall,
                                    hall_mod_grid_maxima$max_index,
                                    hall_local_maxima$local_maxima_vector)

# 6) Transform data
hall_transform_maxima <- transform_df(hall_grid_result$episode_start, hall_final_maxima)

# 7) Detect between maxima
hall_final_between_maxima <- detect_between_maxima(example_data_hall, hall_transform_maxima)