Many manuscript figures are paired with a small numeric table: a numbers-at-risk strip below a Kaplan-Meier curve, or the count panel beneath a longitudinal participation bar chart. The hvtiPlotR plot objects carry these companion tables alongside the plot data, so we can pull the numbers straight out and render them with gt(Iannone et al. 2026), the right tool, as in the publication tables chapter, when a table’s destination is this book or another HTML document. Manuscript tables headed for a Word file go through hvtiRtables instead, which turns a gtsummary summary into a CORR-compliant flat-header table.
40.1 When to use it
A figure shows the shape of a result; the companion table gives the reader the numbers the shape rests on. The two go together when a curve hides how much data backs each region of it. The clearest case is the numbers-at-risk strip beneath a Kaplan-Meier curve: the curve might look confident out at ten years, but if only eight patients remain at risk there, the at-risk row is what tells the reader to read that tail with caution. Reach for a companion table whenever the figure invites a question the plot cannot answer on its own (“based on how many?”) and you want the answer on the same page rather than buried in the text.
The at-risk count is a denominator, not a second estimate. At time \(t\) it counts patients who are still observed and event-free immediately before \(t\). An event changes the survival curve and removes that patient from later risk sets; censoring removes a patient from later risk sets without creating a survival step. Thus the count tells you how much information supports \(S(t)\) and its confidence interval, but it does not tell you the survival probability by itself.
We work through two cases the package already supports: the at-risk strip that a survival curve needs beside it, and the participation counts that sit beneath a longitudinal bar chart. In both, the numbers already live on the plot object, so we never recompute them and risk a table that disagrees with its own figure.
40.2 Numbers at risk
hv_survival() stores its companion tables in the object’s $tables slot. The numbers-at-risk strip lives in $tables$risk (one row per strata × report-time) and the per-time-point summary in $tables$report.
km <-hv_survival(sample_survival_data(n =500, seed =42),report_times =seq(0, 20, 5))names(km$tables)
[1] "risk" "report"
km$tables$risk
strata report_time n.risk
1 All 0 500
2 All 5 411
3 All 10 321
4 All 15 259
5 All 20 207
We render the at-risk table directly with gt, relabelling the columns for presentation.
km$tables$risk |>gt() |>tab_header(title ="Numbers at Risk",subtitle ="Patients remaining under follow-up at each report time" ) |>cols_label(strata ="Stratum",report_time ="Time (years)",n.risk ="At risk (n)" )
Numbers at Risk
Patients remaining under follow-up at each report time
Stratum
Time (years)
At risk (n)
All
0
500
All
5
411
All
10
321
All
15
259
All
20
207
The companion report table carries the survival estimates and confidence limits at the same report times. It lets you quote a 5- or 10-year estimate together with the denominator that supports it.
For the figure itself, build the curve and the risk panel from that same km object. hv_atrisk_compose() reads the finished curve’s x-range and applies it to the table, so the 0-, 5-, 10-, 15-, and 20-year counts sit directly beneath their curve positions. This is why the report times and axis breaks are set to the same sequence.
Figure 40.1: Kaplan-Meier survival with the supporting at-risk denominator aligned at 0, 5, 10, 15, and 20 years
40.3 Longitudinal count table
hv_longitudinal() produces a two-panel figure: a grouped bar chart of participation counts and a numeric table panel below it. The underlying counts live in the object’s $data slot in long format (one row per time-window × series), with the column roles recorded in $meta.
We reshape the long counts into a wide table (one row per follow-up window, one column per series) and render it with gt. This is the tabular companion to the bar chart’s table panel.
# A tibble: 7 × 3
time_label Patients Measurements
<fct> <int> <int>
1 ≥0 Days 19 19
2 ≥1 Month 47 50
3 ≥3 Months 49 56
4 ≥6 Months 100 118
5 ≥1 Year 159 217
6 ≥2 Years 101 113
7 ≥2.5 Years 276 620
lc_wide |>gt(rowname_col ="time_label") |>tab_header(title ="Longitudinal Participation Counts",subtitle ="Patients and measurements at each follow-up window" ) |>fmt_number(columns =where(is.numeric), decimals =0) |>tab_stubhead(label ="Follow-up window")
Longitudinal Participation Counts
Patients and measurements at each follow-up window
Follow-up window
Patients
Measurements
≥0 Days
19
19
≥1 Month
47
50
≥3 Months
49
56
≥6 Months
100
118
≥1 Year
159
217
≥2 Years
101
113
≥2.5 Years
276
620
The gt version above is for a standalone table, say in the manuscript body or a supplement. When the numbers belong inside the figure, the package draws them as a table panel you stack under the chart. For reference, the same counts rendered as the in-figure table panel appear below the bar chart when the two are composed with patchwork. Building both from the same lc$data keeps the standalone table and the in-figure panel telling one story.
At-risk counts must match the curve’s time axis. The risk strip is only useful if its columns sit under the same time points the curve uses. If the plot runs 0 to 20 years on a five-year grid, the at-risk table needs the same grid, or the reader cannot line a count up with the part of the curve it describes. Set report_times explicitly when you build km, use those times as the curve’s breaks, and let hv_atrisk_compose() align the finished panels. If you later crop the curve, inspect the table again.
Keeping the table and figure in sync. The reason we pull numbers off the plot object rather than recomputing them is to keep a single source of truth. The moment you summarise the cohort separately for the table, the two can drift: a filter applied to one and not the other, a different handling of missing follow-up, a stale data load. If you must build a companion table by hand, derive it from the exact frame the figure was built from, and re-derive both whenever the data change. A figure and a table that disagree are worse than either alone, because a reader cannot tell which to believe.
Iannone, Richard, Joe Cheng, Barret Schloerke, et al. 2026. Gt: Easily Create Presentation-Ready Display Tables. https://gt.rstudio.com.