# Visualise Regression Results

Regression tables are the evidence. Plots are the quick read. Use `km_plot()`, `km_risk_table()`, `rmst_table()`, `survival_summary()`, `survival_quantiles()`, `survival_prob()`, `logrank_test()`, `surv_model_compare()`, `plot_surv_fit()`, `surv_predict()`, `plot_reg()`, `plot_reg_combine()`, `forest_df()`, and `forest_reg()` to visualise survival and regression results, inspect reference categories, and prepare figures for manuscripts or reports.

    library(gtregression)
    library(dplyr)

    data("data_birthwt", package = "gtregression")
    data("data_lungcancer", package = "gtregression")

    birthwt_data <- data_birthwt |>
      mutate(
        race = factor(race, levels = c(1, 2, 3),
                      labels = c("White", "Black", "Other")),
        smoke = factor(smoke, levels = c(0, 1), labels = c("No", "Yes")),
        ht = factor(ht, levels = c(0, 1), labels = c("No", "Yes")),
        ui = factor(ui, levels = c(0, 1), labels = c("No", "Yes")),
        low = factor(low, levels = c(0, 1), labels = c("Normal BW", "Low BW")),
        ptl_cat = factor(ifelse(ptl > 0, "Yes", "No"), levels = c("No", "Yes")),
        ftv_cat = factor(case_when(
          ftv == 0 ~ "None",
          ftv == 1 ~ "One",
          ftv >= 2 ~ "Two or more"
        ), levels = c("None", "One", "Two or more"))
      )

    birthwt_exposures <- c(
      "age", "lwt", "race", "smoke", "ht", "ui", "ptl_cat", "ftv_cat"
    )

    attr(birthwt_data$age, "label") <- "Maternal age"
    attr(birthwt_data$lwt, "label") <- "Maternal weight"
    attr(birthwt_data$race, "label") <- "Maternal race"
    attr(birthwt_data$smoke, "label") <- "Smoking during pregnancy"
    attr(birthwt_data$ht, "label") <- "Hypertension"
    attr(birthwt_data$ui, "label") <- "Uterine irritability"
    attr(birthwt_data$ptl_cat, "label") <- "Previous preterm labour"
    attr(birthwt_data$ftv_cat, "label") <- "First trimester visits"

    birthwt_desc <- descriptive_table(
      birthwt_data,
      exposures = birthwt_exposures,
      by = "low",
      show_overall = "last"
    )
    birthwt_uni <- uni_reg(
      birthwt_data,
      outcome = "low",
      exposures = birthwt_exposures,
      approach = "logit"
    )
    birthwt_multi <- multi_reg(
      birthwt_data,
      outcome = "low",
      exposures = c("smoke", "ht", "ui", "ptl_cat", "ftv_cat"),
      adjust_for = c("age", "lwt", "race"),
      approach = "logit"
    )

    lung_data <- data_lungcancer |>
      mutate(
        trt = factor(trt, levels = c(1, 2),
                     labels = c("Standard treatment", "Test treatment")),
        prior = factor(prior, levels = c(0, 10), labels = c("No", "Yes"))
      )

    lung_surv <- surv_reg(
      data = lung_data,
      time = time,
      event = status,
      exposures = "trt",
      adjust_for = c("age", "karno"),
      distribution = weibull
    )

## Kaplan-Meier Curve

`km_plot()` gives the survival curve before regression modelling. Use it to show the observed survival experience by group, with optional confidence intervals, censoring marks, log-rank p-value, and number-at-risk table. If the curves sit near the top of the graph, use `ylim = c(50, 100)` on the default percentage scale, or `ylim = c(0.5, 1)` when `y_percent = FALSE`.

    km_plot(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      break_time_by = 200,
      ylim = c(50, 100),
      title = "Kaplan-Meier Survival by Treatment"
    )

When preparing a multi-panel figure, use compact titles and control the legend directly. The result remains a normal ggplot object when `risk_table = FALSE`, so it can be combined with `patchwork`.

    km_treatment <- km_plot(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      risk_table = FALSE,
      title = "A. Treatment",
      title_size = 10,
      title_face = plain,
      legend_position = bottom,
      base_size = 10
    )

    km_prior <- km_plot(
      data = lung_data,
      time = time,
      event = status,
      by = prior,
      risk_table = FALSE,
      title = "B. Prior therapy",
      title_size = 10,
      title_face = plain,
      legend_position = bottom,
      base_size = 10
    )

    patchwork::wrap_plots(km_treatment, km_prior, ncol = 2) +
      patchwork::plot_layout(guides = "collect") &
      ggplot2::theme(legend.position = "bottom")

## Risk Table

`km_risk_table()` gives the number at risk at selected follow-up times as a standalone table. This is useful when the risk table needs to be reported beside or underneath a Kaplan-Meier curve.

    km_risk_table(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      times = c(0, 90, 180, 365)
    )

    ## a flextable object.
    ## col_keys: `Group`, `Time`, `At risk`, `Events`, `Censored`
    ## header has 1 row(s)
    ## body has 8 row(s)
    ## original dataset sample:
    ## 'data.frame':    8 obs. of  5 variables:
    ##  $ Group   : chr  "Standard treatment" "Standard treatment" "Standard treatment" "Standard treatment" ...
    ##  $ Time    : chr  "0.0" "90.0" "180.0" "365.0" ...
    ##  $ At risk : chr  "69" "37" "13" "4" ...
    ##  $ Events  : chr  "0" "31" "21" "8" ...
    ##  $ Censored: chr  "0" "1" "3" "1" ...

## Restricted Mean Survival Time

`rmst_table()` reports the average survival time up to a fixed follow-up point, called `tau`. This is useful when readers want an absolute survival-time summary, or when hazard ratios are difficult to explain.

    rmst_table(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      tau = 365
    )

    ## a flextable object.
    ## col_keys: `Group`, `Tau`, `N`, `Events`, `RMST (95% CI)`, `RMST difference (95% CI)`, `p-value`
    ## header has 1 row(s)
    ## body has 3 row(s)
    ## original dataset sample:
    ## 'data.frame':    3 obs. of  7 variables:
    ##  $ Group                   : chr  "Standard treatment" "Test treatment" "Difference (Test treatment - Standard treatment)"
    ##  $ Tau                     : chr  "365.0" "365.0" "365.0"
    ##  $ N                       : chr  "69" "68" ""
    ##  $ Events                  : chr  "64" "64" ""
    ##  $ RMST (95% CI)           : chr  "119.0 (93.5-144.5)" "112.4 (83.3-141.6)" ""
    ##  $ RMST difference (95% CI): chr  "" "" "-6.6 (-45.3-32.2)"
    ##  $ p-value                 : chr  "" "" "0.740"

## Kaplan-Meier Summary

`survival_summary()` is the table companion to the Kaplan-Meier curve. It reports the number analysed, events, censored observations, and median survival with a 95% confidence interval. Use it when readers need the key survival numbers without reading them from the plot.

    survival_summary(
      data = lung_data,
      time = time,
      event = status,
      by = trt
    )

    ## a flextable object.
    ## col_keys: `Group`, `N`, `Events`, `Censored`, `Median survival (95% CI)`
    ## header has 1 row(s)
    ## body has 2 row(s)
    ## original dataset sample:
    ## 'data.frame':    2 obs. of  5 variables:
    ##  $ Group                   : chr  "Standard treatment" "Test treatment"
    ##  $ N                       : chr  "69" "68"
    ##  $ Events                  : chr  "64" "64"
    ##  $ Censored                : chr  "5" "4"
    ##  $ Median survival (95% CI): chr  "103.0 (59.0-132.0)" "52.5 (44.0-95.0)"

## Survival Quantiles

`survival_quantiles()` reports detailed Kaplan-Meier time points. This is useful when the median alone is not enough, or when you want to show the 25th, 50th, and 75th percentile event times by group.

    survival_quantiles(
      data = lung_data,
      time = time,
      event = status,
      by = trt
    )

    ## a flextable object.
    ## col_keys: `Group`, `Event percentile`, `Survival probability`, `Time (95% CI)`
    ## header has 1 row(s)
    ## body has 6 row(s)
    ## original dataset sample:
    ## 'data.frame':    6 obs. of  4 variables:
    ##  $ Group               : chr  "Standard treatment" "Standard treatment" "Standard treatment" "Test treatment" ...
    ##  $ Event percentile    : chr  "25%" "50%" "75%" "25%" ...
    ##  $ Survival probability: chr  "75%" "50%" "25%" "75%" ...
    ##  $ Time (95% CI)       : chr  "27.0 (16.0-54.0)" "103.0 (59.0-132.0)" "162.0 (139.0-260.0)" "24.5 (19.0-43.0)" ...

## Survival Probabilities

`survival_prob()` reports Kaplan-Meier survival probability at fixed follow-up times. This is useful for clinically familiar summaries such as 90-day, 6-month, or 1-year survival.

    survival_prob(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      times = c(90, 180, 365)
    )

    ## a flextable object.
    ## col_keys: `Group`, `Time`, `At risk`, `Events`, `Censored`, `Survival probability (95% CI)`
    ## header has 1 row(s)
    ## body has 6 row(s)
    ## original dataset sample:
    ## 'data.frame':    6 obs. of  6 variables:
    ##  $ Group                        : chr  "Standard treatment" "Standard treatment" "Standard treatment" "Test treatment" ...
    ##  $ Time                         : chr  "90.0" "180.0" "365.0" "90.0" ...
    ##  $ At risk                      : chr  "37" "13" "4" "25" ...
    ##  $ Events                       : chr  "31" "21" "8" "42" ...
    ##  $ Censored                     : chr  "1" "3" "1" "2" ...
    ##  $ Survival probability (95% CI): chr  "54.7% (44.0%-67.9%)" "21.2% (13.2%-34.1%)" "7.1% (2.8%-18.0%)" "38.0% (28.0%-51.6%)" ...

## Log-Rank Test

`logrank_test()` formally compares Kaplan-Meier curves between groups. It is useful after the curve and summary table, but it does not give an effect size; use `cox_reg()` when a hazard ratio is needed.

    logrank_test(
      data = lung_data,
      time = time,
      event = status,
      by = trt
    )

    ## a flextable object.
    ## col_keys: `Group`, `N`, `Observed events`, `Expected events`
    ## header has 1 row(s)
    ## body has 2 row(s)
    ## original dataset sample:
    ## 'data.frame':    2 obs. of  4 variables:
    ##  $ Group          : chr  "Standard treatment" "Test treatment"
    ##  $ N              : chr  "69" "68"
    ##  $ Observed events: chr  "64" "64"
    ##  $ Expected events: chr  "64.50" "63.50"

## Parametric Survival Model Comparison

`surv_model_compare()` helps users choose a candidate distribution before using `surv_reg()`. It fits the same model with Weibull, exponential, lognormal, and loglogistic distributions, then compares AIC and BIC.

    surv_model_compare(
      data = lung_data,
      time = time,
      event = status,
      exposures = c("trt", "celltype"),
      adjust_for = c("age", "karno")
    )

    ## a flextable object.
    ## col_keys: `Distribution`, `AIC`, `BIC`, `Log-likelihood`, `Scale`, `N`, `Events`, `Best AIC`, `Best BIC`
    ## header has 1 row(s)
    ## body has 4 row(s)
    ## original dataset sample:
    ## 'data.frame':    4 obs. of  9 variables:
    ##  $ Distribution  : chr  "loglogistic" "lognormal" "exponential" "weibull"
    ##  $ AIC           : chr  "1,440.12" "1,445.61" "1,446.39" "1,447.17"
    ##  $ BIC           : chr  "1,463.48" "1,468.97" "1,466.83" "1,470.53"
    ##  $ Log-likelihood: chr  "-712.06" "-714.80" "-716.19" "-715.59"
    ##  $ Scale         : chr  "0.58" "1.06" "1.00" "0.93"
    ##  $ N             : chr  "137" "137" "137" "137"
    ##  $ Events        : chr  "128" "128" "128" "128"
    ##  $ Best AIC      : chr  "Yes" "No" "No" "No"
    ##  $ Best BIC      : chr  "Yes" "No" "No" "No"

## Parametric Survival Fit Plot

`plot_surv_fit()` is the visual companion to `surv_model_compare()`. It overlays the observed Kaplan-Meier curve with fitted parametric survival curves, so users can check whether a distribution that looks good by AIC/BIC also follows the observed survival pattern.

    plot_surv_fit(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      distributions = c(weibull, lognormal),
      break_time_by = 200
    )

Adjusted fitted curves can also be drawn. In that case, the curves are predicted at typical adjustment values, such as medians for numeric variables and the most common level for categorical variables.

    plot_surv_fit(
      data = lung_data,
      time = time,
      event = status,
      by = trt,
      adjust_for = c(age, karno),
      distributions = loglogistic,
      xlim = c(0, 800)
    )

## Parametric Survival Prediction

`surv_predict()` turns a fitted parametric survival model into predicted survival probabilities at clinically useful follow-up times. This helps users move from a time-ratio table to a more direct statement such as predicted 90-day, 180-day, or 1-year survival for a profile.

    surv_predict(
      model = lung_surv$models$trt,
      newdata = data.frame(
        trt = factor("Test treatment", levels = levels(lung_data$trt)),
        age = 60,
        karno = 70
      ),
      times = c(90, 180, 365)
    )

    ## a flextable object.
    ## col_keys: `Profile`, `trt`, `age`, `karno`, `Time`, `Predicted survival`, `Model distribution`
    ## header has 1 row(s)
    ## body has 3 row(s)
    ## original dataset sample:
    ## 'data.frame':    3 obs. of  7 variables:
    ##  $ Profile           : int  1 1 1
    ##  $ trt               : Factor w/ 2 levels "Standard treatment",..: 2 2 2
    ##  $ age               : num  60 60 60
    ##  $ karno             : num  70 70 70
    ##  $ Time              : chr  "90.0" "180.0" "365.0"
    ##  $ Predicted survival: chr  "55.3%" "31.1%" "9.7%"
    ##  $ Model distribution: chr  "weibull" "weibull" "weibull"

If `newdata` is omitted, `surv_predict()` uses a typical profile from the model data, using medians for numeric variables and the most common level for categorical variables.

## One Regression Plot

`plot_reg()` turns a `uni_reg()` or `multi_reg()` result into a forest-style plot. By default, categorical reference levels are shown and labelled as `(Ref.)`; the caption explains the abbreviation. Variable labels set on the data are used automatically in the plot.

The default style uses colour-blind-safe blue estimates and vermillion for estimates whose confidence interval excludes the null. Use `point_size`, `point_stroke`, `ci_linewidth`, and `base_size` when preparing a slide or a larger single-panel figure.

    plot_reg(
      birthwt_uni,
      title = "Crude Associations With Low Birth Weight"
    )

    plot_reg(
      birthwt_uni,
      show_ref = FALSE,
      point_size = 3.5,
      point_stroke = 0.7,
      ci_linewidth = 0.75,
      base_size = 13,
      title = "Crude Associations With Low Birth Weight"
    )

## Adjusted Regression Plot

When the input comes from `multi_reg(adjust_for = ...)`, the adjustment set is shown in the plot caption by default. This keeps the figure interpretable when it is copied into slides or a manuscript draft.

    plot_reg(
      birthwt_multi,
      show_ref = FALSE,
      log_x = TRUE,
      title = "Adjusted Associations With Low Birth Weight"
    )

## Compact Binary Predictors

For Yes/No, 1/0, true/false, or similar binary predictors, set `show_ref = FALSE` to hide reference rows. Affirmative binary levels such as `Yes` are displayed as the exposure name itself, so the plot remains compact: `smoke`, `ht`, `ui`, and `ptl_cat` are easier to read than repeated `variable: Yes` labels.

    plot_reg(
      birthwt_uni,
      show_ref = FALSE,
      title = "Crude Associations With Reference Rows Hidden"
    )

## Log Axis and Tick Marks

For ratio measures such as odds ratios, risk ratios, and incidence rate ratios, `log_x = TRUE` uses a log-scaled x-axis. If you do not provide tick marks, `gtregression` chooses sensible defaults around the null value of 1.

    plot_reg(
      birthwt_uni,
      log_x = TRUE,
      title = "Crude Associations on a Log Scale"
    )

You can still take full control of the visible axis range and tick marks.

    plot_reg(
      birthwt_uni,
      show_ref = FALSE,
      log_x = TRUE,
      xlim = c(0.25, 12),
      breaks = c(0.5, 1, 2, 4, 8),
      title = "Crude Associations With Custom Axis"
    )

## Compare Crude and Adjusted Effects

`plot_reg_combine()` places crude and adjusted model results side by side. This is useful when a manuscript needs to show how adjustment changes the estimate. Axis limits and tick marks can be controlled separately for each side.

    plot_reg_combine(
      tbl_uni = birthwt_uni,
      tbl_multi = birthwt_multi,
      show_ref = FALSE,
      log_x = TRUE,
      xlim_uni = c(0.25, 12),
      breaks_uni = c(0.5, 1, 2, 4, 8),
      xlim_multi = c(0.25, 16),
      breaks_multi = c(0.5, 1, 2, 4, 8),
      title_uni = "Crude Effects",
      title_multi = "Adjusted Effects"
    )

## Publication-Style Forest Table

`forest_df()` prepares the data. `forest_reg()` draws the forest table. This is the most manuscript-oriented plot when you want descriptive summaries and crude or adjusted estimates in the same figure.

    forest_data <- forest_df(
      uni = birthwt_uni,
      multi = birthwt_multi,
      desc = birthwt_desc
    )
    forest_reg(forest_data)

### Fine-Tune Forest Tables

Wide forest tables combine descriptive summaries, crude estimates, adjusted estimates, and one or two forest plot panels. If the x-axis labels are crowded, control the axis range and tick marks with `xlim` and `ticks_at`.

Use a list when the table has crude and adjusted forest plot columns.

    forest_reg(
      forest_data,
      xlim = list(c(0.25, 8), c(0.8, 25)),
      ticks_at = list(
        c(0.5, 1, 2, 4, 8),
        c(1, 2, 4, 8, 16)
      ),
      quiet = TRUE
    )

If the plot panel itself looks too narrow or too wide, tune `ci_col_width`. This changes the blank spacer column that `forestploter` uses for drawing the confidence intervals. Larger values give the CI panel more room; smaller values make the overall table more compact.

    forest_reg(
      forest_data,
      ci_col_width = c(18, 22),
      xlim = list(c(0.25, 8), c(0.8, 25)),
      ticks_at = list(
        c(0.5, 1, 2, 4, 8),
        c(1, 2, 4, 8, 16)
      )
    )

For publication export, use a wider graphics device or document canvas when the table includes several descriptive columns and two model columns.

You can also build and draw in one call.

    forest_reg(
      uni = birthwt_uni,
      multi = birthwt_multi,
      desc = birthwt_desc,
      side = "left"
    )

## What To Inspect

- `plot_reg()` returns a `ggplot`.
- `plot_reg_combine()` returns a combined `ggplot`.
- `km_risk_table()` reports at-risk, event, and censored counts at requested follow-up times.
- `rmst_table()` reports restricted mean survival time up to a chosen time point.
- `show_ref = TRUE` displays reference levels as `(Ref.)`.
- `show_ref = FALSE` hides reference levels; affirmative binary predictors are shown as compact bold exposure rows.
- Adjustment variables from `multi_reg(adjust_for = ...)` are carried into plot captions.
- `log_x = TRUE` uses log scaling for non-linear model effect measures.
- `forest_df()` returns the plotting data frame.
- `survival_prob()` reports survival probability at requested follow-up times.
- `surv_model_compare()` compares parametric survival distributions before fitting final `surv_reg()` tables.
- `plot_surv_fit()` overlays fitted parametric survival curves on the observed Kaplan-Meier curve.
- `surv_predict()` reports model-based survival probabilities at user-specified follow-up times.
- In `forest_reg()`, use `xlim` and `ticks_at` when x-axis labels overlap.
- In `forest_reg()`, use `ci_col_width` when the CI plot panel is too narrow or too wide.
- `forest_reg()` returns `plot`, `data`, `input_data`, and `meta`.