Skip to contents

[Experimental]

leo_cox_mediation_plot() turns a leo_cox_mediation result into a compact pathway figure for a selected model. The plot keeps all annotation text on a shared vertical center axis, uses equal-sized boxes, and draws only straight arrows between the exposure, mediator, and outcome nodes.

Usage

leo_cox_mediation(
  df,
  y_out,
  x_exp,
  x_med,
  x_cov = NULL,
  event_value = 1,
  min_followup_time = 0,
  x_exp_a0 = NULL,
  x_exp_a1 = NULL,
  x_med_cde = NULL,
  x_cov_cond = NULL,
  mreg = c("auto", "linear", "logistic"),
  yreg = c("auto", "survCox", "survAFT_weibull", "survAFT_exp"),
  interaction = TRUE,
  verbose = TRUE
)

leo_cox_mediation_plot(
  x,
  model = NULL,
  exposure_label = "Exposure",
  mediator_label = "Mediator",
  outcome_label = "Outcome",
  language = c("en", "zh"),
  palette = c("jama", "jco", "lancet", "nejm"),
  add_note = TRUE,
  font_family = NULL
)

Arguments

df

Data frame with outcome, time, exposure, mediator, and covariates.

y_out

c(event, time) column names.

x_exp

Exposure column name.

x_med

Mediator column name.

x_cov

Covariate column names, or NULL.

event_value

Value indicating an event.

min_followup_time

Minimum follow-up time filter.

x_exp_a0

Reference value of x_exp. Defaults to first factor level; required if x_exp is character (not factor). For a binary factor exposure, either use the default contrast or supply x_exp_a0 and x_exp_a1 together. For numeric x_exp, if exactly two unique observed values remain after filtering, they are used as the default contrast; otherwise, supply x_exp_a0 and x_exp_a1 explicitly.

x_exp_a1

Contrasted value of x_exp. Defaults to second factor level; required if x_exp is character (not factor). For a binary factor exposure, either use the default contrast or supply x_exp_a0 and x_exp_a1 together. For numeric x_exp, if exactly two unique observed values remain after filtering, they are used as the default contrast; otherwise, supply x_exp_a0 and x_exp_a1 explicitly.

x_med_cde

Value of x_med for CDE evaluation. Defaults to second factor level (binary) or median (continuous); required if x_med is character (not factor). For logistic mediators, use the original observed mediator value rather than an internal 0/1 recode unless the observed values are themselves 0/1.

x_cov_cond

Covariate values for effect evaluation. Named list/vector for non-numeric covariates; defaults to medians. Required for factor/binary covariates.

mreg

Mediator model: "auto", "linear", or "logistic".

yreg

Outcome model: "auto", "survCox", "survAFT_weibull", or "survAFT_exp".

interaction

Include x_exp:x_med interaction? If TRUE, the outcome model adds x_exp:x_med, i.e. time ~ x_exp + x_med + x_exp:x_med + x_cov.

verbose

Print progress messages.

x

A leo_cox_mediation object returned by leo_cox_mediation().

model

Character scalar giving the model to display. Defaults to the last model in x$result.

exposure_label

Character label shown in the left box. Manual \n line breaks are respected.

mediator_label

Character label shown in the middle box. Manual \n line breaks are respected.

outcome_label

Character label shown in the right box. Manual \n line breaks are respected.

language

One of "en" or "zh".

palette

One of "jama", "jco", "lancet", or "nejm".

add_note

Logical; whether to show tutorial-style note text such as the exposure contrast, CDE mediator reference, and a TE-null caution when applicable.

font_family

Optional graphics font family. Use generic families such as "sans", "serif", or "mono" for the most robust cross-device output.

Value

leo_cox_mediation object with $result, $result_detail, $evaluation, $fit.

For leo_cox_mediation(), a leo_cox_mediation object containing a display table in $result, a detailed mediation table in $result_detail, an evaluation summary in $evaluation, and fitted regmedint objects in $fit. For leo_cox_mediation_plot(), an invisible recordedplot object after drawing the pathway figure on the active graphics device.

Details

Performs causal mediation analysis for survival outcomes via regmedint::regmedint(). Fits mediator model x_med ~ x_exp + x_cov and outcome model time ~ x_exp + x_med + x_cov (with optional x_exp:x_med interaction).

yreg = "auto" uses the observed event proportion in the post-filter, complete-case analysis set: Cox for rare outcomes (<=10% events), AFT otherwise. This 10% threshold is an empirical rare-event approximation rule.

Examples

if (requireNamespace("regmedint", quietly = TRUE)) {
  set.seed(123)
  n <- 200
  age <- rnorm(n, 60, 8)
  exposure <- rbinom(n, 1, 0.5)
  mediator <- rnorm(n, 0.6 * exposure + 0.02 * (age - 60), 1)
  time_event <- rexp(n, rate = exp(-5.8 + 0.55 * exposure + 0.20 * mediator + 0.02 * (age - 60)))
  time_censor <- rexp(n, rate = 0.08)
  med_df <- data.frame(
    outcome = as.integer(time_event <= time_censor),
    outcome_censor = pmax(pmin(time_event, time_censor), 0.1),
    exposure = exposure, mediator = mediator, age = age
  )
  res_med <- leo_cox_mediation(
    df = med_df, y_out = c("outcome", "outcome_censor"),
    x_exp = "exposure", x_med = "mediator", x_cov = "age",
    verbose = FALSE
  )
  res_med$result
}
#>     Model Effect code                        Effect        Scale Exposure
#> 1 model_1         cde      Controlled direct effect Hazard ratio exposure
#> 2 model_1        pnde    Pure natural direct effect Hazard ratio exposure
#> 3 model_1        tnie Total natural indirect effect Hazard ratio exposure
#> 4 model_1        tnde   Total natural direct effect Hazard ratio exposure
#> 5 model_1        pnie  Pure natural indirect effect Hazard ratio exposure
#> 6 model_1          te                  Total effect Hazard ratio exposure
#> 7 model_1          pm           Proportion mediated   Proportion exposure
#>   Mediator Outcome Exposure contrast Mediator reference   N Case N Non-event N
#> 1 mediator outcome    0.000 -> 1.000              0.370 200      8         192
#> 2 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#> 3 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#> 4 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#> 5 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#> 6 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#> 7 mediator outcome    0.000 -> 1.000               <NA> 200      8         192
#>   Total follow-up Estimate        95% CI P value Mediator model Outcome model
#> 1        2545.621    3.247 0.540, 19.534   0.198         linear       survCox
#> 2        2545.621    3.420 0.626, 18.687   0.156         linear       survCox
#> 3        2545.621    1.376  0.865, 2.189   0.178         linear       survCox
#> 4        2545.621    3.741 0.570, 24.566   0.169         linear       survCox
#> 5        2545.621    1.258  0.567, 2.788   0.572         linear       survCox
#> 6        2545.621    4.705 0.865, 25.595   0.073         linear       survCox
#> 7        2545.621    0.347 -0.091, 0.784   0.120         linear       survCox

# Continuous exposure requires x_exp_a0 and x_exp_a1
if (requireNamespace("regmedint", quietly = TRUE)) {
  set.seed(123)
  n <- 200
  cont_df <- data.frame(
    outcome = rbinom(n, 1, 0.06),
    outcome_censor = rexp(n, 0.1),
    exposure = rnorm(n), mediator = rnorm(n), age = rnorm(n, 60, 8)
  )
  res_cont <- leo_cox_mediation(
    df = cont_df, y_out = c("outcome", "outcome_censor"),
    x_exp = "exposure", x_med = "mediator", x_cov = "age",
    x_exp_a0 = -1, x_exp_a1 = 1, verbose = FALSE
  )
  res_cont$result
}
#>     Model Effect code                        Effect        Scale Exposure
#> 1 model_1         cde      Controlled direct effect Hazard ratio exposure
#> 2 model_1        pnde    Pure natural direct effect Hazard ratio exposure
#> 3 model_1        tnie Total natural indirect effect Hazard ratio exposure
#> 4 model_1        tnde   Total natural direct effect Hazard ratio exposure
#> 5 model_1        pnie  Pure natural indirect effect Hazard ratio exposure
#> 6 model_1          te                  Total effect Hazard ratio exposure
#> 7 model_1          pm           Proportion mediated   Proportion exposure
#>   Mediator Outcome Exposure contrast Mediator reference   N Case N Non-event N
#> 1 mediator outcome   -1.000 -> 1.000              0.002 200     12         188
#> 2 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#> 3 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#> 4 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#> 5 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#> 6 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#> 7 mediator outcome   -1.000 -> 1.000               <NA> 200     12         188
#>   Total follow-up Estimate        95% CI P value Mediator model Outcome model
#> 1        2055.604    0.973  0.333, 2.841   0.960         linear       survCox
#> 2        2055.604    0.975  0.332, 2.867   0.964         linear       survCox
#> 3        2055.604    0.995  0.941, 1.052   0.854         linear       survCox
#> 4        2055.604    0.967  0.333, 2.806   0.951         linear       survCox
#> 5        2055.604    1.003  0.960, 1.049   0.880         linear       survCox
#> 6        2055.604    0.970  0.333, 2.828   0.956         linear       survCox
#> 7        2055.604    0.172 -6.494, 6.838   0.960         linear       survCox
if (requireNamespace("regmedint", quietly = TRUE)) {
  set.seed(123)
  n <- 200
  age <- rnorm(n, 60, 8)
  exposure_num <- rbinom(n, 1, 0.5)
  mediator_num <- rbinom(n, 1, plogis(-0.4 + 0.9 * exposure_num + 0.02 * (age - 60)))
  time_event <- rexp(n, rate = exp(-5.8 + 0.55 * exposure_num + 0.45 * mediator_num + 0.02 * (age - 60)))
  time_censor <- rexp(n, rate = 0.08)
  med_df <- data.frame(
    outcome = as.integer(time_event <= time_censor),
    outcome_censor = pmax(pmin(time_event, time_censor), 0.1),
    exposure = factor(exposure_num, levels = c(0, 1), labels = c("Low risk", "High risk")),
    mediator = factor(mediator_num, levels = c(0, 1), labels = c("Low inflammation", "High inflammation")),
    age = age
  )
  res_med <- leo_cox_mediation(
    df = med_df, y_out = c("outcome", "outcome_censor"),
    x_exp = "exposure", x_med = "mediator", x_cov = "age",
    verbose = FALSE
  )
  # with default labels and note
  leo_cox_mediation_plot(
    res_med, model = "model_1",
    exposure_label = "Metabolic\nrisk",
    mediator_label = "Inflammation",
    outcome_label = "Incident\noutcome"
  )
  # with no note
  leo_cox_mediation_plot(
    res_med, model = "model_1",
    exposure_label = "Metabolic\nrisk",
    mediator_label = "Inflammation",
    outcome_label = "Incident\noutcome",
    add_note = FALSE
  )
}