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.
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:
grid_vector: A tibble with the results of the GRID analysis. Contains agridcolumn (0/1 values; 1 denotes a detected GRID event) and all relevant input columns.episode_counts: A tibble summarizing the number of GRID events per subject (id) asepisode_counts.episode_start: A tibble listing the start of each GRID episode, with columns:id: Subject ID.time: The timestamp (POSIXct) at which the GRID event was detected.gl: The glucose value (mg/dL; integer or numeric) at the GRID event.index: R-based (1-indexed) row number(s) in the original dataframe where the GRID event occurs. The occurrence time equalsdf$time[index]and glucose equalsdf$gl[index].
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.
- This function operates on the rows supplied in df. It does not use
interpolate_cgm or the full-day event preprocessing grid.
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)
#> # A tibble: 5 × 2
#> id episode_counts
#> <chr> <int>
#> 1 Subject 1 10
#> 2 Subject 2 22
#> 3 Subject 3 7
#> 4 Subject 4 18
#> 5 Subject 5 42
print(grid_result$episode_start)
#> # A tibble: 99 × 4
#> id time gl index
#> <chr> <dttm> <dbl> <int>
#> 1 Subject 1 2015-06-11 15:30:07 143 966
#> 2 Subject 1 2015-06-11 17:10:07 157 985
#> 3 Subject 1 2015-06-11 22:00:06 135 1038
#> 4 Subject 1 2015-06-11 22:25:06 162 1043
#> 5 Subject 1 2015-06-12 07:40:04 160 1154
#> 6 Subject 1 2015-06-13 16:34:59 132 1415
#> 7 Subject 1 2015-06-14 17:39:55 176 1676
#> 8 Subject 1 2015-06-16 19:14:47 166 2222
#> 9 Subject 1 2015-06-18 14:29:40 187 2720
#> 10 Subject 1 2015-06-18 18:19:39 132 2765
#> # ℹ 89 more rows
print(grid_result$grid_vector)
#> # A tibble: 13,866 × 1
#> grid
#> <int>
#> 1 0
#> 2 0
#> 3 0
#> 4 0
#> 5 0
#> 6 0
#> 7 0
#> 8 0
#> 9 0
#> 10 0
#> # ℹ 13,856 more rows
# 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"))
#> [1] "Detected 79 episodes"
print(large_grid$episode_start)
#> # A tibble: 79 × 4
#> id time gl index
#> <chr> <dttm> <dbl> <int>
#> 1 1636-69-001 2014-02-04 07:47:05 138 336
#> 2 1636-69-001 2014-02-04 17:42:03 138 455
#> 3 1636-69-001 2014-02-05 08:41:59 137 635
#> 4 1636-69-001 2015-03-29 14:33:30 137 786
#> 5 1636-69-001 2015-03-30 10:53:25 132 979
#> 6 1636-69-001 2015-03-31 09:18:19 143 1202
#> 7 1636-69-001 2015-03-31 13:58:18 136 1258
#> 8 1636-69-001 2015-04-01 16:58:12 132 1581
#> 9 1636-69-026 2015-11-24 14:37:18 142 2011
#> 10 1636-69-026 2015-11-24 23:32:16 139 2118
#> # ℹ 69 more rows
print(large_grid$grid_vector)
#> # A tibble: 34,890 × 1
#> grid
#> <int>
#> 1 0
#> 2 0
#> 3 0
#> 4 0
#> 5 0
#> 6 0
#> 7 0
#> 8 0
#> 9 0
#> 10 0
#> # ℹ 34,880 more rows