Related to this previous post about changing the color of the figure legend text to match graphing colors in ggplot, I would like to extend this to ggsurvplot objects.
fit <- survfit(Surv(time, status) ~ sex, data = lung)
fitgraph <- ggsurvplot(fit,
risk.table = TRUE, risk.table.y.text.col = TRUE)
fitgraph
What I would really like to do is change the color of the text on the legend to match the color of the lines on the graph.
Using this previous solution from Z Cao I can convert the ggsurvplot$plot to a grob, change the legend text color, and then convert back into a ggplot object, which works fine...
g1 <- fitgraph
pGrob <- ggplotGrob(g1$plot)
g.b <- pGrob[["grobs"]][[which(pGrob$layout$name=="guide-box")]]
l <- g.b[[1]][[1]][["grobs"]]
# get grobs for legend symbols (extract color)
lg <- l[sapply(l, function(i) grepl("GRID.segments", i))]
clr <- mapply(FUN=function(x){x$gp$col},x=lg)
gb <- which(grepl("guide-box", pGrob$layout$name))
gb2 <- which(grepl("guides", pGrob$grobs[[gb]]$layout$name))
label_text <- which(grepl("label",pGrob$grobs[[gb]]$grobs[[gb2]]$layout$name))
pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text] <-
mapply(FUN = function(x, y) {x[["children"]][[1]][["children"]][[1]]$gp <- gpar(col =y); return(x)},
x = pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text],
y = clr, SIMPLIFY = FALSE)
grid.draw(pGrob)
Then convert this back into a ggplot object, with color of figure legend text changed to match the color of lines as desired...
plot1 <- as.ggplot(pGrob)
plot1
Where I am stuck is now integrating this with the original ggsurvplot risk table underneath, as in the first graph.
My pretty basic reaction was to simply replace the ggsurvplot$plot with the new ggplot object created after extracting the original plot to a grob and then back to a ggplot...
g1$plot <- plot1
However this does not work...
Error in
[.data.frame
(g$data[1], "colour") : undefined columns selected
Most likely due to a loss of the underlying data in the process above, storing only a 2x2 table...
plot1$data
x y
1 0 0
2 1 1
Compared to the original fitgraph$plot$data
which yields the entire data set (hundreds of rows, ongoing survival proportions) which presumably feeds the risk table. The other dumb strategy of plot1 + fitgraph$table
doesn't work either.
There must be a better strategy - any ideas? Thanks in advance!
EDIT Thanks to Stefan for the ggtext solution below, however each of my ggsurvplots has a different color scheme, and applying this method seems to over-ride these, e.g.
fit <- survfit(Surv(time, status) ~ sex, data = lung)
fitgraph <- ggsurvplot(fit,
risk.table = TRUE,
palette=c("#B79F00", "#619CFF"),
risk.table.col = "strata")
For a graph that looks like...
That looks good but when I then apply the method to change the legend color...
cols <- c("#B79F00", "#619CFF")
labels <- function(x, cols) {
glue::glue("<span style = 'color: {cols}'>{x}</span>")
}
fitgraph$plot <- fitgraph$plot +
scale_color_discrete(labels = ~labels(.x, cols)) +
theme(legend.text = element_markdown())
fitgraph
This results in a loss of the original graph colors...
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Any ideas? Thanks again...