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

How to move the x-axis without cropping part of the risk table?
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
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

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With