{width=“75%”, height=“75%”}
Name | iglu R output | iglu R function | Reference | Time Dependent |
---|---|---|---|---|
Active Percent | Yes | |||
ADRR (Average Daily Risk Range) | ADRR | Yes | ||
AUC (Area Under the Curve) | AUC | Yes | ||
COGI (COntinuous Glucose monitoring Index) | COGI | No | ||
CONGA (Continuous Overall Net Glycemic Action) | Conga | Yes | ||
Coefficient of variation (CV) | CV | No | ||
CVmean (mean of daily CV) | CV_Measures_Mean | Yes | ||
CVsd (SD of daily CV) | CV_Measures_SD | Yes | ||
eA1c (Estimated A1c) | eA1C | No | ||
Episode Calculation | Yes | |||
Episode Calculation Profile | Yes | |||
Glucose Management Indicator (GMI) | GMI | No | ||
GRADE (Glycemic Risk Assessment Diabetes Equation) | GRADE | No | ||
GRADEeu | GRADE_eugly | No | ||
GRADEhyper | GRADE_hyper | No | ||
GRADEhypo | GRADE_hypo | No | ||
GVP (Glucose Variability Percentage) | GVP | Yes | ||
HBGI (High Blood Glucose Index) | HBGI | No | ||
LBGI (Low Blood Glucose Index) | LBGI | No | ||
Hyperglycemia Index Parameters | hyper_index | No | ||
Hypoglycemia Index Parameters | hypo_index | No | ||
IGC (Index of Glycemic Control) | IGC | No | ||
IQR (Interquartile Range) | No | |||
J-Index | J_index | No | ||
M-value | M_value | No | ||
MAD (Mean Absolute Deviation) | MAD | No | ||
MAG (Mean Absolute Glucose) | MAG | Yes | ||
MAGE (Mean Amplitude of Glycemic Excursions) | MAGE | No | ||
Mean | Mean | No | ||
Median | Median | No | ||
MODD (Mean Of Daily Differences) | MODD | Yes | ||
Percent Above | No | |||
Percent Below | No | |||
Percent in range | No | |||
Quantiles | No | |||
Range | range | No | ||
Rate of change (ROC) | Yes | |||
Standard Deviation of ROC | SD.Roc | Yes | ||
Standard Deviation | SD | No | ||
SdB (between days, within timepoints) | SDb | Yes | ||
SdBDM (between days, within timepoints, corrected for changes in daily means) | SDbdm | Yes | ||
SdDM (horizontal sd) | SDdm | Yes | ||
SdHHMM (between time points) | SDhhmm | Yes | ||
SdW (vertical within days) | SDw | Yes | ||
SdWSH (within time series) | SDwsh | Yes |
---
title: Continuous Glucose Monitoring (CGM) Visualization
author: Agustin Calatroni <a href='https://github.com/agstn/CGM'> <i style='background-color:white' class='fa fa-github'> Wonderful-Wednesdays </i></a>
date: "`r format(Sys.Date(), format='%a %d %b %Y')`"
output:
flexdashboard::flex_dashboard:
self_contained: true
source_code: embed
---
```{=html}
<style>
element.style {
width: 900px;
height: 550px;
margin-top: 10px;
margin-bottom: 10px;
}
</style>
```
```{css, echo=FALSE}
.bgwhite {
background-color: white;
}
```
```{r knitr-defaults}
knitr::opts_chunk$set(warning = FALSE, message = FALSE, comment = NA)
knitr::opts_chunk$set(cache = FALSE)
options(width=170)
```
```{r load-packages}
pacman::p_load(tidyverse, rio, gt, gtsummary)
pacman::p_load(labelled)
pacman::p_load(patchwork)
pacman::p_load(iglu)
pacman::p_load(partykit, ggparty)
pacman::p_load(ggdist)
```
```{r import-reshape-label}
CGM_data_1 <- import('https://raw.githubusercontent.com/VIS-SIG/Wonderful-Wednesdays/master/data/2021/2021-08-11/simulated_data.csv')
CGM_data_2 <- CGM_data_1 %>%
rename(VISITNUM_N = VISITNUM) %>%
mutate(VISITNUM_C = factor(VISITNUM_N, labels = c('Baseline','26-weeks','52-weeks')),
VISITNUM_M = case_when(VISITNUM_N == 3 ~ '01',
VISITNUM_N == 17 ~ '06',
VISITNUM_N == 21 ~ '12'),
TREATMENT = factor(TREATMENT ) %>%
fct_relevel("SOC","Rx low","Rx medium","Rx high") %>%
fct_recode(`Rx-Low` = "Rx low",
`Rx-Med` = "Rx medium",
`Rx-High` = "Rx high")) %>%
rename(VALUE_ORIG = 'Original_CGMValue',
VALUE_SIM = 'Simulated_CGMValue',
TIME = 'CGMTIME',
DAY = 'CGMDAY') %>%
select(SUBJID, TREATMENT,
VISITNUM_N, VISITNUM_C, VISITNUM_M,
DAY, TIME,
VALUE_ORIG, VALUE_SIM) %>%
mutate(DAY_TIME = as.POSIXct(str_glue("2021-{VISITNUM_M}-0{DAY} {TIME}:00")),
.after = TIME)
var_label(CGM_data_2) <- list(
SUBJID = 'Subject Identification',
TREATMENT = 'Treatment Regimen',
VISITNUM_N = 'Study visit NUM',
VISITNUM_M = 'Study visit MONTH',
VISITNUM_C = 'Study Visit NAME',
DAY = 'Day the CGM was measured',
TIME = 'Time of the day that the CGM value was measured',
DAY_TIME = 'Day/Time created',
VALUE_ORIG = 'Original CGM value',
VALUE_SIM = 'Simulated CGM value')
export(CGM_data_2, "CGM_data.rds")
```
```{r iglue-function}
plot_ranges_2 <- function (data) {
gl = id = below_54 = below_70 = in_range_70_180 = above_180 = above_250 = percent = NULL
rm(list = c("gl", "id", "below_54", "below_70", "in_range_70_180", "above_180", "above_250", "percent"))
subject = unique(data$id)
ns = length(subject)
if (ns > 1) {
subject = subject[1]
warning(paste("The provided data have", ns, "subjects. The plot will only be created for subject", subject))
data = data %>% filter(id == subject)
}
ranges <- agp_metrics(data, shinyformat = FALSE) %>%
select(-c("id", "active_percent", "mean", "GMI", "CV")) %>%
summarise(range = c("very_low", "low", "target", "high", "very_high"),
percent = c(below_54, below_70 - below_54, in_range_70_180, above_180 - above_250, above_250))
ranges = ranges %>%
mutate(range = factor(range, levels = c("very_high", "high", "target", "low", "very_low")))
colors <- c("#F9B500", "#F9F000", "#48BA3C", "#F92D00", "#8E1B1B")
ggplot(data = ranges, mapping = aes(y = '', fill = range, x = percent)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = colors,
drop = FALSE,
labels = c("Very High\n>250",
"High\n181-250",
"Target\n70-180",
"Low\n54-69",
"Very Low\n<54")) +
scale_x_continuous(breaks = seq(0, 100, 25)) +
labs(x = NULL,
y = NULL,
fill = "Percent (%) Time spent in \nstandardized glycemic ranges") +
scale_y_discrete(breaks = NULL) +
guides(fill = guide_legend(reverse = TRUE,
label.position = 'bottom'))
}
```
```{r iglu-derive}
options(warn=-1)
CGM_iglu_data <- CGM_data_2 %>%
mutate(studyid = SUBJID) %>%
process_data(id = "SUBJID", timestamp = "DAY_TIME", glu = "VALUE_ORIG") %>%
select(-visitnum_n, -visitnum_m, -time.1, -value_sim, -day) %>%
rename(visit = visitnum_c) %>%
nest_by(studyid, treatment, visit)
CGM_iglu_data <- CGM_iglu_data %>%
ungroup() %>%
add_count(studyid, name = 'visit_n')
CGM_iglu_metrics <- CGM_iglu_data %>%
rowwise() %>%
mutate(all_metrics = list( data %>% data.frame() %>% all_metrics() )) %>%
mutate(panel = list(
(plot_ranges_2(data)) /
(guide_area()) /
(plot_agp(data) + ylim(39, 401)) /
(plot_daily(data) + ylim(39, 401) +
geom_ribbon(aes(reltime, ymin = 70, ymax = 180), fill = "#48BA3C", alpha = 0.3) +
geom_hline(yintercept = c(70, 180), color = "#48BA3C")) /
plot_layout(heights = c(1, 1, 3, 3),
guides = 'collect') &
theme_bw() &
theme(legend.position='bottom',
legend.key.height = unit(0.3, 'cm'),
legend.key.width = unit(1.5, 'cm'),
strip.background = element_blank(),
strip.text = element_blank(),
legend.title=element_text(size=9))
)
) %>%
unnest(c(all_metrics))
CGM_iglu_metrics %>%
select(studyid, treatment, visit, visit_n, data) %>%
export('CGM_iglu_data.rds')
CGM_iglu_metrics %>%
select(-panel, -data, -id) %>%
export('CGM_iglu_stats.rds')
```
# VISUALIZATION {data-navmenu="AGP"}
```{r iglu-trelliscope}
pacman::p_load(trelliscopejs)
CGM_iglu_metrics %>%
select(studyid, treatment, visit, visit_n,
ADRR, AUC, COGI, Conga, CV, CV_Measures_Mean, CV_Measures_SD, eA1C, GMI,
GRADE, GRADE_eugly, GRADE_hyper, GRADE_hypo, GVP, HBGI, hyper_index, hypo_index,
IGC, J_index, LBGI, M_value, MAD, MAG, MAGE, Mean, Median, MODD, range, SD, SD.Roc,
SDb, SDbdm, SDdm, SDhhmm, SDw, SDwsh,
panel) %>%
ungroup() %>%
set_labels(list(ADRR = 'average daily risk range',
AUC = 'Area Under Curve',
COGI = 'Continuous GlucoseMonitoring Index',
Conga = 'Continuous Overall Net Glycemic Action',
eA1C = 'Estimated A1c',
GMI = 'Glucose Management Indicator',
GVP = 'Glucose Variability Percentage',
HBGI = 'High Blood Glucose Index',
LBGI = 'Low Blood Glucose Index',
IGC = 'Index of Glycemic Control')) %>%
mutate(across(where(is.numeric), ~round(.x, 2))) %>%
trelliscope(name = 'CGM: Continuous Glucose Monitoring Visualization',
desc = 'Display Ambulatory Glucose Profile (AGP) statistics',
panel_col = 'panel',
path = './CGM-trelliscope',
ncol = 3,
nrow = 1,
height = 900,
width = 700,
state = list(sort = list(sort_spec('studyid'))))
```
# HELP PLOT AGP {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
pacman::p_load(webshot)
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_agp.html", height = "800px")
```
# HELP PLOT RANGES {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_ranges.html", height = "800px")
```
# HELP PLOT DAILY {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_daily.html", height = "800px")
```
# MODEL RESULTS
```{r party-model}
CGM_data_m1 <- CGM_iglu_metrics %>%
select(studyid, treatment, visit, data) %>%
rowwise() %>%
mutate(below_percent(data, targets_below = 53.9)[2],
below_percent(data, targets_below = 69.9)[2],
in_range_percent(data, target_ranges = list(c(70, 180)))[2],
above_percent(data, targets_above = 180.1)[2],
above_percent(data, targets_above = 250.1)[2]) %>%
mutate(below_69.9 = below_69.9 - below_53.9,
above_180.1 = above_180.1 - above_250.1) %>%
select(-data) %>%
pivot_longer(cols = -c(1:3),
names_to = 'state',
values_to = 'pct') %>%
mutate(state = as.factor(state) %>%
fct_relevel('below_53.9',
'below_69.9',
'in_range_70_180',
'above_180.1',
'above_250.1') %>%
fct_rev())
target_plot <- CGM_data_m1 %>%
lmtree(pct ~ state | treatment + visit, data = .,
alpha = 0.10, bonferroni = FALSE) %>%
ggparty(terminal_space = 0.4, horizontal = TRUE) +
geom_edge() +
geom_edge_label() +
geom_node_label(line_list = list(aes(label = splitvar),
aes(label = gtsummary::style_pvalue(p.value, digits = 2, prepend_p = TRUE))),
line_gpar = list(list(size = 9),
list(size = 9, fontface = 'bold')),
ids = "inner") +
geom_node_plot(
gglist = list(
geom_bar(aes(x = pct, y = "", fill = state),
position = 'stack', stat = "summary", fun = "mean"),
scale_y_discrete(breaks = NULL),
scale_fill_manual(values = c("#F9B500", "#F9F000", "#48BA3C", "#F92D00", "#8E1B1B"),
drop = FALSE,
labels = c("Very High\n>250",
"High\n181-250",
"Target\n70-180",
"Low\n54-69",
"Very Low\n<54"),
guide = guide_legend(reverse = TRUE,
label.position = 'bottom')),
labs(y = NULL,
x = "Percent (%)",
fill = "Percent (%) Time spent in \nstandardized glycemic ranges"),
theme_bw(),
theme(legend.position='bottom',
legend.key.height = unit(0.3, 'cm'),
legend.key.width = unit(1.5, 'cm'),
strip.background = element_blank(),
strip.text = element_blank(),
legend.title=element_text(size=9))),
ids = "terminal",
shared_axis_labels = TRUE
)
ggsave(str_glue("CGM_target.png"),
plot = target_plot,
scale = 4,
width = 900, height = 600, units = 'px')
```
![party-model](CGM_target.png){width="75%", height="75%"}
# MODEL RESULTS {data-navmenu="OTHER METRICS"}
## Inputs {.sidebar}
**Model Results Legend**
Recursive Partytioning models results ([partykit](https://cran.r-project.org/web/packages/partykit/index.html)) using p-values to determine splits in the data. Here is the conditional inference tree which shows how and what factors contribute to the differential effect of Continuous Glucose Monitoring (CGM) during the clinical trial. The [ggparty](https://cran.r-project.org/web/packages/ggparty/index.html) function allows to use the ggplot syntax to customize the tree which allows more adjustments and is more aesthetically pleasing. Starting from the top the graphic represents:
- [geom_rug](https://ggplot2.tidyverse.org/reference/geom_rug.html) A Rug display representing the individual responses
- [stat_halfeye](https://mjskay.github.io/ggdist/reference/stat_sample_slabinterval.html) A probability density (or mass) function
- [point_interval](https://mjskay.github.io/ggdist/reference/point_interval.html) Point interval representing the mean with 66% and 95% confidence interval
- [geom_boxplot](https://ggplot2.tidyverse.org/reference/geom_boxplot.html) A boxplot to compactly displays the distribution. It visualizes five summary statistics (the median, the 25th and 75th percentiles, and two whiskers. The whisker extends +/- 1.5 * IQR from the hinges
## Column {.bgwhite}
```{r party-mod-viz}
CGM_metric <- import("CGM_metric.csv")
vars <- CGM_metric %>%
filter(Metric_function != '') %>%
arrange(Metric_function)
for(i in vars$Metric_function){
metric_run <- sym(paste(i))
metric_lab <- vars %>%
filter(Metric_function == i) %>%
pull(Metric_Name)
metric_plot <- CGM_iglu_metrics %>%
lmtree(formula(str_glue("{metric_run} ~ 1 | treatment + visit")),
data = .,
alpha = 0.10, bonferroni = FALSE) %>%
ggparty(terminal_space = 0.4, horizontal = TRUE) +
geom_edge() +
geom_edge_label() +
geom_node_label(line_list = list(aes(label = splitvar),
aes(label = style_pvalue(p.value, digits = 2, prepend_p = TRUE))),
line_gpar = list(list(size = 9),
list(size = 9, fontface = 'bold')),
ids = "inner") +
geom_node_plot(
gglist = list(
stat_halfeye(aes(x = !!metric_run, y = ""),
point_interval = mean_qi,
justification = -0.05),
geom_rug(aes(x = !!metric_run), alpha = 0.25, sides = 't', length = unit(0.05, "npc")),
geom_boxplot(aes(x = !!metric_run, y = ""),
width = 0.30, outlier.shape = NA,
position = position_nudge(y = -0.25)),
scale_y_discrete(breaks = NULL),
labs(y = NULL,
x = metric_lab),
theme_bw()),
ids = "terminal",
shared_axis_labels = TRUE)
ggsave(str_glue("CGM-models/{metric_run}.png"),
plot = metric_plot,
scale = 4,
width = 900, height = 600, units = 'px')
}
```
```{r}
# devtools::install_github("walkerke/bsselectR")
library(bsselectR)
plots_in <- str_glue("CGM-models/{vars$Metric_function}.png")
names(plots_in) <- vars$Metric_Name
bsselect(plots_in,
frame_height = "800px", frame_width = "1200px",
style = "btn-primary",
box_width = 'fit',
type = "img",
selected = "ADRR (Average Daily Risk Range)",
live_search = TRUE,
show_tick = TRUE)
```
# DEFINITIONS {data-navmenu="OTHER METRICS"}
```{r}
CGM_metric %>%
gt() %>%
cols_move_to_end(Metric_Time_dependent) %>%
cols_move(Metric_function, Metric_Name) %>%
cols_label(Metric_function = 'iglu R output',
Metric_Name = "Name",
Metric_Iglu = "iglu R function",
Metric_Time_dependent = "Time Dependent") %>%
fmt_missing(Reference, missing_text = "-") %>%
fmt_markdown(c(Reference, Metric_Iglu)) %>%
cols_align('left') %>%
cols_width(Metric_Name ~ px(450),
Metric_Iglu ~ px(250),
Reference ~ px(250)) %>%
tab_options(table.font.size = "medium",
data_row.padding = px(2),
footnotes.padding = px(2),
source_notes.padding = px(2))
```