Сбой при объединении ggplots с графиками, созданными из другого пакета: почему и как добиться успеха?

#r #dataframe #ggplot2 #plot

#r #фрейм данных #ggplot2 #график

Вопрос:

Я хочу объединить два ggplots с двумя калибровочными графиками, созданными riskRegression в.

Я пробовал много подходов, например par(mfrow) (см. Ниже), но потерпел неудачу. Я считаю, что должен быть способ комбинировать ggplots с другими графиками таким par(mfrow=c(2,2)) образом.

Вопрос: почему этот подход недействителен и как его можно исправить?

Образец данных o можно найти ниже.

Я использую

 library(riskRegression)
library(survival)
library(tidyverse) 
 

Сначала выполните калибровку

 i <- Score(list("WHO w/o TERT" = coxph(Surv(event.tid, event==1) ~ WHO, data=o, x=T, y=T), 
                "WHO  TERT   " = coxph(Surv(event.tid, event==1) ~ WHO   tert.mut, data=o, x=T, y=T)), 
           Surv(event.tid, event==1)~1,
           data=o,
           times=c(60,90),
           plots = "calibration",
           summary="risks")
 

Затем два калибровочных графика

 plotCalibration(i, 
                cens.method = "local", 
                legend.x=.215,
                legend.y=.225,
                cex=1,
                times=60,
                col=c("#6DBCC3","#8B3A62"),
                ylab="Observed frequency",
                xlab="Predicted risk of recurrence",
                auc.in.legend = TRUE)
 

И

 plotCalibration(i, 
                cens.method = "local", 
                legend.x=.215,
                legend.y=.225,
                cex=1,
                times=90,
                col=c("#6DBCC3","#8B3A62"),
                ylab="Observed frequency",
                xlab="Predicted risk of recurrence",
                auc.in.legend = TRUE)
 

А затем два ggplot

 ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==60),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7)
 

И

 ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==90),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7) 
 

Попытка # 1

 par(mfrow=c(2,2), mai=c(.65,.75,.3,.3))
# the first two goes well
plotCalibration(i, 
                cens.method = "local", 
                legend.x=.215,
                legend.y=.225,
                cex=1,
                times=60,
                col=c("#6DBCC3","#8B3A62"),
                ylab="Observed frequency",
                xlab="Predicted risk of recurrence",
                auc.in.legend = TRUE)

plotCalibration(i, 
                cens.method = "local", 
                legend.x=.215,
                legend.y=.225,
                cex=1,
                times=90,
                col=c("#6DBCC3","#8B3A62"),
                ylab="Observed frequency",
                xlab="Predicted risk of recurrence",
                auc.in.legend = TRUE)

# here it goes wrong

ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==60),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7) 

ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==90),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7) 
 

Попытка # 2 создавала a ggarrange для объединения с par(mfrow) , что также не удалось.

Ожидаемый результат

введите описание изображения здесь

Образец данных

 o <- structure(list(event.tid = c(56, 127, 29.6, 18, 13.2, 41, 96, 
146.83, 45.37, 1, 105.29, 55, 13.84, 12, 26, 10.8, 1, 7.2, 22.68, 
59, 16, 8.4, 75.3, 25.7, 13, 21, 19, 22, 48, 93, 26, 72, 73.5, 
125, 31, 63, 2.02, 28.2, 2, 8, 60, 46.7, 53, 4, 85, 12, 75.9, 
1, 5, 2.5), event = c(0, 0, 1, 1, 1, 0, 0, 2, 1, 2, 0, 0, 1, 
1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 2, 0, 
0, 0, 1, 0, 0, 1, 2, 0, 0, 1, 0, 1, 0, 2, 0, 1), WHO = structure(c(3L, 
3L, 3L, 2L, 3L, 2L, 3L, 2L, 3L, 3L, 3L, 1L, 2L, 1L, 2L, 3L, 2L, 
3L, 2L, 3L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 
3L, 2L, 1L, 3L, 2L, 2L, 2L, 1L, 2L, 3L, 2L, 2L, 3L, 1L, 2L, 3L, 
2L), .Label = c("WHO-I", "WHO-II", "WHO-III"), class = "factor"), 
    tert.mut = structure(c(1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 2L), .Label = c("0", 
    "1"), class = "factor")), row.names = c(NA, -50L), class = "data.frame")
 

Комментарии:

1. Вы пробовали patchwork пакет?

2. Привет @markus. Да, но я не смог заставить его работать. Вы можете отправить ответ, используя patchwork.

Ответ №1:

Вот один из используемых подходов cowplot .

Вы можете использовать as_grob для преобразования своих plotCalibration графиков в grob, которые можно комбинировать с вашими 2 ggplots.

Вы можете дополнительно настроить свои калибровочные графики, добавив базовые графические операторы R в свои функции.

С plot_grid помощью from cowplot вы можете поместить свои графики 2×2 в упорядоченную форму, в данном случае с 2 строками 2 столбцами.

 library(riskRegression)
library(survival)
library(tidyverse) 
library(cowplot)

i <- Score(list("WHO w/o TERT" = coxph(Surv(event.tid, event==1) ~ WHO, data=o, x=T, y=T), 
                "WHO  TERT   " = coxph(Surv(event.tid, event==1) ~ WHO   tert.mut, data=o, x=T, y=T)), 
           Surv(event.tid, event==1)~1,
           data=o,
           times=c(60,90),
           plots = "calibration",
           summary="risks")

make_pc1 <- function() {
  par(mgp=c(4,1,0), mar=c(5,6,4,2) 0.1, cex.lab=1.4, cex.axis=1.5)
  plotCalibration(i, 
                  cens.method = "local", 
                  legend.x=.215,
                  legend.y=.225,
                  cex=1,
                  times=60,
                  col=c("#6DBCC3","#8B3A62"),
                  ylab="Observed frequency",
                  xlab="Predicted risk of recurrence",
                  auc.in.legend = TRUE)
  mtext("10-year prediction", adj=0.05, line = -1.4, cex=1, font=2)
}

pc1 <- as_grob(make_pc1)

make_pc2 <- function() {
  par(mgp=c(4,1,0), mar=c(5,6,4,2) 0.1, cex.lab=1.4, cex.axis=1.5)
  plotCalibration(i, 
                  cens.method = "local", 
                  legend.x=.215,
                  legend.y=.225,
                  cex=1,
                  times=90,
                  col=c("#6DBCC3","#8B3A62"),
                  ylab="Observed frequency",
                  xlab="Predicted risk of recurrence",
                  auc.in.legend = TRUE)
  mtext("10-year prediction", adj=0.05, line = -1.4, cex=1, font=2)
}

pc2 <- as_grob(make_pc2)

gg1 <- ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==60),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7)

gg2 <- ggplot(arrange(o, event.tid) %>% 
         transmute(ID = 1:nrow(o),
                   WHO = WHO,
                   tert.mut = tert.mut) %>%
         left_join(i$Calibration$plotframe, 
                   ., 
                   by="ID") %>%
         select(ID, times, model, risk, WHO, tert.mut) %>%
         pivot_wider(names_from = model, values_from = risk) %>%
         rename(., "woTERT" = "WHO w/o TERT", "wTERT" = "WHO  TERT   ") %>%
         filter(times==90),
       aes(x=woTERT, y=wTERT, shape=WHO, col=tert.mut, fill=tert.mut))  
  geom_point(size = 7) 

# Draw all 4 plots
plot_grid(pc1, pc2, gg1, gg2, ncol = 2)
 

График

cowplot с комбинированными ggplots и калибровочными графиками

Немного изменив интервал между графиками калибровки (например, используя title для меток осей и корректируя mgp линию полей заголовка оси), вы можете сделать:

 make_pc1 <- function() {
  par(mar=c(5,6,4,2) 0.1, cex.lab=1.4, cex.axis=1.5)
  plotCalibration(i, 
                  cens.method = "local", 
                  legend.x=.215,
                  legend.y=.225,
                  cex=1,
                  times=60,
                  col=c("#6DBCC3","#8B3A62"),
                  ylab="",
                  xlab="",
                  auc.in.legend = TRUE)
  mtext("10-year prediction", adj=0.05, line = -1.4, cex=1, font=2)
  title(ylab = "Observed frequency", mgp = c(4.5,1,0))
  title(xlab = "Predicted risk of recurrence", mgp = c(2.5,1,0))
}

pc1 <- as_grob(make_pc1)

make_pc2 <- function() {
  par(mar=c(5,6,4,2) 0.1, cex.lab=1.4, cex.axis=1.5)
  plotCalibration(i, 
                  cens.method = "local", 
                  legend.x=.215,
                  legend.y=.225,
                  cex=1,
                  times=90,
                  col=c("#6DBCC3","#8B3A62"),
                  ylab="",
                  xlab="",
                  auc.in.legend = TRUE)
  mtext("10-year prediction", adj=0.05, line = -1.4, cex=1, font=2)
  title(ylab = "Observed frequency", mgp = c(4.5,1,0))
  title(xlab = "Predicted risk of recurrence", mgp = c(2.5,1,0))
}

pc2 <- as_grob(make_pc2)

# Draw all 4 plots
plot_grid(pc1, pc2, gg1, gg2, ncol = 2)
 

График

модифицированные калибровочные графики для регулировки интервала между заголовками осей

Комментарии:

1. Спасибо @Ben. Это очень приятно. Я пытаюсь добавить mtext к plotCalibration after as.grob — т.е. pc1 mtext("10-year prediction", adj=0.05, line = -1.4, cex=1, font=2) . Однако это приводит к ошибке : non-numeric argument to binary operator . Возможно ли добавить mtext объект -object в as.grob -plot?

2. @cmirian Смотрите отредактированный ответ. Я разместил функции plotCalibration in, в которые вы можете добавлять все, что вам нравится (например, mtext и par ). Возможно, вам потребуется внести незначительные коррективы. Он также использует as_grob (с подчеркиванием) from cowplot для создания grobs. Надеюсь, это полезно.

3. Спасибо @Ben, ваши ответы всегда полезны. Я вознагражу вас щедростью, как только это будет возможно.

4. Я не совсем уверен, что вы имеете в виду, но, полагаю, вы чувствуете, что высота графика мала для калибровочных графиков (по крайней мере, по сравнению с другими 2-мя графиками). Если это так, вы можете уменьшить поля… например, чтобы уменьшить верхнюю границу графика калибровки, попробуйте par(mar=c(5,6,1,2) 0.1... («1» вместо «4» будет иметь большое значение). Вы можете поэкспериментировать, mar а также mgp получить желаемый вид. Часть компромисса здесь заключается в том, что у вас очень большие метки осей и тиков, которые тоже занимают место.

5. Или, mar=c(4,6,1,2) и уменьшение нижнего поля тоже выглядит неплохо…