Skip to contents

leo_cox() fits one or more Cox proportional hazards models for a single exposure and incident outcome. leo_cox_format() converts the returned object into a wide summary table, tidy result table, or gtsummary output.

Usage

leo_cox(
  df,
  y_out,
  x_exp,
  x_cov = NULL,
  event_value = 1,
  min_followup_time = 0,
  x_exp_type = "auto",
  simplify = "wide",
  p_fmt = "threshold",
  verbose = TRUE
)

leo_cox_format(x, style = "wide")

Arguments

df

Data frame containing the outcome, follow-up time, exposure, and covariates.

y_out

Character vector of length 2 giving the event and follow-up time column names: c(event, time).

x_exp

Character scalar giving the exposure column name.

x_cov

NULL, a character vector of covariate column names, or a list of covariate column-name vectors. All models are fitted on the same complete-case cohort defined by y_out, x_exp, and all covariates that appear in x_cov. If x_cov is a named list, those model names are preserved in the output columns.

event_value

Value in the event column that indicates incident events.

min_followup_time

Numeric scalar; keep rows with time > min_followup_time. Default is 0, which excludes pre-baseline or baseline events when defining an incident outcome.

x_exp_type

Exposure type handling for x_exp. Use "auto" to infer from the input type, "continuous" to force a numeric Cox term, or "categorical" to force factor coding. In "auto" mode, small integer-coded exposures will trigger a warning because they may represent categorical groups.

simplify

Output mode: "wide" (default) returns the wide result table directly; "tidy" returns the tidy result table; FALSE returns the full leo_cox object.

p_fmt

P-value display format: "threshold" (default) uses <0.001; "scientific" uses scientific notation for p < 0.001 (e.g. 2.300e-04).

verbose

Logical; print progress messages.

x

Result returned by leo_cox().

style

One of "wide", "tidy", or "gtsummary".

Value

When simplify = FALSE, a leo_cox object containing the default wide table in $result, the underlying tidy rows in $result_tidy, model metadata, and fitted coxph objects. When simplify = "wide" or "tidy", the corresponding formatted table directly.

Formatting output

leo_cox_format() returns:

  • "wide": a wide summary data frame with one set of HR / CI / p-value columns per model.

  • "tidy": the row-level tidy result table stored in x$result_tidy, with formatted display columns added.

  • "gtsummary": a gtsummary regression table, or a merged gtsummary table when multiple models are present.

Examples

lung_df <- stats::na.omit(
  dplyr::transmute(
    survival::lung,
    outcome = as.integer(status == 2),
    outcome_censor = time / 365.25,
    age = age,
    sex = factor(sex, levels = c(1, 2), labels = c("Male", "Female")),
    ecog_group = factor(ph.ecog, levels = 0:3, labels = c("ECOG0", "ECOG1", "ECOG2", "ECOG3"))
  )
); head(lung_df)
#>   outcome outcome_censor age  sex ecog_group
#> 1       1      0.8377823  74 Male      ECOG1
#> 2       1      1.2457221  68 Male      ECOG0
#> 3       0      2.7652293  56 Male      ECOG0
#> 4       1      0.5749487  57 Male      ECOG1
#> 5       1      2.4175222  60 Male      ECOG0
#> 6       0      2.7980835  74 Male      ECOG1

model_cont <- list(
  "Crude" = NULL,
  "Model A" = c("sex"),
  "Model B" = c("sex", "ecog_group")
)

res_age <- leo_cox(
  df = lung_df, y_out = c("outcome", "outcome_censor"),
  x_exp = "age", x_cov = model_cont, simplify = FALSE, verbose = FALSE
)
res_age$result
#>   Exposure Outcome Case N Control N Person-years      Class Crude HR
#> 1      age outcome    164        63     190.3409 Continuous    1.019
#>   Crude 95% CI Crude P value Model A HR Model A 95% CI Model A P value
#> 1 1.001, 1.038         0.040      1.017   0.999, 1.036           0.061
#>   Model B HR Model B 95% CI Model B P value
#> 1      1.011   0.993, 1.029           0.246
leo_cox_format(res_age, style = "tidy")
#>     Model Exposure Outcome Level Case N Control N Person-years    HR
#> 1   Crude      age outcome  <NA>    164        63     190.3409 1.019
#> 2 Model A      age outcome  <NA>    164        63     190.3409 1.017
#> 3 Model B      age outcome  <NA>    164        63     190.3409 1.011
#>         95% CI P value      Class
#> 1 1.001, 1.038   0.040 Continuous
#> 2 0.999, 1.036   0.061 Continuous
#> 3 0.993, 1.029   0.246 Continuous

model_cat <- list(
  "Crude" = NULL,
  "Model A" = c("sex")
)

res_ecog <- leo_cox(
  df = lung_df, y_out = c("outcome", "outcome_censor"),
  x_exp = "ecog_group", x_cov = model_cat,
  x_exp_type = "categorical", simplify = FALSE, verbose = FALSE
)
res_ecog$result
#>      Exposure Outcome Case N Control N Person-years                  Class
#> 1 ECOG0 (Ref) outcome     37        26   60.6926762 Categorical (4 levels)
#> 2       ECOG1 outcome     82        31   97.2813142 Categorical (4 levels)
#> 3       ECOG2 outcome     44         6   32.0438056 Categorical (4 levels)
#> 4       ECOG3 outcome      1         0    0.3230664 Categorical (4 levels)
#>   Crude HR  Crude 95% CI Crude P value Model A HR Model A 95% CI
#> 1    1.000  1.000, 1.000         1.000      1.000   1.000, 1.000
#> 2    1.446  0.980, 2.134         0.063      1.519   1.028, 2.246
#> 3    2.500  1.610, 3.883        <0.001      2.579   1.660, 4.007
#> 4    9.097 1.218, 67.937         0.031      7.756  1.037, 58.039
#>   Model A P value
#> 1           1.000
#> 2           0.036
#> 3          <0.001
#> 4           0.046

if (requireNamespace("gtsummary", quietly = TRUE) && requireNamespace("broom.helpers", quietly = TRUE)) {
  leo_cox_format(res_age, style = "gtsummary")
}