Skip to content

Add get_surv_times_df and get_surv_diff_df()/ overhaul creation of survival times tables #269

Open
jszczypinski wants to merge 18 commits into
mainfrom
268_add_get_surv_times_df
Open

Add get_surv_times_df and get_surv_diff_df()/ overhaul creation of survival times tables #269
jszczypinski wants to merge 18 commits into
mainfrom
268_add_get_surv_times_df

Conversation

@jszczypinski

@jszczypinski jszczypinski commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

This PR overhauls the survival time summary pipeline, shifting from a tightly coupled architecture to a modular two-step process (Data Extraction ➔ Presentation). This mirrors tbl_coxph() and empowers users to manipulate summary contents via standard dplyr wrangling before rendering tables or plotting annotations.

🚀 New Extraction Engine

get_surv_times_df(): A new function to extract survival metrics, numbers at risk, and CIs into a highly malleable data.frame.

get_surv_diff_df(): Added a companion extractor leveraging cardx to calculate pairwise survival differences, complete with CIs and p-values.

🎨 Presentation & Rendering

tbl_survfit_times(): Refactored to process structured data frames rather than raw survfit models. Automatically maps dynamically bound columns (like the p-value output from get_surv_diff_df()).

annotate_surv_med(): Updated the function signature to accept a pre-computed data frame (surv_tbl), acting as a generic floating table renderer identical to annotate_coxph().

🛑 Legacy Migration & Fixes

Defunct Methods: Made add_overall() and add_difference_row() defunct for tbl_survfit_times. They now throw explicit errors guiding users to extract data and use the dplyr::bind_rows() workflow instead.

Strict Type Safety: Resolved vctrs/bind_rows() crashes by ensuring empty columns map correctly to NA_real_ across extractors.

Documentation Clean-up: Fixed Roxygen2 parameter mismatches and rewrote @examples to demonstrate advanced configuration scenarios.

Closes #268


Pre-review Checklist (if item does not apply, mark is as complete)

  • All GitHub Action workflows pass with a ✅
  • PR branch has pulled the most recent updates from master branch: usethis::pr_merge_main()
  • If a bug was fixed, a unit test was added.
  • Code coverage is suitable for any new functions/features (generally, 100% coverage for new code): devtools::test_coverage()
  • Request a reviewer

Reviewer Checklist (if item does not apply, mark is as complete)

  • If a bug was fixed, a unit test was added.
  • Run pkgdown::build_site(). Check the R console for errors, and review the rendered website.
  • Code coverage is suitable for any new functions/features: devtools::test_coverage()

When the branch is ready to be merged:

  • Update NEWS.md with the changes from this pull request under the heading "# cards (development version)". If there is an issue associated with the pull request, reference it in parentheses at the end update (see NEWS.md for examples).
  • All GitHub Action workflows pass with a ✅
  • Approve Pull Request
  • Merge the PR. Please use "Squash and merge" or "Rebase and merge".

jszczypinski and others added 13 commits June 15, 2026 16:23
**What changes are proposed in this pull request?**
* `tbl_survfit_quantiles()` now indicates censored observation in
min/max values with `*` (#192)

Small change as requested in the issue below:
Censored min/max range values are now marked with `*`

Closes #192 



--------------------------------------------------------------------------------

Pre-review Checklist (if item does not apply, mark is as complete)
- [ ] **All** GitHub Action workflows pass with a ✅
- [ ] PR branch has pulled the most recent updates from master branch:
`usethis::pr_merge_main()`
- [ ] If a bug was fixed, a unit test was added.
- [ ] Code coverage is suitable for any new functions/features
(generally, 100% coverage for new code): `devtools::test_coverage()`
- [ ] Request a reviewer

Reviewer Checklist (if item does not apply, mark is as complete)

- [ ] If a bug was fixed, a unit test was added.
- [ ] Run `pkgdown::build_site()`. Check the R console for errors, and
review the rendered website.
- [ ] Code coverage is suitable for any new functions/features:
`devtools::test_coverage()`

When the branch is ready to be merged:
- [ ] Update `NEWS.md` with the changes from this pull request under the
heading "`# cards (development version)`". If there is an issue
associated with the pull request, reference it in parentheses at the end
update (see `NEWS.md` for examples).
- [ ] **All** GitHub Action workflows pass with a ✅
- [ ] Approve Pull Request
- [ ] Merge the PR. Please use "Squash and merge" or "Rebase and merge".
This reverts commit b610804.
@jszczypinski jszczypinski self-assigned this Jun 18, 2026
@jszczypinski jszczypinski marked this pull request as ready for review June 18, 2026 12:37
@jszczypinski jszczypinski requested a review from Melkiades June 18, 2026 12:38
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Unit Tests Summary

  1 files  263 suites   3m 54s ⏱️
263 tests 263 ✅ 0 💤 0 ❌
760 runs  760 ✅ 0 💤 0 ❌

Results for commit 0fc082a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Unit Test Performance Difference

Test Suite $Status$ Time on main $±Time$ $±Tests$ $±Skipped$ $±Failures$ $±Errors$
add_difference_row 💀 $0.00$ $-0.00$ $-2$ $0$ $0$ $0$
get_surv_diff_df.R 👶 $+0.00$ $+3$ $0$ $0$ $0$
get_surv_times_df 👶 $+0.00$ $+2$ $0$ $0$ $0$
Additional test case details
Test Suite $Status$ Time on main $±Time$ Test Case
add_blank_rows 💔 $7.16$ $+1.01$ add_blank_rows_works
add_difference_row 💀 $0.00$ $-0.00$ add_difference_row.tbl_survfit_times_error_messaging_works
add_difference_row 💀 $2.56$ $-2.56$ add_difference_row.tbl_survfit_times_works
add_forest 💚 $2.57$ $-1.97$ add_forest_table_engine_flextable_works
add_grade_column 💔 $19.99$ $+1.90$ add_grade_column_works_on_a_standalone_tbl_hierarchical_rate_by_grade
adjust_stat_columns_wrap 💔 $0.66$ $+1.50$ adjust_stat_columns_wrap_validates_input_correctly
get_surv_diff_df.R 👶 $+0.00$ get_surv_diff_df_correctly_handles_custom_confidence_intervals
get_surv_diff_df.R 👶 $+0.00$ get_surv_diff_df_correctly_handles_scale_parameter
get_surv_diff_df.R 👶 $+0.66$ get_surv_diff_df_returns_a_correctly_structured_data.frame
get_surv_diff_df.R 👶 $+0.00$ get_surv_diff_df_triggers_aborts_on_invalid_inputs
get_surv_times_df 👶 $+0.00$ get_surv_times_df_applies_scale_and_conf_int_correctly
get_surv_times_df 👶 $+0.00$ get_surv_times_df_catches_invalid_inputs
get_surv_times_df 👶 $+0.13$ get_surv_times_df_works_for_stratified_and_unstratified_models
tbl_baseline_chg 💔 $31.00$ $+5.27$ tbl_baseline_chg_works
tbl_hierarchical_rate_and_count 💔 $24.65$ $+3.91$ tbl_hierarchical_rate_and_count_works
tbl_hierarchical_rate_by_grade 💔 $3.41$ $+2.31$ tbl_hierarchical_rate_by_grade_works
tbl_mmrm 💔 $7.18$ $+1.32$ se_calculates_standard_error_correctly
tbl_rmpt 💔 $15.65$ $+2.71$ tbl_rmpt_works_with_default_parameters_Example_1_
tbl_roche_subgroups 💔 $23.60$ $+5.11$ tbl_roche_subgroups_time_to_event_NULL_works
tbl_survfit_quantiles 💔 $7.43$ $+1.25$ tbl_survfit_quantiles_works
tbl_survfit_times 💀 $0.00$ $-0.00$ add_overall.tbl_survfit_times_works
tbl_survfit_times 👶 $+0.01$ tbl_survfit_times_adapts_to_user_modifications_dropping_columns_
tbl_survfit_times 💀 $0.00$ $-0.00$ tbl_survfit_times_by_messaging
tbl_survfit_times 👶 $+0.00$ tbl_survfit_times_catches_invalid_inputs
tbl_survfit_times 👶 $+0.00$ tbl_survfit_times_correctly_renders_the_p_value_row_from_combined_data
tbl_survfit_times 💀 $3.53$ $-3.53$ tbl_survfit_times_works
tbl_survfit_times 👶 $+4.63$ tbl_survfit_times_works_for_stratified_data
tbl_survfit_times 👶 $+0.00$ tbl_survfit_times_works_for_unstratified_data
tbl_with_pools 💔 $12.93$ $+2.52$ tbl_with_pools_validates_inputs_correctly
theme_gtsummary_roche 💔 $5.32$ $+1.38$ theme_gtsummary_roche_works

Results for commit b726237

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown
Contributor

badge

Code Coverage Summary

Filename                                 Stmts    Miss  Cover    Missing
-------------------------------------  -------  ------  -------  ------------------------------------------------------------------------------------------------
R/add_blank_rows.R                          63       0  100.00%
R/add_forest_utils.R                        97      10  89.69%   76-79, 94-100
R/add_forest.R                             139       0  100.00%
R/add_hierarchical_count_row.R              33       0  100.00%
R/adjust_stat_columns_wrap.R                29       1  96.55%   59
R/annotate_gg_km.R                          80       0  100.00%
R/annotate_gg_pkc.R                         92       0  100.00%
R/annotate_gg.R                             81       0  100.00%
R/ard_tabulate_abnormal_by_baseline.R       65       0  100.00%
R/crane-package.R                            2       2  0.00%    26-27
R/deprecated.R                              45      45  0.00%    18-88
R/df_add_poolings.R                         41       0  100.00%
R/get_cox_pairwise_df.R                    163       0  100.00%
R/get_surv_diff_df.R                        38       0  100.00%
R/get_surv_times_df.R                       27       0  100.00%
R/gg_km_utils.R                             35      32  8.57%    20-71
R/gg_km.R                                  143      37  74.13%   55-58, 75, 102, 176-181, 184-187, 197-199, 204-205, 239-241, 248-251, 255, 266-270, 283, 285-287
R/gg_lineplot.R                             94       0  100.00%
R/gg_mmrm_lineplot.R                       102       1  99.02%   106
R/gg_pkc_lineplot.R                         98       0  100.00%
R/gg_utils.R                               221       0  100.00%
R/label_roche.R                             72       0  100.00%
R/modify_header_rm_md.R                     18       2  88.89%   35-36
R/modify_zero_recode.R                      20       1  95.00%   64
R/reverse_difference_ci.R                   33       0  100.00%
R/tbl_baseline_chg.R                       188       0  100.00%
R/tbl_coxph.R                               90       1  98.89%   229
R/tbl_hierarchical_incidence_rate.R        291       4  98.63%   430-433
R/tbl_hierarchical_rate_and_count.R        339      13  96.17%   343, 425, 446-456
R/tbl_hierarchical_rate_by_grade.R         317       3  99.05%   169-171
R/tbl_listing.R                             35       0  100.00%
R/tbl_mmrm.R                               254       1  99.61%   393
R/tbl_null_report.R                          9       0  100.00%
R/tbl_rmpt.R                               157      12  92.36%   299-304, 316-321
R/tbl_roche_subgroups.R                    155       0  100.00%
R/tbl_roche_summary.R                       64       0  100.00%
R/tbl_shift.R                              116       0  100.00%
R/tbl_survfit_quantiles.R                  154       0  100.00%
R/tbl_survfit_times.R                       66       0  100.00%
R/tbl_with_pools.R                          64       0  100.00%
R/theme_gtsummary_roche.R                   84       1  98.81%   61
R/utils.R                                   42       0  100.00%
TOTAL                                     4256     166  96.10%

Diff against main

Filename                 Stmts    Miss  Cover
---------------------  -------  ------  --------
R/annotate_gg_km.R         -55       0  +100.00%
R/deprecated.R             +24     +24  +100.00%
R/get_surv_diff_df.R       +38       0  +100.00%
R/get_surv_times_df.R      +27       0  +100.00%
R/gg_km_utils.R              0     +18  -51.43%
R/tbl_survfit_times.R      -26       0  +100.00%
TOTAL                       +8     +42  -1.05%

Results for commit: 0fc082a

Minimum allowed coverage is 80%

♻️ This comment has been updated with latest results

Comment thread R/get_surv_times_df.R
Comment on lines +40 to +43
get_surv_times_df <- function(fit_km, times, conf_int = 0.95, scale = 1) {
# Enforce rigorous type-checking using rlang
if (!inherits(fit_km, "survfit")) {
rlang::abort("`fit_km` must be a survfit object.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it a lot!!! I think it is an amazing way forwards. Just wondering if the two get_* could be in the tbl_survfit_times. Just wondering because if they are only used there that would make documentation a bit more compact and direct (I know I have asked differently for helper functions in the past but these are exported and used only there right?)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do the full review tomorrow! ;)

@jszczypinski jszczypinski changed the title Add get_surv_times_df / overhaul creation of survival times tables Add get_surv_times_df and get_surv_diff_df()/ overhaul creation of survival times tables Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add get_surv_times_df()

2 participants