kassambara / survminer

Survival Analysis and Visualization
https://rpkgs.datanovia.com/survminer/
507 stars 162 forks source link

Saving ggsurvplot() #152

Open kassambara opened 7 years ago

kassambara commented 7 years ago

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 commented 7 years ago

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.

VincentGuyader commented 7 years ago

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"

kassambara commented 7 years ago

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)
Phil-T commented 7 years ago

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?

kassambara commented 7 years ago

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 commented 7 years ago

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

jvanlunenburg commented 4 years ago

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

kassambara commented 4 years ago

no, let's open this issue

chunjie-sam-liu commented 3 years ago
ggsave(
  filename ='os-plot.pdf',
  plot = print(os.plot, newpage = FALSE),
  device = 'pdf',
  path = 'data/output',
  width = 8,
  height = 9
)

Worked for me.

DKBurnsBresMed commented 3 years ago

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!

sstandage commented 3 years ago

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?

adayim commented 3 years ago

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 commented 2 years ago

@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")
wiedenhoeft commented 2 years ago

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 commented 2 years ago

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])) }

CorradoLanera commented 2 years ago

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 ``` r 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 #> #> ------------------------------------------------------------------------------ ```
HuoJnx commented 2 years ago

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!

kapsner commented 2 years ago

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.