--- title: "

Sustained Response Analysis using Trajectory Clustering by Agustin Calatroni" output: html_document: self_containded: TRUE code_download: yes toc: false --- ```{=html} ``` ```{r setup, include=FALSE} options(width = 200) knitr::opts_chunk$set(echo = FALSE, eval = TRUE, warning = FALSE, message = FALSE, comment = NA, cache = FALSE) ``` ```{r load-packages} # https://rpubs.com/acalatroni/764773 pacman::p_load(tidyverse) pacman::p_load(ggtext) # devtools::install_github("hrbrmstr/waffle") library(waffle) # pacman::p_load(gtsummary) # http://www.danieldsjoberg.com/gtsummary-weill-cornell-presentation/#1 ``` ```{r import-data} d1_w <- read.csv("https://raw.githubusercontent.com/VIS-SIG/Wonderful-Wednesdays/master/data/2021/2021-04-14/WWW_SustainedResponse.csv") %>% mutate(ID = parse_number(USUBJID), TRTC = factor(TRT, labels = c("D1","D2","C"))) write.csv(d1_w, "WWW_SustainedResponse.csv") d1_l <- d1_w %>% pivot_longer(cols = c(BASELINE, WEEK01, WEEK04, WEEK08, WEEK16, WEEK24, WEEK32, WEEK40, WEEK52), names_to = "TIMEC", values_to = "PASI") %>% select(ID, TRTC, TIMEC, PASI) %>% mutate(TIMEN = ifelse(TIMEC == "BASELINE", 0, parse_number(TIMEC))) %>% group_by(ID) %>% mutate(PASI_CHG = PASI - PASI[TIMEC == "BASELINE"], PASI_PCHG = 100*(PASI - PASI[TIMEC == "BASELINE"]) / PASI[TIMEC == "BASELINE"]) %>% ungroup() ``` ```{r impute-data, include=FALSE} pacman::p_load(mice) d1_imp <- mice(d1_w %>% select(-USUBJID, -TRT, -ID), m = 1, maxiter = 500, method = 'pmm', seed = 123) d1_comp <- bind_cols( d1_w %>% select(ID), complete(d1_imp)) ``` ```{r long-data} d1_l_imp <- d1_comp %>% pivot_longer(cols = c(BASELINE, WEEK01, WEEK04, WEEK08, WEEK16, WEEK24, WEEK32, WEEK40, WEEK52), names_to = "TIMEC", values_to = "PASI") %>% select(ID, TRTC, TIMEC, PASI) %>% mutate(TIMEN = ifelse(TIMEC == "BASELINE", 0, parse_number(TIMEC))) %>% group_by(ID) %>% mutate(PASI_CHG = PASI - PASI[TIMEN == 0], PASI_PCHG = 100*(PASI - PASI[TIMEN == 0]) / PASI[TIMEN == 0]) %>% ungroup() ``` ```{r kml-clusters} pacman::p_load(latrend) kmlMethod <- lcMethodKML(response = "PASI_PCHG", id = "ID", time = "TIMEN", nbRedrawing = 25) kmlMethods <- lcMethods(kmlMethod, nClusters = 1:5) kmlModels <- latrendBatch(kmlMethods, data = d1_l_imp, verbose = FALSE) kmlModel5 <- subset(kmlModels, nClusters == 5, drop = TRUE) clusterNames(kmlModel5) <- c('T2','T3','T1','T4','T5') ``` ```{r derive-data} t_p <- data.frame(TRAJ = clusterNames(kmlModel5), P = gtsummary::style_percent( clusterProportions(kmlModel5) %>% as.vector(), symbol = TRUE, digits = 1)) %>% mutate(TRAJ = TRAJ %>% fct_relevel('T1', 'T2', 'T3', 'T4', 'T5') ) %>% mutate(TRAJ_P = str_glue("{TRAJ} ({P})") %>% fct_relevel('T1', 'T2', 'T3', 'T4', 'T5')) %>% select(-P) d_per <- data.frame(ID = ids(kmlModel5), TRTC = d1_w$TRTC, TRAJ = trajectoryAssignments(kmlModel5)) %>% mutate(TRAJ = TRAJ %>% fct_relevel('T1','T2','T3','T4','T5')) %>% left_join(t_p) %>% bind_cols(postprob(kmlModel5) %>% data.frame()) d_vis <- d1_l %>% left_join(d_per %>% select(ID, TRAJ, TRAJ_P)) d_traj <- clusterTrajectories(kmlModel5, at = seq(0,52)) %>% as.data.frame() %>% rename(TRAJ = Cluster) %>% mutate(TRAJ = TRAJ %>% fct_relevel('T1','T2','T3','T4','T5')) %>% left_join(t_p) ``` ```{r create-figures} f0 <- ggplot(data = d_vis, aes(x = TIMEN, y = PASI_PCHG, group = factor(ID))) + geom_line(alpha = 0.20) + scale_x_continuous("Week", breaks = c(0, 1, 4, 8, 16, 24, 32, 40, 52), labels = c("B ","1","4","8","16","24","32","40","52")) + scale_y_continuous("PASI Percent Change", limits = c(-100, 25), breaks = c( 25, 0, -25, -50, -75, -90, -100), labels = c('25%', '0%', '-25%', '50%', '-75%', '-90%', '-100%'), sec.axis = dup_axis(name = "") ) + theme_minimal() + theme(panel.grid.minor = element_blank(), plot.margin = margin(0,0,0,0, 'pt')) f1 <- ggplot(data = d_vis, aes(x = TIMEN, y = PASI_PCHG, group = factor(ID))) + geom_line(alpha = 0.20) + scale_x_continuous(name = NULL, breaks = c(0, 1, 4, 8, 16, 24, 32, 40, 52), labels = c("B "," 1","4","8","16","24","32","40","52")) + scale_y_continuous("PASI Percent Change", limits = c(-100, 25), breaks = c( 25, 0, -25, -50, -75, -90, -100), labels = c('25%', '0%', '-25%', '50%', '-75%', '-90%', '-100%'), sec.axis = dup_axis(name = "")) + guides(color = FALSE) + facet_wrap(~ TRAJ_P, nrow = 1) + theme_minimal() + theme(panel.grid.minor = element_blank(), plot.margin = margin(0,0,0,0, 'pt')) + theme( strip.background = element_blank(), strip.text = element_textbox( size = 12, color = "black", fill = "gray85", box.color = "gray85", halign = 0.5, linetype = 1, r = unit(5, "pt"), width = unit(1, "npc"), padding = margin(3, 0, 3, 0), margin = margin(3, 3, 3, 3) ) ) f2 <- ggplot(data = d_traj, aes(x = TIMEN, y = PASI_PCHG, group = TRAJ_P, color = TRAJ_P)) + geom_line(lwd = 1.5) + scale_x_continuous(name = NULL, breaks = c(0, 1, 4, 8, 16, 24, 32, 40, 52), labels = c("B "," 1","4","8","16","24","32","40","52")) + scale_y_continuous("PASI Percent Change", limits = c(-100, 0), breaks = c( 0, -25, -50, -75, -90, -100), labels = c('0%', '-25%', '50%', '-75%', '-90%', '-100%'), sec.axis = dup_axis(name = "")) + ggthemes::scale_color_tableau(palette = 'Classic Cyclic') + guides(color = FALSE) + facet_wrap(~ TRAJ_P, nrow = 1) + theme_minimal() + theme(panel.grid.minor = element_blank(), strip.background = element_blank(), strip.text.x = element_blank(), plot.margin = margin(0,0,0,0, 'pt')) library(waffle) d4 <- d_per %>% count(TRAJ_P, TRTC) %>% mutate(TRTC = fct_rev(TRTC)) f3 <- ggplot(d4, aes(fill = TRTC, values = n)) + geom_waffle(color = "white", size = 0.50, n_rows = 10, flip = TRUE) + facet_wrap(~ TRAJ_P, nrow = 1) + scale_x_discrete() + scale_y_continuous(name = "Treatment Counts", labels = function(x) x * 10, expand = c(0,0), sec.axis = dup_axis(name = "")) + ggthemes::scale_fill_tableau(palette = 'Miller Stone', direction = -1, name = NULL, labels = c("Comparator (C)","TRT: Dose 1 (D1)","TRT: Dose 2 (D2)")) + theme_minimal() + theme(strip.background = element_blank(), strip.text.x = element_blank(), panel.grid = element_blank(), axis.ticks.y = element_line(), axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), legend.position = c(0.90, 0.75), plot.margin = margin(0,0,0,0, 'pt')) f4 <- ggplot(d4, aes(fill = TRTC, y = n, x = 1)) + geom_bar(position="fill", stat="identity") + facet_wrap(~ TRAJ_P, nrow = 1, strip.position = 'bottom', labeller = as_labeller(c(`T1 (29.0%)` = "T1 (n=262) **x**", `T2 (30.9%)` = "T2 (n=278) **y**", `T3 (30.6%)` = "T3 (n=274) **z**", `T4 (8.15%)` = "T4 (n=74) **z**", `T5 (1.33%)` = "T5 (n=12) **z**"))) + scale_x_continuous(labels = NULL, breaks = NULL) + scale_y_continuous(name = "Treatment Percents", breaks = c( 0, 0.25, 0.50, 0.75, 1), labels = c('0%', '25%', '50%', '75%', '100%'), expand = c(0,0), sec.axis = dup_axis(name = "")) + ggthemes::scale_fill_tableau(palette = 'Miller Stone', direction = -1) + guides(fill = FALSE) + theme_minimal() + theme(strip.background = element_blank(), #strip.text.x = element_blank(), panel.grid = element_blank(), axis.ticks.y = element_line(), axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), plot.margin = margin(0,0,0,0, 'pt')) + theme( strip.background = element_blank(), strip.text = element_textbox( size = 12, color = "black", fill = "gray85", box.color = "gray85", halign = 0.5, linetype = 1, r = unit(5, "pt"), width = unit(1, "npc"), padding = margin(3, 0, 3, 0), margin = margin(3, 3, 3, 3) ) ) ``` ```{r create-legend} df <- data.frame( x = 0.00, y = 1.0, label = "**Figure Legend**: **A** Individual patients response over time **B** Individual patients response with 5 trajectories (T1, T2, T3, T4 & T5) cluster solution using a non-parametric k-means for longitudinal data **C** Grouped patients response with 5 trajectories (T) cluster solution **D** Treatment composition counts by trajectories clusters (Waffle Chart) **E** Treatment composition percents by trajectories clusters (Stacked Chart w/ pairwise differences#)") t <- ggplot() + geom_textbox( data = df, aes(x, y, label = label), color = "black", fill = "gray85", box.color = "gray85", width = grid::unit(1.0, "npc"), # 73% of plot panel width hjust = 0, vjust = 1, size = 4 ) + scale_x_continuous(expand = c(0,0), limits = c(0,1))+ scale_y_continuous(expand = c(0,0), limits = c(0,1)) + theme_void() ``` #### {.tabset .tabset-pills} ##### Figure ```{r combine-figures, fig.height = 11, fig.width = 12, dpi = 300} pacman::p_load(patchwork) (f0 + t)/f1/f2/f3/f4 + plot_annotation(tag_levels = list(c('A',' ','B','C','D','E')), caption = '#Trajectories clusters (T) not sharing the same letter (**x**,**y**,**z**) are significantly **p<0.01** different (i.e. T1 **x** different than T2 **y**) in their treament composition', theme = theme(plot.caption = element_textbox(size = 12)) ) ``` ##### **E** Pairwise Differences ```{r, fig.width = 7, fig.height = 4} pacman::p_load(partykit) pacman::p_load(ggparty) m1 <- ctree(TRTC ~ TRAJ, data = d_per) ggparty(m1) + 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 = 10), list(size = 9, fontface = 'bold')), ids = "inner") + geom_node_label(aes(label = paste0("N = ", nodesize)), ids = "terminal", size = 4, nudge_y = 0.02, nudge_x = 0.01) + geom_node_plot(gglist = list(geom_bar(aes(x = 1, fill = TRTC %>% fct_rev()), position = position_fill()), scale_x_continuous(name = NULL, breaks = NULL, labels = NULL), scale_y_continuous(name = "Percent", breaks = c( 0, 0.25, 0.50, 0.75, 1), labels = c('0%', '25%', '50%', '75%', '100%')), ggthemes::scale_fill_tableau(palette = 'Miller Stone', direction = -1, name = NULL, labels = c("Comparator (C)", "TRT: Dose 1 (D1)", "TRT: Dose 2 (D2)"), guide = guide_legend(reverse = TRUE)), theme_minimal(), theme(panel.grid.minor = element_blank(), plot.margin = margin(0,0,0,0, 'pt')) ), shared_axis_labels = TRUE ) ```