Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

creating theme_survminer() #151

Closed
kassambara opened this issue Feb 19, 2017 · 22 comments
Closed

creating theme_survminer() #151

kassambara opened this issue Feb 19, 2017 · 22 comments

Comments

@kassambara
Copy link
Owner

@kassambara kassambara commented Feb 19, 2017

suggestions by @pbiecek :

many functions take graphical parameters like font.tickslab, font.x and so on. Maybe it will be cleaner to create theme_survminer() and pass these graphical parameters through the theme function?

The function theme_survminer() could be a simple wrapper of the function ggpubr::ggpar() [ http://www.sthda.com/english/rpkgs/ggpubr/reference/ggpar.html ], which can already handle many of these arguments: font.x, font.main, ....

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

I'll create this function

kassambara added a commit that referenced this issue Feb 19, 2017
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

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

# Change font size, style and color
#++++++++++++++++++++++++++++++++++++
# Change font size, style and color at the same time
# Use font.x = 14, to change only font size; or use
# font.x = "bold", to change only font face.

library(survminer)
ggsurvplot(
   fit, data = lung,
   main = "Survival curve",
   submain = "Based on Kaplan-Meier estimates",
   caption = "created with survminer",

   ggtheme = theme_survminer(
     font.main = c(16, "bold", "darkblue"),
     font.submain = c(15, "bold.italic", "purple"),
     font.caption = c(14, "plain", "orange"),
     font.x = c(14, "bold.italic", "red"),
     font.y = c(14, "bold.italic", "darkred"),
     font.tickslab = c(12, "plain", "darkgreen")
   )
 )

rplot

@pbiecek
Copy link
Contributor

@pbiecek pbiecek commented Feb 19, 2017

I thought about adding the theme with + operator (a bit more convinient).
Since ggsurvplot returns list of plots then right now one can extract the plot with [[1]]

ggsurvplot(
   fit, data = lung,
   main = "Survival curve",
   submain = "Based on Kaplan-Meier estimates",
   caption = "created with survminer")[[1]] +
theme_survminer(
     font.main = c(16, "bold", "darkblue"),
     font.submain = c(15, "bold.italic", "purple"),
     font.caption = c(14, "plain", "orange"),
     font.x = c(14, "bold.italic", "red"),
     font.y = c(14, "bold.italic", "darkred"),
     font.tickslab = c(12, "plain", "darkgreen")
   )

but you may consider adding an overloaded + operator for object of the class ggsurvplot
which will add RHS to every element of ggsurvplot list.

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

Suggestion to simplify ggsurvplot() function:

-Removing the following graphical arguments (without breaking users old scripts ):

font.main = c(16, "plain", "black"), 
font.submain = c(15, "plain", "black"),
font.caption = c(15, "plain", "black"),
font.x = c(14, "plain", "black"), font.y = c(14, "plain", "black"),
font.tickslab = c(12, "plain", "black"),

font.risk.table.title = c(16, "plain", "black"), 
font.risk.table.subtitle = c(15, "plain", "black"),
font.risk.table.caption = c(15, "plain", "black"),
font.risk.table.x = c(14, "plain", "black"), font.risk.table.y =  c(14, "plain", "black"),
font.risk.table.tickslab = c(12, "plain", "black"),

font.ncensor.plot.title = c(16, "plain", "black"), 
font.ncensor.plot.subtitle = c(15, "plain", "black"),
font.ncensor.plot.caption = c(15, "plain", "black"),
font.ncensor.plot.x = c(14, "plain", "black"), 
font.ncensor.plot.y = c(14, "plain", "black"),
font.ncensor.plot.tickslab = c(12, "plain", "black")

These arguments can be replaced by ... argument, which can be passed to the function ggpubr::ggpar().

The function ggpar() can be used to customize the graphical parameters of one or multiple ggplots at once.

For example, users can customize ggsurvplots as follow.

  1. To change the appearance of survival curves, risk.table and ncensor.plot at once:
# Customizing during plot creation
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Use this
ggsurv <- ggsurvplot(fit, 
      ggtheme = theme_survminer(font.main, font.submain, ....))

# Or this (which doesn't break user old scripts. Arguments are passed to ggpar)
ggsurv <- ggsurvplot(fit,
        font.main, font.submain, font.x, ....)

# Customizing after plot creation
#%%%%%%%%%%%%%%%%%%%
ggsurv <- ggsurvplot(fit)
ggpubr::ggpar(ggsurv, font.main, font.submain, font.caption, ...)
  1. To change a specific component of ggsurvplot (e.g.: risk table), one can use this
ggsurv <- ggsurvplot(fit, risk.table = TRUE)
ggsurv$table <- ggpubr::ggpar(ggsurv$table, font.main, font.submain, font.caption, ...)
print(ggsurv)

Regarding + operator for object of the class ggsurvplot, I tried the following code without success:

#' @rdname theme_survminer
#' @method + ggsurvplot
#' @export
"+.ggsurvplot" <- function (e1, e2)
{
  e2name <- deparse(substitute(e2))
  if(inherits(e1, "ggsurvplot")){
    e1$plot <- ggplot2:::add_ggplot(e1$plot, e2, e2name)
    if(!is.null(e1$table)) e1$table <- ggplot2:::add_ggplot(e1$table, e2, e2name)
  }
  e1
}

The above code is inspired from the one used by hadley in ggplot2:

"+.gg" <- function (e1, e2) 
{
    e2name <- deparse(substitute(e2))
    if (is.theme(e1)) 
        add_theme(e1, e2, e2name)
    else if (is.ggplot(e1)) 
        add_ggplot(e1, e2, e2name)
}
kassambara added a commit that referenced this issue Feb 19, 2017
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

Should work now, with %+% operator

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

# Basicsurvival curves
require("survminer")
p <- ggsurvplot(fit, data = lung, risk.table = TRUE,
   main = "Survival curve",
   submain = "Based on Kaplan-Meier estimates",
   caption = "created with survminer")
p

# Customizing the plots
p %+% theme_survminer(
     font.main = c(16, "bold", "darkblue"),
     font.submain = c(15, "bold.italic", "purple"),
     font.caption = c(14, "plain", "orange"),
     font.x = c(14, "bold.italic", "red"),
     font.y = c(14, "bold.italic", "darkred"),
     font.tickslab = c(12, "plain", "darkgreen")
)

rplot

kassambara added a commit that referenced this issue Feb 19, 2017
@MarcinKosinski
Copy link
Contributor

@MarcinKosinski MarcinKosinski commented Feb 19, 2017

Outstanding job! Do you think this will work with the rest of survminer's functions or only with ggsurvplot()?
I can have a look at your change to apply this to the ggcox.. functions

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

I will update the %+% operator to handle the outputs of the other survminer functions. I will also update ggsurvplot to reduce the number of graphical arguments...

@pbiecek
Copy link
Contributor

@pbiecek pbiecek commented Feb 19, 2017

Regarding the problem with + operator
In addition to function definition the new operator shall be also registered in NAMESPACE as
S3method("+",ggsurvplot)
(as in https://github.com/tidyverse/ggplot2/blob/0d402da4a9338b9eab7caa3f99d1addd30ea5707/NAMESPACE)

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

The warning message I obtained using the + operator is

Warning message:
Incompatible methods ("+.ggsurvplot", "+.gg") for "+" 

But, using %+% works.

kassambara added a commit that referenced this issue Feb 19, 2017
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

Now the %+% operator works with one ggplot or any list of ggplots

library(survival)
fit <- coxph(Surv(futime, fustat) ~ age + ecog.ps + rx, data=ovarian)
cox.zph.fit <- cox.zph(fit)
# plot all variables
p <- ggcoxzph(cox.zph.fit)

p %+% theme_survminer(
    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")
  )

rplot01

kassambara added a commit that referenced this issue Feb 19, 2017
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

the + operator seems to work with labs() but not with theme()

p1+labs(y = "yyyyyyyyyyy", x ="xxxxxx", caption = "survvvvvvv")

rplot02

@pbiecek
Copy link
Contributor

@pbiecek pbiecek commented Feb 19, 2017

The problem with + operator was discussed here: http://r.789695.n4.nabble.com/quot-Incompatible-methods-quot-for-overloaded-operator-td4633362.html

So for A + B only two solutions will work. Either there is only one overloaded + (+.A or +.B) and it will be used, or there are two methods +.A and +.B but they point into the same object.

It is satisfied for ggsurv()+labs() since labs() returns an object of the class labels and there is no +.labels
But ggsurv() + theme() is equivalent to gglist + (theme, gg), there is no +.theme but there are different +.gglist and +.gg.

The simplest solution is to remove the class gg from the classes returned by theme_survminer().

pbiecek added a commit to pbiecek/survminer that referenced this issue Feb 19, 2017
kassambara added a commit that referenced this issue Feb 19, 2017
kassambara added a commit that referenced this issue Feb 19, 2017
Candidate fixes for #151
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

Oh, thank you!!!!!!

@pbiecek
Copy link
Contributor

@pbiecek pbiecek commented Feb 19, 2017

I see that you have now +.gglist, but the ggsurv() returns object of the class ggsurvplot, this needs to be somehow simplified ;-)

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

As ggsurvplot(), ggcoxzph() and ggcoxfunctional()return a list of ggplots, we need to define a common class for the 3 functions...

This could be either gglist , ggsurv or survminer,....

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 19, 2017

It works like a charm now

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 20, 2017

I'll update the 3 functions to add the class ggsurv

@pbiecek
Copy link
Contributor

@pbiecek pbiecek commented Feb 20, 2017

+1
in future the gglist may collide with some other package, that creates lists of ggplots
ggsurv is short and unique

kassambara added a commit that referenced this issue Feb 20, 2017
kassambara added a commit that referenced this issue Feb 20, 2017
@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 23, 2017

The graphical parameters - font.main, font.x, ... -, which can be passed directly to the ggsurvplot() function are no longer documented in the ggsurvplot.R file...

ggsurvplot(
  fit,                     # survfit object with calculated statistics.
  data = lung,             # data used to fit survival curves.
  risk.table = TRUE,       # show risk table.
  pval = TRUE,             # show p-value of log-rank test.
  conf.int = TRUE,         # show confidence intervals for 
  # point estimates of survival curves.
  xlim = c(0,500),         # present narrower X axis, but not affect
  # survival estimates.
  xlab = "Time in days",   # customize X axis label.
  break.time.by = 100,     # break X axis in time intervals by 500.
  ggtheme = theme_light(), # customize plot and risk table with a theme.
  risk.table.y.text.col = T,# colour risk table text annotations.
  risk.table.y.text = FALSE,# show bars instead of names in text annotations
  # in legend of risk table.
  ncensor.plot = TRUE,      # plot the number of censored subjects at time t
  conf.int.style = "step",  # customize style of confidence intervals
  surv.median.line = "hv",  # add the median survival pointer.
  legend.labs = 
    c("Male", "Female"),    # change legend labels.
  palette = 
    c("#E7B800", "#2E9FDF"),# custom color palettes.
  main = "Survival curves",                       # specify the title of the plot
  submain = "Based on Kaplan-Meier estimates",    # the subtitle of the plot 
  caption = "created with survminer",             # the caption of the plot
  font.main = c(16, "bold", "darkblue"),          # font for titles of the plot, the table and censor part
  font.submain = c(15, "bold.italic", "purple"),  # font for subtitles in the plot, the table and censor part
  font.caption = c(14, "plain", "orange"),        # font for captions in the plot, the table and censor part
  font.x = c(14, "bold.italic", "red"),           # font for x axises in the plot, the table and censor part
  font.y = c(14, "bold.italic", "darkred"),       # font for y axises in the plot, the table and censor part
  font.tickslab = c(12, "plain", "darkgreen"),    # font for ticklabs in the plot, the table and censor part
  ########## risk table #########,
  risk.table.title = "Note the risk set sizes",          # the title of the risk table
  risk.table.subtitle = "and remember about censoring.", # the subtitle of the risk table
  risk.table.caption = "source code: website.com",       # the caption of the risk table
  risk.table.height = 0.35,                              # the height of the risk table
  ########## ncensor plot ######
  ncensor.plot.title = "Number of censorings",           # as above but for the censoring plot
  ncensor.plot.subtitle = "over the time.",
  ncensor.plot.caption = "data available at data.com",
  ncensor.plot.height = 0.35
)

Now, I think that it would be better to update the README and vignette files...

@MarcinKosinski
Copy link
Contributor

@MarcinKosinski MarcinKosinski commented Feb 23, 2017

going to far with survival plots .... : )

@kassambara
Copy link
Owner Author

@kassambara kassambara commented Feb 23, 2017

Suggestion:

  • ggplot2::labs() for changing the title of survival curves, risk table and ncensor plots.
  • ggpubr::ggpar() for changing the graphical parameters of one or list of ggplots

Examples:

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

# Drawing survival curves
require("survminer")
ggsurv <- ggsurvplot(fit, data = lung, risk.table = TRUE)

# Uber platinum customized survival curves
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Survival curves
ggsurv$plot <- ggsurv$plot + labs(
  title = "Survival curves",                     # specify the title of the plot
  subtitle = "Based on Kaplan-Meier estimates",  # the subtitle of the plot 
  caption = "created with survminer"             # the caption of the plot
  )

#risk table 
ggsurv$table <- ggsurv$table + labs(
  title = "Note the risk set sizes",          # the title of the risk table
  subtitle = "and remember about censoring.", # the subtitle of the risk table
  caption = "source code: website.com"        # the caption of the risk table
  )

# ncensor plot 
ggsurv$ncensor.plot <- ggsurv$ncensor.plot + labs( 
  title = "Number of censorings", 
  subtitle = "over the time.",
  caption = "source code: website.com"
  )

# use ggpar to change the font of one or a list of ggplots at once
ggpubr::ggpar(ggsurv,
      font.title = c(16, "bold", "darkblue"),          # font for titles of the plot, the table and censor part
      font.subtitle = c(15, "bold.italic", "purple"),  # font for subtitles in the plot, the table and censor part
      font.caption = c(14, "plain", "orange"),        # font for captions in the plot, the table and censor part
      font.x = c(14, "bold.italic", "red"),           # font for x axises in the plot, the table and censor part
      font.y = c(14, "bold.italic", "darkred"),       # font for y axises in the plot, the table and censor part
      font.tickslab = c(12, "plain", "darkgreen"),    # font for ticklabs in the plot, the table and censor part
      legend = "top"
      )
kassambara added a commit that referenced this issue Feb 25, 2017
kassambara added a commit that referenced this issue Feb 25, 2017
kassambara added a commit that referenced this issue Feb 26, 2017
@kassambara kassambara closed this Feb 27, 2017
@kassambara kassambara reopened this Feb 27, 2017
@kassambara kassambara closed this Feb 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.