user122514
user122514

Reputation: 407

Specifying custom time points for survival plot

I'm working on creating a survival/cumulative event plot using the ggsurvplot function from the survminer package. I want to specify custom time points for my plot, but I cannot figure out how to do so. The xlim and break.x.by parameters kind of help, but they create evenly spaced time points and more time points than I want. Specifically, I only want time points at 0, 30, and 365 days. Is it possible to do this using the survminer package? If it's possible to almost replicate the output from ggsurvplot using a different package, that would work too - I really want to have information on number at risk and cumulative number of events. Thanks so much!

Sample data:

data <- structure(list(status = c(1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 
1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 
0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 
0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 
0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 
0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 
0, 0, 0, 0, 0, 0), time_to_trt = c(48, 202, 83, 235, 195, 158, 
81, 251, 237, 408, 794, 82, 734, 697, 208, 385, 96, 384, 126, 
277, 102, 95, 651, 475, 522, 119, 51, 248, 202, 76, 606, 129, 
675, 399, 26, 969, 76, 491, 91, 68, 261, 185, 164, 395, 93, 176, 
389, 84, 67, 245, 299, 500, 487, 347, 325, 125, 102, 62, 403, 
496, 298, 264, 249, 167, 146, 67, 311, 175, 283, 654, 599, 371, 
172, 367, 151, 277, 234, 81, 54, 419, 405, 244, 152, 948, 332, 
174, 451, 347, 872, 436, 738, 699, 449, 578, 41, 517, 332, 56, 
214, 214, 172, 48, 343, 23, 157, 874, 668, 375, 201, 55, 633, 
112, 43, 245, 23, 720, 60, 405, 799, 173, 446, 363, 333, 393, 
335, 294, 46, 48, 776, 237, 435, 132, 216, 58, 266, 50, 333, 
171, 816, 844, 201, 135, 24, 833, 398, 354, 269, 573, 51, 391, 
48, 713, 206, 148, 538, 52, 294, 139, 647, 655, 8, 25, 486, 458, 
370, 314, 224, 841, 933, 191, 601, 648, 755, 727, 717, 194, 684, 
37, 577, 580, 580, 399, 462, 378, 148, 71, 273, 265, 190, 160, 
46, 58, 323, 370, 181, 377, 291, 254, 535, 113, 129, 440, 202, 
523, 155, 556, 284, 266, 945, 281, 914, 293, 175, 805, 78, 327, 
82, 77, 606, 376, 292, 168, 110, 124, 98, 170, 83, 25, 18, 54, 
26, 561, 106, 45, 528, 41, 341, 259, 102, 277, 591, 256, 165, 
354, 53, 356, 391, 221, 127, 444, 69, 188, 377, 54, 874, 851, 
252, 42, 762, 76, 54, 96, 315, 347, 313, 109, 74, 231, 283, 223, 
237, 194, 172, 321, 217, 384, 486, 466, 111, 105, 378, 140, 129, 
207, 110, 518, 60, 278, 252, 363, 213, 356, 167, 45, 438, 308, 
95, 374, 363, 252, 46, 172, 139, 52, 92, 34, 158, 87, 87, 207, 
95, 175), trtsdt = structure(c(17605, 17885, 18074, 18065, 18025, 
NA, NA, 18156, NA, 17906, NA, 17688, NA, NA, 17883, 18135, 17932, 
NA, 18191, NA, NA, NA, NA, 18281, NA, 18106, 18171, NA, NA, NA, 
NA, 18169, 18225, 18058, 18171, NA, 17702, 18164, 17780, 17814, 
18102, NA, NA, 18085, 17963, 18326, 17820, 17634, 17687, 17834, 
17974, NA, NA, 18282, NA, NA, NA, NA, 18282, NA, NA, NA, NA, 
NA, NA, NA, 18103, 17800, 17935, NA, NA, 18291, 18128, NA, 18246, 
NA, NA, NA, NA, NA, NA, NA, 17744, NA, 17819, 17826, NA, NA, 
NA, 18003, NA, NA, 18131, NA, 17842, NA, NA, 18187, NA, NA, NA, 
NA, 18116, 17871, NA, NA, NA, NA, 18248, NA, 18018, 17603, 17617, 
17927, 17626, NA, 17940, NA, 18220, 17624, 17989, 18038, 18058, 
18184, NA, NA, NA, NA, NA, 17856, 18304, 18045, NA, 17562, 17910, 
17779, NA, 18351, NA, NA, 18247, 17598, 17507, NA, 18351, NA, 
NA, 18071, 17619, NA, NA, NA, 18001, 18227, 18274, 17980, NA, 
NA, 18225, NA, 17757, 17854, NA, NA, NA, NA, 18303, 18200, NA, 
17745, 18172, 18233, NA, NA, NA, 17883, NA, 17771, NA, NA, NA, 
18284, NA, NA, 18137, 18156, NA, NA, NA, NA, NA, 17506, 18066, 
NA, 18311, NA, NA, 18059, NA, 18122, NA, NA, NA, 18010, 17723, 
NA, NA, 17682, NA, 17736, NA, 17827, 17729, NA, 17729, 18009, 
17834, 17841, NA, 18198, 18205, 18109, 18114, 18198, 18219, 18319, 
NA, 18317, 17576, 17625, 17681, 18199, 17879, 17914, NA, 17983, 
18332, NA, 17673, 18037, NA, 18065, 18121, NA, 18121, 18088, 
NA, 17652, 17638, NA, 17660, 18080, NA, NA, NA, NA, 17806, 17652, 
NA, 17759, 17869, 17925, 18233, NA, NA, NA, NA, 18305, NA, NA, 
NA, NA, NA, 18060, 18025, 18235, NA, NA, 18200, NA, NA, 18109, 
18249, NA, 17932, NA, 18121, NA, NA, NA, NA, NA, NA, NA, NA, 
18226, NA, NA, NA, 18296, 18093, NA, NA, NA, 18199, 18241, NA, 
NA, NA, NA, NA, NA), label = "Index Procedure Date", class = "Date", format.sas = "DATE"), 
    randdt = structure(c(17557, 17683, 17991, 17830, 17830, 18214, 
    18291, 17905, 18135, 17498, 17578, 17606, 17638, 17675, 17675, 
    17750, 17836, 17988, 18065, 18095, 18270, 18277, 17721, 17806, 
    17850, 17987, 18120, 18124, 18170, 18296, 17766, 18040, 17550, 
    17659, 18145, 17403, 17626, 17673, 17689, 17746, 17841, 18187, 
    18208, 17690, 17870, 18150, 17431, 17550, 17620, 17589, 17675, 
    17872, 17885, 17935, 18047, 18247, 18270, 18310, 17879, 17876, 
    18074, 18108, 18123, 18205, 18226, 18305, 17792, 17625, 17652, 
    17718, 17773, 17920, 17956, 18005, 18095, 18095, 18138, 18291, 
    18318, 17953, 17967, 18128, 17592, 17424, 17487, 17652, 17921, 
    18025, 17500, 17567, 17634, 17673, 17682, 17794, 17801, 17855, 
    18040, 18131, 18158, 18158, 18200, 18324, 17773, 17848, 18215, 
    17498, 17704, 17997, 18047, 18317, 17385, 17491, 17574, 17682, 
    17603, 17652, 17880, 17967, 17421, 17451, 17543, 17675, 17725, 
    17791, 18037, 18078, 18326, 18324, 17596, 17619, 17869, 17913, 
    18156, 17504, 17644, 17729, 18039, 18180, 17556, 17528, 18046, 
    17463, 17483, 17539, 17953, 18018, 18103, 17498, 17568, 17981, 
    18324, 17659, 17795, 18079, 17736, 17928, 18078, 18233, 17578, 
    17717, 17749, 17829, 17886, 17914, 18002, 18058, 18079, 17359, 
    17439, 17554, 17571, 17585, 17617, 17645, 17655, 17689, 17688, 
    17734, 17795, 17792, 17792, 17885, 17910, 17994, 17989, 18085, 
    18099, 18107, 18182, 18212, 18326, 17448, 17743, 18002, 18130, 
    17995, 18081, 17805, 17837, 18009, 18243, 17932, 18170, 17487, 
    17568, 17816, 18088, 17416, 17427, 17455, 17458, 17534, 17554, 
    17567, 17651, 17682, 17752, 17764, 17766, 17822, 17913, 17941, 
    18004, 18074, 18121, 18149, 18289, 18292, 17558, 17571, 17655, 
    17638, 17773, 17869, 17844, 17942, 17991, 18113, 17571, 17760, 
    17781, 17809, 17956, 18018, 18068, 17732, 17981, 17431, 17511, 
    17928, 17591, 17892, 17995, 18318, 17498, 17521, 17554, 17610, 
    17610, 17683, 17815, 17829, 17918, 18025, 18059, 18263, 18298, 
    18074, 18089, 18149, 18135, 18178, 18200, 17739, 17808, 17851, 
    17886, 17906, 18089, 18267, 17994, 17969, 18120, 18165, 17822, 
    17854, 18061, 18094, 18120, 18009, 18159, 18016, 18205, 18327, 
    17934, 17918, 18277, 17998, 18009, 18044, 18047, 18200, 18233, 
    18320, 18107, 18207, 18214, 18285, 18285, 18165, 18277, 18197
    ), label = "Randomization Date", class = "Date", format.sas = "DATE")), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -312L))

Fit:

fit <- survfit(Surv(time_to_trt, status) ~ 1, data = data)

"Bad" attempt using ggsurvplot:

a <- ggsurvplot(fit, 
           data = data, 
           conf.int = FALSE,
           risk.table = TRUE,
           cumevents = TRUE,
           fun = "event",
           ggtheme = theme_minimal(),
           risk.table.y.text = FALSE,
           risk.table.y.text.col = TRUE,
           legend = "none",
           legend.title = "")

Output: enter image description here

Upvotes: 3

Views: 3344

Answers (1)

astrofunkswag
astrofunkswag

Reputation: 2698

Here's one workaround solution. It's a bit of a Frankenstein monster but I think it shows nicely what is happening behind the scenes in the ggsurvplot function. Please let me know if this wasn't what you had in mind. I might recommend that you use the principles from this answer to create your plot directly using just ggplot functions, but I'll leave that up to you

The ggsurvplot returns 3 ggplot objects as part of a list: the scatter and 2 tables. You can extract those objects, edit them directly, and put them back in the list. First, I generate the plot with the xlim you specify and with break.x.by = 30

a <- ggsurvplot(fit, 
                data = data, 
                conf.int = FALSE,
                risk.table = TRUE,
                cumevents = TRUE,
                fun = "event",
                ggtheme = theme_minimal(),
                risk.table.y.text = FALSE,
                risk.table.y.text.col = TRUE,
                legend = "none",
                legend.title = "",
                xlim = c(0, 360),
                break.x.by = 30)
a

enter image description here

Then I manually edit each of the ggplot objects to set the x-axis breaks. The two tables are generated with geom_text. If you want to remove the labels that don't correspond to the ticks, the easiest way for me was to clear the labels, then manually recreate them with the right x values.

# extract ggplot object from ggsurvplot
p <- a$plot 
p <- p + scale_x_continuous(breaks = c(0, 30, 360))

# extract table object from ggsurvplot
tab <- a$table
tab$layers = NULL # clear labels
tab <- tab + 
  geom_text(aes(x = time, y = rev(strata), label = llabels), data = tab$data[tab$data$time %in% c(0, 30, 360),]) +
  scale_x_continuous(breaks = c(0, 30, 360))

# extract cumevents object from ggsurvplot
tab2 <- a$cumevents
tab2$layers = NULL # clear labels
tab2 <- tab2 + 
  geom_text(aes(x = time, y = rev(strata), label = cum.n.event), data = tab$data[tab$data$time %in% c(0, 30, 360),]) +
  scale_x_continuous(breaks = c(0, 30, 360))


# Add plots back
a$plot <- p
a$table <- tab
a$cumevents <- tab2

a

enter image description here

Upvotes: 3

Related Questions