1 Introduction

Continuous Glucose Monitoring (CGM) systems provide real-time measurements of glucose levels and are increasingly used in diabetes research and clinical practice to assess glycemic control and glucose variability.

Several CGM-derived metrics are commonly used to evaluate: - sensor accuracy, - short-term glycemic variability, - glucose excursions, - and inter-day glycemic stability.

This document presents the methodological framework for computing the following CGM metrics in R:

  1. Mean Absolute Relative Difference (MARD)
  2. Continuous Overall Net Glycemic Action (CONGA)
  3. Mean Amplitude of Glycemic Excursions (MAGE)
  4. Mean of Daily Differences (MODD)

For each metric, the following are presented: - definition and purpose, - feasibility of computation, - required variables, - data structure, - formula, - R packages and functions, - and example R code.

2 Required Data Structure for CGM Analysis

2.1 Minimum Variables Required

Variable Description Required For
id Participant identifier All metrics
datetime Date and time of CGM measurement All metrics
glucose CGM glucose measurement All metrics
reference_glucose Reference blood glucose value MARD
day Day extracted from datetime MODD

2.3 Important Considerations

  • CGM observations should preferably be recorded at regular intervals.
  • Typical intervals include:
    • every 5 minutes,
    • every 10 minutes,
    • or every 15 minutes.
  • Datetime values must be correctly formatted.
  • Glucose units should remain consistent throughout the dataset.
  • Missing timestamps and missing glucose values should be checked before analysis.

3 Mean Absolute Relative Difference (MARD)

3.1 Definition and Purpose

Mean Absolute Relative Difference (MARD) is a widely used metric for evaluating the analytical accuracy of Continuous Glucose Monitoring (CGM) systems.

It measures the average relative difference between CGM glucose measurements and corresponding reference glucose values obtained from laboratory or capillary measurements.

Lower MARD values indicate better agreement between CGM readings and reference glucose values, reflecting higher sensor accuracy.

3.2 Is It Possible to Compute?

Yes.

MARD can be computed when paired CGM and reference glucose measurements are available for the same observation time points.

3.3 Required Variables

Variable Description
glucose CGM glucose measurement
reference_glucose Reference glucose measurement

3.5 Formula

\[ MARD = \frac{1}{n}\sum_{i=1}^{n}\left|\frac{CGM_i - REF_i}{REF_i}\right| \times 100 \]

Where:

  • \(CGM_i\) = CGM glucose value at observation \(i\)
  • \(REF_i\) = Reference glucose value at observation \(i\)
  • \(n\) = Number of paired observations

3.6 R Packages and Functions

Package Function
base mean()
base abs()
dplyr group_by(), summarize()

3.7 Traceability

  • base::mean()
  • base::abs()
  • dplyr::group_by()
  • dplyr::summarize()

3.8 Implementation Examples

3.8.1 Method A: Direct Vector Calculation (Base R)

This approach calculates the MARD metric directly from two synchronized clinical data vectors.

# Sample paired tracking data

cgm <- c(100, 150, 200, 120, 80)
ref <- c(105, 145, 190, 130, 85)

# Calculate Absolute Relative Differences (ARD)

ard <- abs((cgm - ref) / ref)

# Calculate Mean ARD and convert to percentage

mard <- mean(ard) * 100

print(paste0("MARD: ", round(mard, 2), "%"))
## [1] "MARD: 5.41%"

3.8.2 Method B: Modular Custom R Function

This production-grade wrapper adds logical handling to prevent mathematical errors such as division by zero.

calculate_mard <- function(cgm_data, ref_data) {

  # Prevent division by zero

  if (any(ref_data == 0)) {
    stop("Reference data cannot contain zero.")
  }

  # Compute MARD

  mard_val <- mean(abs((cgm_data - ref_data) / ref_data)) * 100

  return(mard_val)
}

# Apply function to paired vectors

result <- calculate_mard(cgm, ref)

print(paste0("MARD: ", round(result, 2), "%"))
## [1] "MARD: 5.41%"

3.8.3 Method C: Multi-Sensor Analysis (Long Format Data)

This structure calculates individual MARD values for multiple sensors stored sequentially within a unified long-format dataset.

library(dplyr)
## Warning: package 'dplyr' was built under R version 4.5.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Example long-format dataset

df_long <- data.frame(
  sensor_id = rep(c("Sensor_A", "Sensor_B", "Sensor_C"), each = 4),
  reading = c(105, 110, 95, 120,
              102, 107, 98, 122,
              108, 115, 92, 128),
  reference = rep(c(100, 108, 100, 125), 3)
)

# Compute MARD for each sensor

mard_long_results <- df_long %>%
  group_by(sensor_id) %>%
  summarize(
    MARD = mean(abs((reading - reference) / reference)) * 100,
    .groups = "drop"
  )

print(mard_long_results)
## # A tibble: 3 × 2
##   sensor_id  MARD
##   <chr>     <dbl>
## 1 Sensor_A   3.96
## 2 Sensor_B   1.83
## 3 Sensor_C   6.22

3.8.4 Method D: Multi-Sensor Analysis (Wide Format Data)

This approach handles datasets where multiple sensors are stored in separate columns, allowing simultaneous MARD computation across sensors.

# Example wide-format dataset

df_wide <- data.frame(
  ref = c(100, 110, 120, 130),
  sensor1 = c(105, 108, 125, 128),
  sensor2 = c(95, 115, 118, 135),
  sensor3 = c(102, 112, 119, 131)
)

# Define sensor columns

sensor_cols <- c("sensor1", "sensor2", "sensor3")

# Compute MARD for each sensor column

mards_wide_results <- sapply(
  df_wide[sensor_cols],
  function(sensor_val) {
    mean(abs((sensor_val - df_wide$ref) / df_wide$ref)) * 100
  }
)

print(mards_wide_results)
##  sensor1  sensor2  sensor3 
## 3.130828 3.764569 1.355186

3.8.4.1 Interpretation

  • Lower MARD values indicate higher CGM sensor accuracy.
  • MARD values below 10% are generally considered excellent.
  • Higher MARD values indicate larger discrepancies between CGM and reference measurements.

3.9 Important Considerations

  • CGM and reference values must be synchronized temporally.
  • Reference glucose values should never contain zero values.
  • Units must remain consistent throughout the dataset.
  • Missing paired observations should be checked before computation.
  • MARD only evaluates analytical agreement and does not assess glycemic variability.

4 Continuous Overall Net Glycemic Action (CONGA)

4.1 Definition and Purpose

Continuous Overall Net Glycemic Action (CONGA) is a CGM-derived metric used to quantify short-term glycemic variability.

It evaluates glucose fluctuations over a predefined time interval by calculating the standard deviation of glucose differences between measurements separated by a fixed lag period.

CONGA is particularly useful for assessing intra-day glucose instability and short-term glycemic oscillations.

4.2 Is It Possible to Compute?

Yes.

CONGA can be computed using longitudinal CGM data containing sequential glucose measurements associated with timestamps.

4.3 Required Variables

Variable Description
id Participant identifier
datetime Timestamp of CGM measurement
glucose CGM glucose measurement

4.5 Principle

CONGA calculates the standard deviation of the differences between glucose values separated by a predefined time lag \(n\).

For example: - CONGA1 evaluates variability over 1 hour, - CONGA2 evaluates variability over 2 hours.

Higher CONGA values indicate greater short-term glycemic variability.

4.6 Formula

\[ CONGA_n = SD(G_t - G_{t-n}) \]

Where:

  • \(G_t\) = glucose value at time \(t\)
  • \(G_{t-n}\) = glucose value measured \(n\) hours earlier
  • \(SD\) = standard deviation of all glucose differences

4.7 R Packages and Functions

Package Function
iglu conga()
dplyr data manipulation

4.8 Traceability

  • iglu::conga()
  • dplyr::mutate()
  • dplyr::arrange()

4.9 Implementation Example

library(iglu)
## Warning: package 'iglu' was built under R version 4.5.3
library(dplyr)

# 1. Generate compliant CGM dataset

cgm_data <- data.frame(
  id = rep("Subject_01", 9),
  time = as.POSIXct(c(
    "2026-05-14 08:00:00",
    "2026-05-14 08:30:00",
    "2026-05-14 09:00:00",
    "2026-05-14 09:30:00",
    "2026-05-14 10:00:00",
    "2026-05-14 10:30:00",
    "2026-05-14 11:00:00",
    "2026-05-14 11:30:00",
    "2026-05-14 12:00:00"
  ), tz = "GMT"),
  gl = c(100, 110, 130, 140, 150, 160, 145, 135, 120)
)

# 2. Inspect input data structure

print("Structured Input Data Frame:")
## [1] "Structured Input Data Frame:"
print(cgm_data)
##           id                time  gl
## 1 Subject_01 2026-05-14 08:00:00 100
## 2 Subject_01 2026-05-14 08:30:00 110
## 3 Subject_01 2026-05-14 09:00:00 130
## 4 Subject_01 2026-05-14 09:30:00 140
## 5 Subject_01 2026-05-14 10:00:00 150
## 6 Subject_01 2026-05-14 10:30:00 160
## 7 Subject_01 2026-05-14 11:00:00 145
## 8 Subject_01 2026-05-14 11:30:00 135
## 9 Subject_01 2026-05-14 12:00:00 120
# 3. Compute CONGA using a 2-hour lag window

conga_result <- iglu::conga(
  cgm_data,
  n = 2,
  tz = "GMT"
)

# 4. Display output results

print("CONGA Output Tibble:")
## [1] "CONGA Output Tibble:"
print(conga_result)
## # A tibble: 1 × 2
##   id         CONGA
##   <chr>      <dbl>
## 1 Subject_01  34.9

4.10 Interpretation

  • Higher CONGA values indicate greater short-term glucose variability.
  • Lower CONGA values indicate more stable glucose profiles.
  • CONGA is sensitive to rapid glucose fluctuations occurring over time.

4.11 Important Considerations

  • CGM measurements should preferably occur at regular intervals.
  • Proper timestamp formatting is essential.
  • Missing values and irregular spacing may affect CONGA estimation.
  • Time zone consistency should be maintained throughout the dataset.
  • Longitudinal CGM data are required for valid computation.

5 Mean Amplitude of Glycemic Excursions (MAGE)

5.1 Definition and Purpose

Mean Amplitude of Glycemic Excursions (MAGE) is a CGM-derived metric used to quantify major glucose fluctuations occurring over time.

It measures the average amplitude of significant upward and downward glucose excursions that exceed a predefined variability threshold, typically one standard deviation of the mean glucose profile.

MAGE is commonly used to assess glycemic instability and detect clinically meaningful glucose swings.

5.2 Is It Possible to Compute?

Yes.

MAGE can be computed using longitudinal CGM datasets containing sequential glucose measurements and timestamps.

5.3 Required Variables

Variable Description
id Participant identifier
datetime Timestamp of CGM measurement
glucose CGM glucose measurement

5.5 Principle

MAGE identifies significant glucose excursions by detecting peaks and troughs in the glucose profile that exceed one standard deviation of the mean glucose level.

Only major excursions are retained in the computation process.

Higher MAGE values indicate greater glycemic instability and larger glucose fluctuations.

5.6 R Packages and Functions

Package Function
iglu mage()
dplyr data manipulation

5.7 Traceability

  • iglu::mage()
  • dplyr::mutate()
  • dplyr::arrange()

5.8 Implementation Example & Algorithmic Plot

The iglu package calculates MAGE using the same structured layout format as the CONGA function (id, time, gl).

library(iglu)
library(dplyr)

# 1. Structure a single-subject dataset

cgm_data_mage <- data.frame(
  id = rep("Subject_01", 12),
  time = as.POSIXct(c(
    "2026-05-14 06:00:00",
    "2026-05-14 06:30:00",
    "2026-05-14 07:00:00",
    "2026-05-14 07:30:00",
    "2026-05-14 08:00:00",
    "2026-05-14 08:30:00",
    "2026-05-14 09:00:00",
    "2026-05-14 09:30:00",
    "2026-05-14 10:00:00",
    "2026-05-14 10:30:00",
    "2026-05-14 11:00:00",
    "2026-05-14 11:30:00"
  ), tz = "GMT"),
  gl = c(95, 90, 160, 185, 175, 110, 85, 90, 145, 160, 130, 100)
)

# 2. Inspect the glucose excursion profile

print("Input Data Frame for MAGE:")
## [1] "Input Data Frame for MAGE:"
print(cgm_data_mage)
##            id                time  gl
## 1  Subject_01 2026-05-14 06:00:00  95
## 2  Subject_01 2026-05-14 06:30:00  90
## 3  Subject_01 2026-05-14 07:00:00 160
## 4  Subject_01 2026-05-14 07:30:00 185
## 5  Subject_01 2026-05-14 08:00:00 175
## 6  Subject_01 2026-05-14 08:30:00 110
## 7  Subject_01 2026-05-14 09:00:00  85
## 8  Subject_01 2026-05-14 09:30:00  90
## 9  Subject_01 2026-05-14 10:00:00 145
## 10 Subject_01 2026-05-14 10:30:00 160
## 11 Subject_01 2026-05-14 11:00:00 130
## 12 Subject_01 2026-05-14 11:30:00 100
# 3. Compute MAGE

mage_result <- iglu::mage(cgm_data_mage)

# 4. Display output results

print("MAGE Output Tibble:")
## [1] "MAGE Output Tibble:"
print(mage_result)
## # A tibble: 1 × 2
## # Rowwise: 
##   id          MAGE
##   <chr>      <dbl>
## 1 Subject_01  82.5

5.9 Visualizing Excursion Peaks and Troughs

By setting plot = TRUE, the iglu package produces a graphical representation of the glucose profile used during MAGE computation.

The visualization: - plots the CGM time-series profile, - identifies major peaks and troughs, - and highlights the excursion threshold band corresponding to one standard deviation.

# Render graphical visualization of glucose excursions

iglu::mage(
  cgm_data_mage,
  plot = TRUE
)

5.10 Interpretation

  • Higher MAGE values indicate greater glycemic variability and larger glucose excursions.
  • Lower MAGE values indicate more stable glucose profiles.
  • Elevated MAGE is often associated with poor glycemic control and increased glucose instability.

5.11 Important Considerations

  • High-frequency CGM measurements are recommended.
  • Proper chronological ordering of timestamps is essential.
  • Missing values may interfere with peak and trough detection.
  • MAGE specifically focuses on major glucose excursions and does not capture all forms of variability.
  • Consistent glucose units should be maintained throughout the dataset.

6 Mean of Daily Differences (MODD)

6.1 Definition and Purpose

Mean of Daily Differences (MODD) is a CGM-derived metric used to evaluate inter-day glycemic stability.

It measures the average absolute difference between glucose values recorded at similar time points on consecutive days.

MODD is particularly useful for assessing day-to-day consistency in glucose profiles and identifying temporal glycemic drift across repeated daily cycles.

6.2 Is It Possible to Compute?

Yes.

MODD can be computed when CGM measurements span at least two consecutive days with sufficiently aligned timestamps.

6.3 Required Variables

Variable Description
id Participant identifier
datetime Timestamp of CGM measurement
glucose CGM glucose measurement

6.5 Principle

MODD compares glucose measurements recorded at equivalent time points across consecutive days and computes the mean absolute difference between those paired observations.

Higher MODD values indicate greater day-to-day glucose variability and lower inter-day stability.

6.6 Formula

\[ MODD = \frac{1}{n}\sum_{i=1}^{n}|G_{day1,i} - G_{day2,i}| \]

Where:

  • \(G_{day1,i}\) = glucose value at time point \(i\) on day 1
  • \(G_{day2,i}\) = glucose value at the corresponding time point on day 2
  • \(n\) = number of matched paired observations

6.7 R Packages and Functions

Package Function
iglu modd()
iglu plot_glu()
dplyr data manipulation

6.8 Traceability

  • iglu::modd()
  • iglu::plot_glu()
  • dplyr::mutate()
  • dplyr::arrange()

6.9 Implementation Example & Multi-Day Visualization

To compute MODD, the input dataset must span multiple days with glucose readings aligned across similar time blocks.

library(iglu)
library(dplyr)

# 1. Structure a dataset spanning two consecutive days

cgm_data_modd <- data.frame(
  id = rep("Subject_01", 10),
  time = as.POSIXct(c(

    # Day 1 readings

    "2026-05-14 08:00:00",
    "2026-05-14 08:15:00",
    "2026-05-14 08:30:00",
    "2026-05-14 08:45:00",
    "2026-05-14 09:00:00",

    # Day 2 readings (same time points)

    "2026-05-15 08:00:00",
    "2026-05-15 08:15:00",
    "2026-05-15 08:30:00",
    "2026-05-15 08:45:00",
    "2026-05-15 09:00:00"

  ), tz = "GMT"),

  gl = c(
    100, 110, 130, 150, 120,   # Day 1 values
    115, 120, 125, 170, 110    # Day 2 values
  )
)

# 2. Compute MODD

modd_result <- iglu::modd(cgm_data_modd)

# 3. Display output results

print("MODD Output Tibble:")
## [1] "MODD Output Tibble:"
print(modd_result)
## # A tibble: 1 × 2
##   id          MODD
##   <chr>      <dbl>
## 1 Subject_01    12

6.10 Visualizing Inter-Day Trajectories

To visually assess the inter-day differences contributing to the MODD score, glucose trajectories from multiple days can be overlaid using plot_glu().

This visualization: - displays longitudinal glucose profiles, - compares repeated daily trajectories, - and highlights inter-day glucose drift across matching time intervals.

# Generate inter-day glucose trajectory visualization

iglu::plot_glu(
  cgm_data_modd,
  plottype = "tsplot",
  inter_gap = 60,
  LLTR = 70,
  ULTR = 180,
  static_or_gui = "ggplot"
)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the iglu package.
##   Please report the issue at <https://github.com/irinagain/iglu/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

6.11 Interpretation

  • Higher MODD values indicate greater inter-day glucose variability.
  • Lower MODD values indicate more stable and reproducible daily glucose patterns.
  • MODD is useful for evaluating consistency of glycemic control across days.

6.12 Important Considerations

  • At least two consecutive days of CGM data are required.
  • Matching time points between days improve computation accuracy.
  • Regular CGM intervals are strongly recommended.
  • Missing timestamps or irregular spacing may affect MODD estimation.
  • Time zone consistency should be maintained throughout the dataset.
  • MODD evaluates day-to-day stability and complements intra-day variability metrics such as CONGA and MAGE.

7 Conclusion

The four CGM metrics presented in this document can be computed in R when appropriately structured CGM datasets are available.

The essential requirements include: - properly formatted timestamps, - longitudinal CGM measurements, - consistent glucose units, - and paired reference glucose values for MARD.

The iglu package provides a practical framework for computing CGM variability metrics in R, while maintaining reproducibility and methodological traceability.

sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_India.utf8  LC_CTYPE=English_India.utf8   
## [3] LC_MONETARY=English_India.utf8 LC_NUMERIC=C                  
## [5] LC_TIME=English_India.utf8    
## 
## time zone: Asia/Calcutta
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] iglu_4.2.2  dplyr_1.2.1
## 
## loaded via a namespace (and not attached):
##  [1] sass_0.4.10        utf8_1.2.6         generics_0.1.4     tidyr_1.3.1       
##  [5] rstatix_0.7.3      lattice_0.22-7     digest_0.6.39      magrittr_2.0.5    
##  [9] evaluate_1.0.5     grid_4.5.1         timechange_0.4.0   RColorBrewer_1.1-3
## [13] fastmap_1.2.0      jsonlite_2.0.0     backports_1.5.1    Formula_1.2-5     
## [17] purrr_1.2.2        scales_1.4.0       jquerylib_0.1.4    abind_1.4-8       
## [21] cli_3.6.6          rlang_1.2.0        cowplot_1.2.0      withr_3.0.2       
## [25] cachem_1.1.0       yaml_2.3.10        otel_0.2.0         tools_4.5.1       
## [29] ggsignif_0.6.4     ggplot2_4.0.2      DT_0.34.0          ggpubr_0.6.3      
## [33] broom_1.0.12       vctrs_0.7.1        R6_2.6.1           zoo_1.8-14        
## [37] lifecycle_1.0.5    lubridate_1.9.5    car_3.1-5          htmlwidgets_1.6.4 
## [41] pkgconfig_2.0.3    pillar_1.11.1      bslib_0.10.0       gtable_0.3.6      
## [45] glue_1.8.1         xfun_0.52          tibble_3.3.0       tidyselect_1.2.1  
## [49] rstudioapi_0.18.0  knitr_1.51         farver_2.1.2       htmltools_0.5.9   
## [53] patchwork_1.3.2    labeling_0.4.3     carData_3.0-6      rmarkdown_2.31    
## [57] compiler_4.5.1     S7_0.2.1