survminer icon indicating copy to clipboard operation
survminer copied to clipboard

Saving ggsurvplot()

Open kassambara opened this issue 8 years ago • 18 comments

e-mail from a user

Hi Alboukadel, How are you? I hope you’re doing good. I was trying to save a KM plot using survminer. However, when I try to save the figure with ggsave I got an error message as below. I did some online search but could not really find a solution. Could you please help? Thanks in advance! Serigne


require("survival")
require("survminer")
 
fit<- survfit(Surv(time, status) ~ sex, data = lung)
 
# Drawing survival curves
ggsurvplot(fit)
 
# Change font size, style and color
#++++++++++++++++++++++++++++++++++++
## Not run:
# Change font size, style and color at the same time
pp = ggsurvplot(fit, main = "Survival curve",
           font.main = c(16, "bold", "darkblue"),
           font.x = c(14, "bold.italic", "red"),
           font.y = c(14, "bold.italic", "darkred"),
           font.tickslab = c(12, "plain", "darkgreen"))
ggsave("C:\\serigne\\KMsurvival.jpeg", plot=pp)
Saving 6.1 x 7.27 in image
Error in UseMethod("grid.draw") :
  no applicable method for 'grid.draw' applied to an object of class "c('ggsurvplot', 'list')"

kassambara avatar Feb 19 '17 07:02 kassambara

ggsurvplot() returns a list of ggplots containing survival curves and optionally the risk table. You can’t directly save the list using ggsave(), but you can save the output of print(ggsurvplot).

First, please install the latest version of survminer:

if(!require(devtools)) install.packages("devtools")
devtools::install_github("kassambara/survminer", build_vignettes = FALSE)

Then, type this:

require("survival")
fit<- survfit(Surv(time, status) ~ sex, data = lung)

require("survminer")
survp <- ggsurvplot(fit, risk.table = TRUE)
ggsave(file = "ggsurv.pdf", print(survp))

It should work.

kassambara avatar Feb 19 '17 07:02 kassambara

Hi, How can I use ggsave if I dont need the "number at risk table" ?

require("survival")
fit<- survfit(Surv(time, status) ~ sex, data = lung)

require("survminer")
survp <- ggsurvplot(fit, risk.table = FALSE)
ggsave(file = "ggsurv.pdf", print(survp))

Saving 4.46 x 7 in image Error in UseMethod("grid.draw") : no applicable method for 'grid.draw' applied to an object of class "list"

VincentGuyader avatar Aug 08 '17 07:08 VincentGuyader

Hi,

To save ggsurvplot + risktable, use this:

pdf("survplot.pdf")
print(survp, newpage = FALSE)
dev.off()

To save only survival plots without the risk table, use this:

pdf("survplot.pdf")
print(survp$plot, newpage = FALSE)
dev.off()

or use this:

ggsave("survplot.pdf", surp$plot)

kassambara avatar Aug 08 '17 08:08 kassambara

Im getting a similar error. Please see my code below.

fit<- survfit(Surv(time, dead) ~ copy_number, data = datain) p <- ggsurvplot(fit, conf.int = TRUE, pval = TRUE, main = "Survival", palette = c("#ff0000", "#0000ff"), font.main = c(10, "bold"), font.x = c(8, "bold"), font.y = c(8, "bold"), font.tickslab = c(6, "bold"), pval.size = 2, size = 0.2, censor.size = 1, legend = "bottom" ) p$plot <- p$plot + theme(legend.text = element_text(size = 4), legend.title = element_text("", size = 4), legend.key.size = unit(0.2, "cm"))

ggsave(filename= "/home/philserver/Desktop/mt_amp.tiff", print(p), device = "tiff", height = 2, width = 2, dpi = 300)

Error in UseMethod("grid.draw") : no applicable method for 'grid.draw' applied to an object of class "list"

In spite of this error, the plot is still saved. However, the error stops the R pipeline I have written, and the other dozen graphs in my pipeline are not generated. Is there a way to resolve the issue of saving ggsurvplots without throwing an error?

Phil-T avatar Sep 04 '17 16:09 Phil-T

You can't use, the standard ggsave(), because ggsurvplot() always generates an object of class list, even if you set risk.table to FALSE. I think that I already suggested a solution to this issue. Please let me know if the solution works for you.

kassambara avatar Sep 04 '17 17:09 kassambara

The function ggpubr::ggexport() should work also for a list of ggplots, which are not supported by ggsave

kassambara avatar Sep 04 '17 17:09 kassambara

To anyone reading this, there is still no ggsave method for ggsurvplot objects.

jvanlunenburg avatar Jul 24 '20 09:07 jvanlunenburg

no, let's open this issue

kassambara avatar Jul 24 '20 10:07 kassambara

ggsave(
  filename ='os-plot.pdf',
  plot = print(os.plot, newpage = FALSE),
  device = 'pdf',
  path = 'data/output',
  width = 8,
  height = 9
)

Worked for me.

chunjie-sam-liu avatar Jan 20 '21 08:01 chunjie-sam-liu

This still does not work in version 0.4.9 e.g.:

ggsave(filename = "./debug/test_picture.png",plot = print(obj), device = "png")

produces a blank image for a simple Kaplan-Meier plot with number at risk table. generated with survminer.

Similarly the producing a plot to pdf method produces an unreadable pdf (error on loading).

UPDATE: the ggexport method does work, producing one blank image and one plot. The full plot is produced with the name (using the above example) test_picture002.png (001 is blank).

ggexport(filename = "./debug/test_picture.png",plot = obj, device = "png")

Hopefully useful for someone!

DKBurnsBresMed avatar Jul 30 '21 11:07 DKBurnsBresMed

Neither the ggsave nor the ggexport works well for me.

I'm getting blank output with ggsave whether I try to save as png or pdf. ggexport will allow me to save as a png, but for publication I need it in pdf. To get a pdf, I have to export using the RStudio utility and some elements are not rendered accurately. Any more ideas/changes?

sstandage avatar Aug 17 '21 15:08 sstandage

There's a fix already, but you can use the code before it's been updated in the CRAN

grid.draw.ggsurvplot <- function(x){
  survminer:::print.ggsurvplot(x, newpage = FALSE)
}

Run this function before you save your plot using ggsave to save your plot.

Full working example

The code below should save the survival plot with risk table.

library("survival")
library("survminer")

fit <- survfit(Surv(time, status) ~ sex, data = lung)

p <- ggsurvplot(fit, risk.table = TRUE)

# add method to grid.draw
grid.draw.ggsurvplot <- function(x){
  survminer:::print.ggsurvplot(x, newpage = FALSE)
}

# Remember to pass object `p`.
ggsave("survival.png", plot = p, 
       dpi=300, width = 10, height = 7, units = "in")

adayim avatar Oct 08 '21 17:10 adayim

@mleipzig It works perfectly fine, I tried the all the code with the first code. You forgot to pass the plot object. The ggsurvplot function returns a ggsurvplot object, not a ggplot object and it will not write anything to last_plot(). Read ggsave function help thoroughly. I think you should correct your plot saving code:

ggsave("one_year_survival.tiff", plot = p, device="tiff", dpi=300, width = 20, height = 7, units = "in")

adayim avatar Dec 18 '21 23:12 adayim

None of these work for me, the best I get is the last plot being saved , i.e. the KM plot if there is no risk table, or the risk table alone.

wiedenhoeft avatar Jan 13 '22 12:01 wiedenhoeft

This works for me: ggsave(file="survival.pdf", plot=ggarrange(g$plot, g$table, nrow=2, ncol=1, heights=c(3,1)))

But this would have to be manually adapted based on the number of subplots. Also, the x-axes become misaligned.

This function is a workaround

save_ggsurvplot <- function(filename, g, rel_heights=c(3,1,1,1)){ nrplots=length(g)-2 ggsave(file=filename, plot=ggarrange(plotlist=g[1:nrplots], nrow=nrplots, ncol=1, align="v", heights=rel_heights[1:nrplots])) }

wiedenhoeft avatar Jan 13 '22 12:01 wiedenhoeft

Maybe it could be possible to convert the res object evaluated in ggsurvplot::print.ggsurvplot to a proper ggplot object and return it.

In the meantime, the following behind-the-scenes workaround, seems working to me:

library("survival")
library("survminer")
#> Loading required package: ggplot2
#> Loading required package: ggpubr
#> 
#> Attaching package: 'survminer'
#> The following object is masked from 'package:survival':
#> 
#>     myeloma

fit <- survfit(Surv(time, status) ~ sex, data = lung)

With risk table

gg_risk <- ggsurvplot(fit, risk.table = TRUE)
risk_png <- tempfile("risk", fileext = ".png")
risk_png |>
  ggsave(
    plot = survminer:::.build_ggsurvplot(gg_risk)
  )
#> Saving 7 x 5 in image
if (!interactive() && requireNamespace("knitr")) {
  knitr::include_graphics(risk_png)
}

Without risk table

gg_norisk <- ggsurvplot(fit, risk.table = FALSE)
norisk_png <- tempfile("norisk", fileext = ".png")
norisk_png |>
  ggsave(
    plot = survminer:::.build_ggsurvplot(gg_norisk)
  )
#> Saving 7 x 5 in image
if (!interactive() && requireNamespace("knitr")) {
  knitr::include_graphics(norisk_png)
}

Created on 2022-03-11 by the reprex package (v2.0.1)

Session info
sessioninfo::session_info()
#> - Session info ---------------------------------------------------------------
#>  setting  value
#>  version  R version 4.1.3 (2022-03-10)
#>  os       Windows 10 x64 (build 22000)
#>  system   x86_64, mingw32
#>  ui       RTerm
#>  language (EN)
#>  collate  English_United States.1252
#>  ctype    English_United States.1252
#>  tz       Europe/Berlin
#>  date     2022-03-11
#>  pandoc   2.14.0.3 @ C:/Program Files/RStudio/bin/pandoc/ (via rmarkdown)
#> 
#> - Packages -------------------------------------------------------------------
#>  package     * version date (UTC) lib source
#>  abind         1.4-5   2016-07-21 [1] CRAN (R 4.1.1)
#>  assertthat    0.2.1   2019-03-21 [1] RSPM (R 4.1.0)
#>  backports     1.4.1   2021-12-13 [1] RSPM (R 4.1.0)
#>  broom         0.7.12  2022-01-28 [1] RSPM (R 4.1.0)
#>  car           3.0-12  2021-11-06 [1] CRAN (R 4.1.2)
#>  carData       3.0-5   2022-01-06 [1] RSPM (R 4.1.0)
#>  cli           3.2.0   2022-02-14 [1] RSPM (R 4.1.0)
#>  colorspace    2.0-3   2022-02-21 [1] CRAN (R 4.1.2)
#>  crayon        1.5.0   2022-02-14 [1] RSPM (R 4.1.0)
#>  curl          4.3.2   2021-06-23 [1] RSPM (R 4.1.0)
#>  data.table    1.14.2  2021-09-27 [1] CRAN (R 4.1.2)
#>  DBI           1.1.2   2021-12-20 [1] RSPM (R 4.1.0)
#>  digest        0.6.29  2021-12-01 [1] RSPM (R 4.1.0)
#>  dplyr         1.0.8   2022-02-08 [1] CRAN (R 4.1.2)
#>  ellipsis      0.3.2   2021-04-29 [1] RSPM (R 4.1.0)
#>  evaluate      0.15    2022-02-18 [1] RSPM (R 4.1.0)
#>  fansi         1.0.2   2022-01-14 [1] CRAN (R 4.1.2)
#>  farver        2.1.0   2021-02-28 [1] RSPM (R 4.1.0)
#>  fastmap       1.1.0   2021-01-25 [1] RSPM (R 4.1.0)
#>  fs            1.5.2   2021-12-08 [1] RSPM (R 4.1.0)
#>  generics      0.1.2   2022-01-31 [1] RSPM (R 4.1.0)
#>  ggplot2     * 3.3.5   2021-06-25 [1] RSPM (R 4.1.0)
#>  ggpubr      * 0.4.0   2020-06-27 [1] RSPM (R 4.1.0)
#>  ggsignif      0.6.3   2021-09-09 [1] RSPM (R 4.1.0)
#>  ggtext        0.1.1   2020-12-17 [1] RSPM (R 4.1.0)
#>  glue          1.6.2   2022-02-24 [1] CRAN (R 4.1.2)
#>  gridExtra     2.3     2017-09-09 [1] RSPM (R 4.1.0)
#>  gridtext      0.1.4   2020-12-10 [1] RSPM (R 4.1.0)
#>  gtable        0.3.0   2019-03-25 [1] RSPM (R 4.1.0)
#>  highr         0.9     2021-04-16 [1] RSPM (R 4.1.0)
#>  htmltools     0.5.2   2021-08-25 [1] CRAN (R 4.1.2)
#>  httr          1.4.2   2020-07-20 [1] RSPM (R 4.1.0)
#>  km.ci         0.5-2   2009-08-30 [1] RSPM (R 4.1.0)
#>  KMsurv        0.1-5   2012-12-03 [1] RSPM (R 4.1.0)
#>  knitr         1.37    2021-12-16 [1] RSPM (R 4.1.0)
#>  labeling      0.4.2   2020-10-20 [1] RSPM (R 4.1.0)
#>  lattice       0.20-45 2021-09-22 [2] CRAN (R 4.1.3)
#>  lifecycle     1.0.1   2021-09-24 [1] CRAN (R 4.1.2)
#>  magrittr      2.0.2   2022-01-26 [1] CRAN (R 4.1.2)
#>  markdown      1.1     2019-08-07 [1] RSPM (R 4.1.0)
#>  Matrix        1.4-0   2021-12-08 [2] CRAN (R 4.1.3)
#>  mime          0.12    2021-09-28 [1] CRAN (R 4.1.1)
#>  munsell       0.5.0   2018-06-12 [1] RSPM (R 4.1.0)
#>  pillar        1.7.0   2022-02-01 [1] RSPM (R 4.1.0)
#>  pkgconfig     2.0.3   2019-09-22 [1] RSPM (R 4.1.0)
#>  png           0.1-7   2013-12-03 [1] RSPM (R 4.1.0)
#>  purrr         0.3.4   2020-04-17 [1] RSPM (R 4.1.0)
#>  R.cache       0.15.0  2021-04-30 [1] CRAN (R 4.1.2)
#>  R.methodsS3   1.8.1   2020-08-26 [1] CRAN (R 4.1.1)
#>  R.oo          1.24.0  2020-08-26 [1] CRAN (R 4.1.1)
#>  R.utils       2.11.0  2021-09-26 [1] CRAN (R 4.1.2)
#>  R6            2.5.1   2021-08-19 [1] CRAN (R 4.1.2)
#>  ragg          1.2.2   2022-02-21 [1] RSPM (R 4.1.0)
#>  Rcpp          1.0.8   2022-01-13 [1] RSPM (R 4.1.0)
#>  reprex        2.0.1   2021-08-05 [1] CRAN (R 4.1.2)
#>  rlang         1.0.2   2022-03-04 [1] CRAN (R 4.1.2)
#>  rmarkdown     2.13    2022-03-10 [1] CRAN (R 4.1.3)
#>  rstatix       0.7.0   2021-02-13 [1] RSPM (R 4.1.0)
#>  rstudioapi    0.13    2020-11-12 [1] RSPM (R 4.1.0)
#>  scales        1.1.1   2020-05-11 [1] RSPM (R 4.1.0)
#>  sessioninfo   1.2.2   2021-12-06 [1] RSPM (R 4.1.0)
#>  stringi       1.7.6   2021-11-29 [1] CRAN (R 4.1.1)
#>  stringr       1.4.0   2019-02-10 [1] RSPM (R 4.1.0)
#>  styler        1.6.2   2021-09-23 [1] CRAN (R 4.1.2)
#>  survival    * 3.3-1   2022-03-03 [1] CRAN (R 4.1.3)
#>  survminer   * 0.4.9   2021-03-09 [1] RSPM (R 4.1.0)
#>  survMisc      0.5.5   2018-07-05 [1] RSPM (R 4.1.0)
#>  systemfonts   1.0.4   2022-02-11 [1] RSPM (R 4.1.0)
#>  textshaping   0.3.6   2021-10-13 [1] CRAN (R 4.1.2)
#>  tibble        3.1.6   2021-11-07 [1] CRAN (R 4.1.2)
#>  tidyr         1.2.0   2022-02-01 [1] RSPM (R 4.1.0)
#>  tidyselect    1.1.2   2022-02-21 [1] RSPM (R 4.1.0)
#>  utf8          1.2.2   2021-07-24 [1] CRAN (R 4.1.2)
#>  vctrs         0.3.8   2021-04-29 [1] RSPM (R 4.1.0)
#>  withr         2.5.0   2022-03-03 [1] RSPM (R 4.1.0)
#>  xfun          0.30    2022-03-02 [1] RSPM (R 4.1.0)
#>  xml2          1.3.3   2021-11-30 [1] RSPM (R 4.1.0)
#>  xtable        1.8-4   2019-04-21 [1] RSPM (R 4.1.0)
#>  yaml          2.3.5   2022-02-21 [1] RSPM (R 4.1.0)
#>  zoo           1.8-9   2021-03-09 [1] RSPM (R 4.1.0)
#> 
#>  [1] C:/Users/corra/Documents/R/win-library/4.1
#>  [2] C:/Program Files/R/R-4.1.3/library
#> 
#> ------------------------------------------------------------------------------

CorradoLanera avatar Mar 11 '22 09:03 CorradoLanera

Maybe it could be possible to convert the res object evaluated in ggsurvplot::print.ggsurvplot to a proper ggplot object and return it.

In the meantime, the following behind-the-scenes workaround, seems working to me:

library("survival")
library("survminer")
#> Loading required package: ggplot2
#> Loading required package: ggpubr
#> 
#> Attaching package: 'survminer'
#> The following object is masked from 'package:survival':
#> 
#>     myeloma

fit <- survfit(Surv(time, status) ~ sex, data = lung)

With risk table

gg_risk <- ggsurvplot(fit, risk.table = TRUE)
risk_png <- tempfile("risk", fileext = ".png")
risk_png |>
  ggsave(
    plot = survminer:::.build_ggsurvplot(gg_risk)
  )
#> Saving 7 x 5 in image
if (!interactive() && requireNamespace("knitr")) {
  knitr::include_graphics(risk_png)
}

Without risk table

gg_norisk <- ggsurvplot(fit, risk.table = FALSE)
norisk_png <- tempfile("norisk", fileext = ".png")
norisk_png |>
  ggsave(
    plot = survminer:::.build_ggsurvplot(gg_norisk)
  )
#> Saving 7 x 5 in image
if (!interactive() && requireNamespace("knitr")) {
  knitr::include_graphics(norisk_png)
}

Created on 2022-03-11 by the reprex package (v2.0.1)

Session info

Work for me, thanks!

HuoJnx avatar Mar 11 '22 10:03 HuoJnx

Both examples (from @adayim and @CorradoLanera) work for me (with argument risk.table=TRUE). It would be very nice, if one of these could be added to the exported functions for the next CRAN release.

kapsner avatar May 27 '22 10:05 kapsner