Shifting the x axis with the ggsurvfit package without cutting the risk table

157 views Asked by At

When I shift the x-axis of the ggplot using scale_x_continuous, the values in the first column of the risk table are partially covered.

library(tidyverse)
library(ggsurvfit)

p <- 
  survfit2(Surv(time, status) ~ sex, data = df_lung) %>%
  ggsurvfit(linewidth = 1) + 
  scale_ggsurvfit() + 
  add_risktable(
    risktable_stats = c("n.risk")) +
  theme_classic()+
  scale_x_continuous(expand=c(0, 1), limits = c(0, 34), breaks = seq(0, 30, 6))


p

enter image description here

How to move the x-axis without cropping part of the risk table?

3

There are 3 answers

0
jay.sf On

Using base R, you could use the original survival package, aggregate the survival table by strata from the fit and add it as text. The plot will come with specific axTicks(1) whih we will use for summary and x-positions. Using xpd=TRUE allows using text outside plotting region.

> library(survival)
> fit <- survfit(Surv(time, status) ~ sex, data=transform(lung, sex=factor(sex, labels=c('Male', 'Female'))))
> par(mar=c(8, 4, 1, 1)+.1, xpd=TRUE)
> plot(fit, col=c(2, 4))
> (tb <- with(summary(fit, time=axTicks(1)), aggregate(n.risk ~ strata, FUN=I)$n.risk) |>
+     sapply(`length<-`, length(axTicks(1))) |> apply(2, \(x) replace(x, is.na(x), 0)) |>
+     `colnames<-`(names(fit$strata)) |> round(2))
     sex=Male sex=Female
[1,]      138         90
[2,]       78         66
[3,]       31         26
[4,]       13         11
[5,]        6          2
[6,]        2          0
> legend('topright', legend=names(fit$strata), lwd=1, col=c(2, 4))
> text(-10, -.4, labels='At Risk', adj=1, cex=.9)
> Map(text, x=list(axTicks(1)), y=c(-0.55, -0.65), labels=asplit(tb, 2), cex=.8)
[[1]]
NULL

[[2]]
NULL

> Map(text, x=-150, y=c(-0.55, -0.65), labels=gsub('sex=', '', names(fit$strata)), 
+     adj=0, cex=.8)
[[1]]
NULL

[[2]]
NULL

enter image description here

0
jared_mamrot On

One potential option is change the horizontal justification:

library(tidyverse)
library(ggsurvfit)

p <- 
  survfit2(Surv(time, status) ~ sex, data = df_lung) %>%
  ggsurvfit(linewidth = 1) + 
  scale_ggsurvfit() + 
  theme_classic()+
  scale_x_continuous(expand = c(0,0), limits = c(0, 34), breaks = seq(0, 30, 6)) +
  add_risktable(
    risktable_stats = c("n.risk")
  )
#> Scale for x is already present.
#> Adding another scale for x, which will replace the existing scale.
p

# with hjust = 1, instead of the default 0.5
p2 <- 
  survfit2(Surv(time, status) ~ sex, data = df_lung) %>%
  ggsurvfit(linewidth = 1) + 
  scale_ggsurvfit() + 
  theme_classic()+
  scale_x_continuous(expand = c(0, 0), limits = c(0, 34), breaks = seq(0, 30, 6)) +
  add_risktable(
    risktable_stats = c("n.risk"),
    hjust = 0
    )
#> Scale for x is already present.
#> Adding another scale for x, which will replace the existing scale.
p2

Created on 2024-01-16 with reprex v2.0.2

The numbers don't really line up though. Maybe if you adjusted the x axis text?

p3 <- 
  survfit2(Surv(time, status) ~ sex, data = df_lung) %>%
  ggsurvfit(linewidth = 1) + 
  scale_ggsurvfit() + 
  theme_classic()+
  scale_x_continuous(expand = c(0, 0), limits = c(0, 34), breaks = seq(0, 30, 6)) +
  theme(axis.text.x = element_text(hjust = 0)) +
  add_risktable(
    risktable_stats = c("n.risk"),
    hjust = 0
    )
#> Scale for x is already present.
#> Adding another scale for x, which will replace the existing scale.
p3

Created on 2024-01-16 with reprex v2.0.2

Not sure if this is what you're after, but, if not, it's probably best to go with @jay.sf's 'fully customisable' approach

0
Kamil On

Finally, I saved the ggplot in *.EPS format as curves and edited it in a graphics program, moving the y-axis and y-axis labels to position 0, achieving the intended effect.

enter image description here