kassambara / survminer

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

How to facet ggsurvplot() output #64

Closed kassambara closed 8 years ago

kassambara commented 8 years ago

How to facet the output by strata or some combination of factors , for example sex ~ treatment, like this:

facetted plot of km event curves

kassambara commented 8 years ago

In the current version it's possible to facet by strata, as follow:

require("survival")
library("survminer")
fit2 <- survfit( Surv(time, status) ~ rx, data = colon )
ggsurv <- ggsurvplot(fit2, conf.int = TRUE)
# Facet
ggsurv$plot + theme_bw()+facet_wrap(~strata)

rplot

We'll work on it to make faceting possible with a combination of factors.

kassambara commented 8 years ago

To make faceting possible with a combination of factors, we'll update survminer as follow:

Add a new function surv_summary(): Compared to the default summary() function, surv_summary(), will create a data frame containing a nice summary from survfit results.

This (new) helper function will be used in ggsurvplot() to summarize survival curves.

In a situation, where survival curves have been fitted with one or more variables, surv_summary object contains extra columns representing the variables.

An example might be:

# Fit survival curves
require("survival")
fit <- survfit(Surv(time, status) ~ rx + adhere, data = colon)

# Summarize
res.sum <- surv_summary(fit)
head(res.sum)
 time n.risk n.event n.censor      surv     std.err     upper     lower           strata  rx adhere
1   20    536       1        0 0.9981343 0.001867414 1.0000000 0.9944878 rx=Obs, adhere=0 Obs      0
2   43    535       1        0 0.9962687 0.002643394 1.0000000 0.9911204 rx=Obs, adhere=0 Obs      0
3   45    534       1        0 0.9944030 0.003240519 1.0000000 0.9881072 rx=Obs, adhere=0 Obs      0
4   59    533       1        0 0.9925373 0.003745345 0.9998501 0.9852780 rx=Obs, adhere=0 Obs      0
5   72    532       1        0 0.9906716 0.004191364 0.9988435 0.9825667 rx=Obs, adhere=0 Obs      0
6   77    531       1        0 0.9888060 0.004595738 0.9977529 0.9799393 rx=Obs, adhere=0 Obs      0

Note that, supplementary columns (rx, adhere) have been added.

This makes it possible to facet the output of ggsurvplot by strata or by some combinations of factors.

An example might be:

# Facet by strata
ggsurv$plot + theme_bw()+facet_wrap(~strata)

# Facet by treatment rx
ggsurv$plot + theme_bw()+facet_wrap(~rx)
bmackenz commented 8 years ago

That sounds great Alboukadel! Thanks so much for your effort and generosity to implement this feature, further improving what is already a stellar package.

kassambara commented 8 years ago

Faceting should work now.

  1. Install the latest version of survminer
devtools::install_github("kassambara/survminer")
  1. Try this:
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Facet ggsurvplot() output by
# a combination of factors
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

# Fit (complexe) survival curves
#++++++++++++++++++++++++++++++++++++
require("survival")
fit3 <- survfit( Surv(time, status) ~ sex + rx + adhere,
                data = colon )

# Visualize: plot survival curves by sex and facet by rx and adhere
#++++++++++++++++++++++++++++++++++++
ggsurv <- ggsurvplot(fit3, fun = "cumhaz", conf.int = TRUE)
ggsurv$plot +theme_bw() + facet_grid(rx ~ adhere)

It looks like this:

rplot

Best, /A

MarcinKosinski commented 8 years ago

A magnificence masterpiece :O I'm amazed. Great that you are still working on survminer and providing such an informative messages during the issue's conversation.

MarcinKosinski commented 8 years ago

I am wondering how this can be displayed with risk tables? Do you think that 1 risk table under all plots is better than plotting as many risk tables as there are columns of graphs after faceting?

kassambara commented 8 years ago

Thank you Marcin, I appreciate your positive comment!!

I will work on that, it should be possible to suggest something about facet + risk tables!

kassambara commented 8 years ago

ggurvplot() returns a list of two different ggplots:

It's now possible to apply facet to the two components plot and table. I think that, it's not possible to combine items of two different facets.

In trying to answer to your question, survminer now makes it possible to apply faceting as follow.

If it's ok, then we can close this issue:-)! Best, /A

# Fit (complexe) survival curves
#++++++++++++++++++++++++++++++++++++
require("survival")
fit <- survfit( Surv(time, status) ~ sex + rx + adhere,
                data = colon )

# Visualize
#++++++++++++++++++++++++++++++++++++
require("survminer")
ggsurv <- ggsurvplot(fit3, fun = "cumhaz", conf.int = TRUE,
  risk.table = TRUE, risk.table.col="strata",
  ggtheme = theme_bw())
# Faceting survival curves
curv_facet <- ggsurv$plot + facet_grid(rx ~ adhere)
curv_facet

rplot08

# Faceting risk tables:
# Generate risk table for each facet plot item
ggsurv$table + facet_grid(rx ~ adhere, scales = "free")+
 theme(legend.position = "none")

rplot10

 # Generate risk table for each facet columns
tbl_facet <- ggsurv$table + facet_grid(.~ adhere, scales = "free")
tbl_facet + theme(legend.position = "none")

rplot09

# Arrange faceted survival curves and risk tables
g2 <- ggplotGrob(curv_facet)
g3 <- ggplotGrob(tbl_facet)
min_ncol <- min(ncol(g2), ncol(g3))
g <- gridExtra::rbind.gtable(g2[, 1:min_ncol], g3[, 1:min_ncol], size="last")
g$widths <- grid::unit.pmax(g2$widths, g3$widths)
grid::grid.newpage()
grid::grid.draw(g)

rplot11

kassambara commented 8 years ago

To add risk table in each facet item, we should implement an option in the future to place the risk table inside the Kaplan-Meier curve, so that risk table can propagate with facet items.

kassambara commented 8 years ago

I think that it's not a good idea to place the risk table inside the KM curve, the final plot is unreadable. So we can close this issue.

DanChaltiel commented 6 years ago

I'm trying to reproduce this example, but using the code

require("survival")
require("survminer")
fit3 <- survfit( Surv(time, status) ~ sex + rx + adhere, data = colon )
ggsurv <- ggsurvplot(fit3, fun = "cumhaz", conf.int = TRUE,
                     risk.table = TRUE, risk.table.col="strata",
                     ggtheme = theme_bw())
ggsurv$plot + facet_grid(rx ~ adhere)

throws this error:

Error in f(...) : Aesthetics can not vary with a ribbon

I get the same message when trying on my own dataset.

Am I doing it wrong ?

smouksassi commented 5 years ago

try using strata sometimes the extraction of strata into column names can fail. I also show how you can do something similar more flexible in geom_km ( it understands, grouping, color, fill etc) up to you to decide how to split the plot using ggplot synthax without a survfit object. ( no risk table and other advanced features) also up to you to ensure variables are factor or continous etc.

require("survival")
#> Loading required package: survival
require("survminer")
#> Loading required package: survminer
#> Loading required package: ggplot2
#> Loading required package: ggpubr
#> Loading required package: magrittr
require("ggquickeda")
#> Loading required package: ggquickeda
#> 
#> Attaching package: 'ggquickeda'
#> The following object is masked from 'package:base':
#> 
#>     +

fit3 <- survfit( Surv(time, status) ~ sex + rx + adhere, data = colon )
ggsurv <- ggsurvplot(fit3, fun = "cumhaz", conf.int = TRUE)$plot + facet_wrap(~ strata)

ggsurv

ggplot(colon, aes(time = time, status = status)) +
         geom_km()+
  geom_kmband()+
  geom_kmticks()+
  facet_grid(~ sex + rx + adhere)


ggplot(colon, aes(time = time, status = status,fill=sex,color=sex,group=sex)) +
  geom_km()+
  geom_kmband()+
  geom_kmticks()+
  facet_grid(rx ~ adhere)

Created on 2019-03-20 by the reprex package (v0.2.1)

RAGG3D commented 4 years ago

try using strata sometimes the extraction of strata into column names can fail. I also show how you can do something similar more flexible in geom_km ( it understands, grouping, color, fill etc) up to you to decide how to split the plot using ggplot synthax without a survfit object. ( no risk table and other advanced features) also up to you to ensure variables are factor or continous etc.

require("survival")
#> Loading required package: survival
require("survminer")
#> Loading required package: survminer
#> Loading required package: ggplot2
#> Loading required package: ggpubr
#> Loading required package: magrittr
require("ggquickeda")
#> Loading required package: ggquickeda
#> 
#> Attaching package: 'ggquickeda'
#> The following object is masked from 'package:base':
#> 
#>     +

fit3 <- survfit( Surv(time, status) ~ sex + rx + adhere, data = colon )
ggsurv <- ggsurvplot(fit3, fun = "cumhaz", conf.int = TRUE)$plot + facet_wrap(~ strata)

ggsurv

ggplot(colon, aes(time = time, status = status)) +
         geom_km()+
  geom_kmband()+
  geom_kmticks()+
  facet_grid(~ sex + rx + adhere)

ggplot(colon, aes(time = time, status = status,fill=sex,color=sex,group=sex)) +
  geom_km()+
  geom_kmband()+
  geom_kmticks()+
  facet_grid(rx ~ adhere)

Created on 2019-03-20 by the reprex package (v0.2.1)

OMG this is definitely what I want! ggsurvplot() is great but it's still not the real "facet". Both are great anyway, thanks!