ggsurvplot_facet возвращает: ошибка в grDevices::col2rgb(color, TRUE): недопустимое имя цвета при использовании внутри функции

Я пытаюсь построить кривые выживания для нескольких переменных, связанных с переменным полом, с помощью функции ggsurvplot_facet(). Когда я применяю свой код к одной подогнанной модели, он работает нормально. Однако, когда я пытаюсь использовать один и тот же код в функции или в цикле for, он не может построить все кривые выживания, которые должны быть построены, и возвращает ошибку. Я бы выполнил это построение в самой ggsurvplot_facet(), если бы он допускал в качестве входных данных список элементов survfit, точно так же, как это делает ggsurvplot(), но ggsurvplot_facet() позволяет использовать только один элемент survfit за раз.

Я запускаю свой код в RStudio на MacBook Pro 2018 года с Mac OS High Sierra.

Рассмотрим следующий набор данных: http://s000.tinyupload.com/index.php?file_id=01704535336107726906

Он содержит наблюдения за несколькими визитами для 100 субъектов и 4 различных переменных. Две переменные (переменная1 и переменная2) могут иметь два разных значения (0 или 1), а две другие переменные (переменная3 и переменная4) могут иметь три разных значения (0, 1 или 2).

Я начал работать с теми, которые могут иметь два разных значения, и написал следующий код:

# Load libraries
require(mgcv)
require(msm)
library(dplyr)
library(grDevices)
library(survival)
library(survminer)


# Set working directory
dirname<-dirname(rstudioapi::getSourceEditorContext()$path)
setwd(dirname)


load("ggsurvplot_facet_error.rda")


fit_test <- survfit(
  Surv(follow_up, as.numeric(status)) ~ (sex + variable1), data = data)

plot_test <- ggsurvplot_facet(fit_test,
                                     data = data,
                                     pval = TRUE,
                                     conf.int = TRUE,
                                     surv.median.line = "hv", # Specify median survival
                                     break.time.by = 1,
                                     facet.by = "sex",
                                     ggtheme = theme_bw(), # Change ggplot2 theme
                                     palette = "aaas",
                                     legend = "bottom",
                                     xlab = "Time (years)",
                                     ylab = "Death probability",
                                     panel.labs = list(sex_recoded=c("Male", "Female")),
                                     legend.labs = c("A", "B")
) 

plot_test

Этот код отлично работает и генерирует следующий график:

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

Однако, когда я пытаюсь преобразовать этот код в функцию или цикл FOR, чтобы он применял один и тот же код к переменной1 и переменной2, я всегда получаю сообщение об ошибке с частью цвета/палитры на этапе построения графика.

# Variables_with_2_categories:  variable1 and variable2
two <- c("variable1", "variable2")

## TEST #1: USING A FUNCTION

fit_plot_function <- function(x) {

# FIT part of the function
  two.i <- two[i]

fit_temp <- survfit(Surv(as.numeric(follow_up), as.numeric(status)) ~ 
                        sex + eval(as.name(paste0(two.i))), data = data)

# PLOT part of the function
  plot_temp <- ggsurvplot_facet(fit_temp,
                                data = data,
                                pval = TRUE,
                                conf.int = TRUE,
                                surv.median.line = "hv", # Specify median survival
                                break.time.by = 1,
                                facet.by = "sex",
                                ggtheme = theme_bw(), # Change ggplot2 theme
                                palette = "aaas",
                                legend = "bottom",
                                xlab = "Time (years)",
                                ylab = "Death probability",
                                panel.labs = list(sex_recoded=c("Male", "Female")),
                                legend.labs = rep(c("A", "B"),2)
  ) 
}


fit_plot_function(two)
# Warning message:
#  Now, to change color palette, use the argument palette= 
#  'eval(as.name(paste0(two.i)))' instead of color = 'eval(as.name(paste0(two.i)))' 

print(plot_temp)

# Error in grDevices::col2rgb(colour, TRUE) : 
#  invalid color name 'eval(as.name(paste0(two.i)))'

Похоже, когда он оценивает имена переменных, которые были проанализированы с помощью вектора, он не распознает имена переменных. С циклом FOR все происходит точно так же:

## TEST #2: USING A FOR LOOP

n.two <- length(two)

for(i in 1:n.two) {
  two.i <- two[i]

  fit_temp <- survfit(Surv(as.numeric(follow_up), as.numeric(status)) ~ 
                        (sex + eval(as.name(paste0(two.i)))), data = data)



  plot_temp <- ggsurvplot_facet(fit_temp,
                                data = data,
                                pval = TRUE,
                                conf.int = TRUE,
                                surv.median.line = "hv", # Specify median survival
                                break.time.by = 1,
                                facet.by = "sex",
                                ggtheme = theme_bw(), # Change ggplot2 theme
                                palette = "aaas",
                                legend = "bottom",
                                xlab = "Time (years)",
                                ylab = "Death probability",
                                panel.labs = list(sex_recoded=c("Male", "Female")),
                                legend.labs = rep(c("A", "B"),2)
    ) 
}

print(plot_temp)

# ERROR: Now, to change color palette, use the argument palette= 'eval(as.name(paste0(two.i)))' 
# instead of color = 'eval(as.name(paste0(two.i)))

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

Большое спасибо за Вашу помощь,

Наилучшие пожелания,

Ятросин

> sessionInfo()
R version 3.5.1 (2018-07-02)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] survminer_0.4.3.999 ggpubr_0.2          magrittr_1.5        ggplot2_3.1.1       survival_2.44-1.1  
[6] dplyr_0.8.0.1       msm_1.6.7           mgcv_1.8-27         nlme_3.1-137       

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1        pillar_1.3.1      compiler_3.5.1    plyr_1.8.4        tools_3.5.1       digest_0.6.18    
 [7] tibble_2.1.1      gtable_0.3.0      lattice_0.20-38   pkgconfig_2.0.2   rlang_0.3.4       Matrix_1.2-17    
[13] ggsci_2.9         rstudioapi_0.10   cmprsk_2.2-7      yaml_2.2.0        mvtnorm_1.0-10    expm_0.999-4     
[19] xfun_0.6          gridExtra_2.3     knitr_1.22        withr_2.1.2       survMisc_0.5.5    generics_0.0.2   
[25] grid_3.5.1        tidyselect_0.2.5  data.table_1.12.2 glue_1.3.1        KMsurv_0.1-5      R6_2.4.0         
[31] km.ci_0.5-2       purrr_0.3.2       tidyr_0.8.3       scales_1.0.0      backports_1.1.4   splines_3.5.1    
[37] assertthat_0.2.1  xtable_1.8-3      colorspace_1.4-1  labeling_0.3      lazyeval_0.2.2    munsell_0.5.0    
[43] broom_0.5.2       crayon_1.3.4      zoo_1.8-5   

person Yatrosin    schedule 16.04.2019    source источник


Ответы (1)


Пришло время мурлыкать. То, что вы хотите, может быть сделано с помощью purrr. Вы можете прочитать о создании ggplot2 purrr здесь и другие примеры здесь.

Прежде всего нам нужно преобразовать ваши данные в длинный формат с помощью tidyr::gather. Мы сохраним все во фрейме данных, как было, кроме переменных 1, 2, 3, 4. Они будут плавиться.

library(tidyr)
library(dplyr)
library(purrr)

data %>% 
  gather(num, variable, -sample_id,  -sex,
         -visit_number, -age_at_enrollment,
         -follow_up, -status) %>% 
  mutate(num2 = num) %>% # We'll need this column later for the titles
  as_tibble() -> long_data


# A tibble: 2,028 x 8
   sample_id   sex    visit_number age_at_enrollment follow_up status num       variable
   <fct>       <fct>  <fct>                    <dbl>     <dbl> <fct>  <chr>        <int>
 1 sample_0001 Female 1                         56.7     0     1      variable1        0
 2 sample_0001 Female 2                         57.7     0.920 1      variable1        0
 3 sample_0001 Female 3                         58.6     1.90  1      variable1        0
 4 sample_0001 Female 4                         59.7     2.97  2      variable1        0
 5 sample_0001 Female 5                         60.7     4.01  1      variable1        0
 6 sample_0001 Female 6                         61.7     4.99  1      variable1        0
 7 sample_0002 Female 1                         55.9     0     1      variable1        1
 8 sample_0002 Female 2                         56.9     1.04  1      variable1        1
 9 sample_0002 Female 3                         58.0     2.15  1      variable1        1
10 sample_0002 Female 4                         59.0     3.08  1      variable1        1
# ... with 2,018 more rows

Теперь нам нужно преобразовать наш длинный фрейм данных во вложенный фрейм данных и map! Будьте точны с ggsurvplot — эта функция не поддерживает tibbles, которые создаются во время nest().

long_data %>% 
  group_by(num) %>% 
  nest() %>% 
  mutate(
    # Run survfit() for every variable
    fit_f = map(data, ~survfit(Surv(follow_up, as.numeric(status)) ~ (sex + variable), data = .)),
    # Create survplot for every variable and survfit
    plots = map2(fit_f, data, ~ggsurvplot(.x,
                                          as.data.frame(.y), # Important! convert from tibble to data.frame 
                                          pval = TRUE,
                                          conf.int = TRUE,
                                          facet.by = "sex",
                                          surv.median.line = "hv", 
                                          break.time.by = 1,
                                          ggtheme = theme_bw(),
                                          palette = "aaas",
                                          xlab = "Time (years)",
                                          ylab = "Death probability") +
                   ggtitle(paste0("This is plot of ", .y$num2)) + # Add a title
                   theme(legend.position = "bottom"))) -> plots

Теперь вы можете вернуть свои графики, набрав следующее:

plots$plots[[1]]
plots$plots[[2]]
plots$plots[[3]] 
plots$plots[[4]] # plotted below

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

И сохраните все свои графики с помощью map2()

map2(paste0(unique(long_data$num), ".pdf"), plots$plots, ggsave)

ОБНОВЛЕНИЕ

К сожалению, я не могу понять, как изменить метки легенды. Единственное решение, которое я могу предложить, приведено ниже. Помните, что plots$plots[[…]] — это объект ggplot, так что потом вы сможете все изменить. Например, чтобы изменить метки легенды, мне просто нужно добавить scale_fill_discrete и scale_color_discrete. То же самое можно сделать с названием, лабораториями, темой и т. д.

library(ggsci) # to add aaas color palette

plots$plots[[3]] +
  labs(title = "Variable 3",
       subtitle = "You just have to be the best") +
  ggsci::scale_color_aaas(guide = F) +
  ggsci::scale_fill_aaas(label = LETTERS[1:3])

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

person atsyplenkov    schedule 16.04.2019
comment
Уважаемый @atsyplenkov, возможно, это другой вопрос, но как бы вы изменили код, чтобы он отображал заголовок для каждого из графиков (переменная1, переменная2...) и как бы вы изменили метки легенды, учитывая, что есть два типа графиков: те, которые содержат две категории (A, B) и те, которые содержат 3 категории (A, B, C)? Проблема с легендой была частью оригинального поста. Сейчас я чувствую себя тяжело мурлыкающим. Спасибо - person Yatrosin; 17.04.2019
comment
@Yatrosin, к сожалению, я не могу понять, как изменить метки легенды. Это очень сложно, потому что количество меток меняется. Чтобы добавить заголовок, просто добавьте после ggsurvplot функцию ggtitle с +, поскольку это была обычная ggplot2. Я также создал новый столбец num2, равный num. Смотрите ответ. - person atsyplenkov; 17.04.2019
comment
Я пробовал .y$num для заголовка раньше, но всегда получаю следующую ошибку, как для .y$num, так и для .y$num2: Warning messages: 1: Unknown or uninitialised column: 'num'. 2: Unknown or uninitialised column: 'num'. 3: Unknown or uninitialised column: 'num'. 4: Unknown or uninitialised column: 'num'. - person Yatrosin; 17.04.2019
comment
Вы создали .y$num2? Попробуйте перезапустить R и еще раз запустить код из моего ответа. - person atsyplenkov; 17.04.2019
comment
Уважаемый @Yatrosin, мне кажется, что проще всего потом преобразовать 0, 1, 2 в ("A", "B", "C"). Чтобы вручную добавить plots$plots[[…]]. Например, для variable 3 это будет так: plots$plots[[3]] + ggsci::scale_color_aaas(guide = F) + ggsci::scale_fill_aaas(label = LETTERS[1:3]). Я использую пакет ggsci для цветовой палитры, не забудьте установить его. - person atsyplenkov; 17.04.2019
comment
@Yatrosin, ты пытался изменить ярлыки и название легенды? - person atsyplenkov; 19.04.2019
comment
Спасибо, я думаю, что смогу оценить проблему, если разделю свои переменные на два фрейма данных, один из которых содержит те, которые имеют 2 категории, а другой - переменные, которые имеют три категории. - person Yatrosin; 19.04.2019