| Title: | Efficacy and Safety Graphics for Clinical Review Packages |
|---|---|
| Description: | Builds submission-style efficacy and safety figures used in clinical review packages, with one simulator for each therapeutic area. Oncology figures include Kaplan-Meier curves, RECIST waterfalls, swimmer plots and spider plots. Cardiovascular, endocrine, immunology, infectious disease and neuroscience figures include time-to-event curves, mean change from baseline and responder rates. Shared safety figures include an adverse-event dot plot, an eDISH liver plot, a laboratory shift plot and an exposure plot. Reference lines follow RECIST 1.1 and the FDA drug-induced liver injury guidance. This is not an FDA product and does not guarantee regulatory acceptance. |
| Authors: | Shekhar [aut, cre] |
| Maintainer: | Shekhar <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-15 20:50:08 UTC |
| Source: | https://github.com/cran/trialfigs |
Simulated-data utilities and submission-style figures commonly used in
clinical review packages. Not an FDA product. See ta_plot_catalog
for the therapeutic-area map.
Shekhar
plot_km, plot_waterfall, plot_edish
The reference level is the other arm, so a ratio below 1 favours the investigational arm. One row is returned, for binding into a forest.
cox_hr(data, time, event, arm, investigational = "Investigational", subgroup = "Overall")cox_hr(data, time, event, arm, investigational = "Investigational", subgroup = "Overall")
data |
Subject-level data. |
time, event, arm
|
Column names. |
investigational |
Level of |
subgroup |
Label stored in the result. |
A one-row data frame with subgroup, estimate, limits and n.
onc <- simulate_oncology(n_per_arm = 20) cox_hr(onc$adsl, "os_months", "os_event", "arm")onc <- simulate_oncology(n_per_arm = 20) cox_hr(onc$adsl, "os_months", "os_event", "arm")
Named colors for treatment arms and RECIST responses.
fda_palette()fda_palette()
A named character vector of colors.
fda_palette()fda_palette()
Classic theme with a left-aligned caption for population notes.
fda_theme(base_size = 11)fda_theme(base_size = 11)
base_size |
Base font size in points. |
A ggplot2 theme object.
fda_theme()fda_theme()
Percent of subjects with each term and a Wilson interval, ordered by the risk difference versus the reference arm.
plot_ae(data, term = "term", arm = "arm", denom = NULL, id = "usubjid", reference = "Placebo", title = "Subjects with an adverse event")plot_ae(data, term = "term", arm = "arm", denom = NULL, id = "usubjid", reference = "Placebo", title = "Subjects with an adverse event")
data |
One row per subject per event. |
term, arm, id
|
Column names. |
denom |
Subject-level denominator. Required for a correct percent. |
reference |
Arm used when ordering by risk difference. |
title |
Plot title. |
A ggplot object.
saf <- simulate_safety(n_per_arm = 25) plot_ae(saf$adae, denom = saf$adsl, reference = "Placebo")saf <- simulate_safety(n_per_arm = 25) plot_ae(saf$adae, denom = saf$adsl, reference = "Placebo")
The band is the normal approximation to the standard error of the mean. Replace it with the model named in the statistical analysis plan for a primary contrast.
plot_change(data, visit = "week", change = "chg", arm = "arm", title = "Mean change from baseline", xlab = "Week", ylab = "Mean change (95% CI)")plot_change(data, visit = "week", change = "chg", arm = "arm", title = "Mean change from baseline", xlab = "Week", ylab = "Mean change (95% CI)")
data |
Longitudinal data. |
visit, change, arm
|
Column names. |
title, xlab, ylab
|
Labels. |
A ggplot object.
endo <- simulate_endocrinology(n_per_arm = 15) plot_change(endo$adlb, title = "HbA1c")endo <- simulate_endocrinology(n_per_arm = 15) plot_change(endo$adlb, title = "HbA1c")
Log-scale multiples of the upper limit of normal. Reference lines are ALT 3x ULN and total bilirubin 2x ULN. A point in that quadrant needs narrative review; it is not by itself a Hy's law case.
plot_edish(data, alt = "alt_xuln", bili = "bili_xuln", arm = "arm", id = "usubjid", title = "eDISH: peak ALT vs peak total bilirubin")plot_edish(data, alt = "alt_xuln", bili = "bili_xuln", arm = "arm", id = "usubjid", title = "eDISH: peak ALT vs peak total bilirubin")
data |
One row per subject, peak values. |
alt, bili, arm
|
Column names, values in multiples of ULN. |
id |
Optional subject id, labelled for quadrant cases. |
title |
Plot title. |
A ggplot object.
saf <- simulate_safety(n_per_arm = 25) plot_edish(saf$adlb)saf <- simulate_safety(n_per_arm = 25) plot_edish(saf$adlb)
Box plot of exposure with the median in the caption.
plot_exposure(data, duration = "trtdur_days", arm = "arm", xlab = "Treatment duration (days)", title = "Exposure")plot_exposure(data, duration = "trtdur_days", arm = "arm", xlab = "Treatment duration (days)", title = "Exposure")
data |
Subject-level data. |
duration, arm
|
Column names. |
xlab, title
|
Labels. |
A ggplot object.
saf <- simulate_safety(n_per_arm = 20) plot_exposure(saf$adsl)saf <- simulate_safety(n_per_arm = 20) plot_exposure(saf$adsl)
Use null = 1 for hazard ratios and null = 0 for mean differences.
Intervals that cross the null are drawn in grey.
plot_forest(data, label = "subgroup", estimate = "estimate", lcl = "lcl", ucl = "ucl", n = NULL, null = 1, xlab = "Hazard ratio (95% CI)", title = "Subgroup forest plot", subtitle = NULL, favour_left = "Favours investigational", favour_right = "Favours control")plot_forest(data, label = "subgroup", estimate = "estimate", lcl = "lcl", ucl = "ucl", n = NULL, null = 1, xlab = "Hazard ratio (95% CI)", title = "Subgroup forest plot", subtitle = NULL, favour_left = "Favours investigational", favour_right = "Favours control")
data |
One row per subgroup or endpoint. |
label, estimate, lcl, ucl
|
Column names. |
n |
Optional sample-size column. |
null |
Null value drawn as a vertical line. |
xlab, title, subtitle, favour_left, favour_right
|
Labels. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 20) forest <- cox_hr(onc$adsl, "os_months", "os_event", "arm") plot_forest(forest)onc <- simulate_oncology(n_per_arm = 20) forest <- cox_hr(onc$adsl, "os_months", "os_event", "arm") plot_forest(forest)
Event-free curve with censoring ticks, dashed confidence limits, a log-rank p-value and the number at risk. Competing risks are not modelled.
plot_km(data, time, event, arm, xlab = "Time (months)", ylab = "Event-free probability", title = "Kaplan-Meier estimate", subtitle = NULL, conf_int = TRUE, risk_times = NULL, palette = NULL, population = "Intent-to-treat population")plot_km(data, time, event, arm, xlab = "Time (months)", ylab = "Event-free probability", title = "Kaplan-Meier estimate", subtitle = NULL, conf_int = TRUE, risk_times = NULL, palette = NULL, population = "Intent-to-treat population")
data |
Subject-level data. |
time, event, arm
|
Column names. |
xlab, ylab, title, subtitle
|
Labels. |
conf_int |
Draw pointwise confidence limits. |
risk_times |
Times for the number at risk. Default is |
palette |
Named color vector. |
population |
Analysis-population note printed in the caption. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 15) plot_km(onc$adsl, time = "os_months", event = "os_event", arm = "arm")onc <- simulate_oncology(n_per_arm = 15) plot_km(onc$adsl, time = "os_months", event = "os_event", arm = "arm")
Points above the identity line rose after baseline.
plot_lab_shift(data, baseline = "alt_base", post = "alt_max", arm = "arm", uln = NULL, title = "Baseline vs post-baseline maximum", xlab = "Baseline", ylab = "Post-baseline maximum")plot_lab_shift(data, baseline = "alt_base", post = "alt_max", arm = "arm", uln = NULL, title = "Baseline vs post-baseline maximum", xlab = "Baseline", ylab = "Post-baseline maximum")
data |
One row per subject. |
baseline, post, arm
|
Column names. |
uln |
Upper limit of normal, drawn if not NULL. |
title, xlab, ylab
|
Labels. |
A ggplot object.
saf <- simulate_safety(n_per_arm = 20) plot_lab_shift(saf$adlb, uln = 1)saf <- simulate_safety(n_per_arm = 20) plot_lab_shift(saf$adlb, uln = 1)
Percent of responders by arm, with a Wilson score interval.
plot_responder(data, responder, arm, title = "Responder rate", ylab = "Responders, percent (Wilson 95% CI)")plot_responder(data, responder, arm, title = "Responder rate", ylab = "Responders, percent (Wilson 95% CI)")
data |
Subject-level data. |
responder |
Logical or 0/1 column name. |
arm |
Arm column name. |
title, ylab
|
Labels. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 15) onc$adsl$responder <- onc$adsl$bor %in% c("CR", "PR") plot_responder(onc$adsl, responder = "responder", arm = "arm")onc <- simulate_oncology(n_per_arm = 15) onc$adsl$responder <- onc$adsl$bor %in% c("CR", "PR") plot_responder(onc$adsl, responder = "responder", arm = "arm")
Individual tumor trajectories with the arm mean overlaid.
plot_spider(data, id = "usubjid", time = "week", pct = "pct_change", arm = "arm", title = "Percent change in sum of diameters")plot_spider(data, id = "usubjid", time = "week", pct = "pct_change", arm = "arm", title = "Percent change in sum of diameters")
data |
Longitudinal data, one row per subject per visit. |
id, time, pct, arm
|
Column names. |
title |
Plot title. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 8) plot_spider(onc$adtr)onc <- simulate_oncology(n_per_arm = 8) plot_spider(onc$adtr)
One bar per subject, with points for objective response and progression. Limited to the longest durations so the figure stays readable.
plot_swimmer(data, id = "usubjid", arm = "arm", duration = "trtdur_months", response_time = "resp_months", progression_time = "pd_months", max_n = 30, xlab = "Months", title = "Time on treatment")plot_swimmer(data, id = "usubjid", arm = "arm", duration = "trtdur_months", response_time = "resp_months", progression_time = "pd_months", max_n = 30, xlab = "Months", title = "Time on treatment")
data |
Subject-level data. |
id, arm, duration
|
Column names. |
response_time, progression_time
|
Optional column names. |
max_n |
Maximum subjects, taken as the longest durations. |
xlab, title
|
Labels. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 12) plot_swimmer(onc$adsl, max_n = 10)onc <- simulate_oncology(n_per_arm = 12) plot_swimmer(onc$adsl, max_n = 10)
Bars of best percent change in the sum of diameters. Reference lines at -30 and +20 follow RECIST 1.1. Subjects with no post-baseline value are omitted.
plot_waterfall(data, pct = "pct_change", response = "bor", arm = "arm", id = "usubjid", title = "Best percent change in sum of diameters", subtitle = "RECIST 1.1 reference lines at -30% and +20%", palette = NULL)plot_waterfall(data, pct = "pct_change", response = "bor", arm = "arm", id = "usubjid", title = "Best percent change in sum of diameters", subtitle = "RECIST 1.1 reference lines at -30% and +20%", palette = NULL)
data |
Subject-level data. |
pct, response, arm, id
|
Column names. |
title, subtitle
|
Labels. |
palette |
Named colors for response categories. |
A ggplot object.
onc <- simulate_oncology(n_per_arm = 15) plot_waterfall(onc$adsl)onc <- simulate_oncology(n_per_arm = 15) plot_waterfall(onc$adsl)
Time to first MACE and systolic blood pressure. Synthetic data.
simulate_cardiovascular(n_per_arm = 220, seed = 20260316)simulate_cardiovascular(n_per_arm = 220, seed = 20260316)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and advs.
cv <- simulate_cardiovascular(n_per_arm = 20) plot_km(cv$adsl, time = "mace_months", event = "mace_event", arm = "arm")cv <- simulate_cardiovascular(n_per_arm = 20) plot_km(cv$adsl, time = "mace_months", event = "mace_event", arm = "arm")
Longitudinal HbA1c. Synthetic data.
simulate_endocrinology(n_per_arm = 150, seed = 20260317)simulate_endocrinology(n_per_arm = 150, seed = 20260317)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and adlb.
endo <- simulate_endocrinology(n_per_arm = 15) plot_change(endo$adlb)endo <- simulate_endocrinology(n_per_arm = 15) plot_change(endo$adlb)
Longitudinal DAS28. Synthetic data.
simulate_immunology(n_per_arm = 120, seed = 20260318)simulate_immunology(n_per_arm = 120, seed = 20260318)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and adeff.
imm <- simulate_immunology(n_per_arm = 15) plot_change(imm$adeff, title = "DAS28")imm <- simulate_immunology(n_per_arm = 15) plot_change(imm$adeff, title = "DAS28")
Log10 viral load and time to clearance. Synthetic data.
simulate_infectious(n_per_arm = 100, seed = 20260319)simulate_infectious(n_per_arm = 100, seed = 20260319)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and adlb.
id <- simulate_infectious(n_per_arm = 15) plot_change(id$adlb, visit = "day", xlab = "Day")id <- simulate_infectious(n_per_arm = 15) plot_change(id$adlb, visit = "day", xlab = "Day")
Longitudinal MADRS. Synthetic data.
simulate_neuroscience(n_per_arm = 140, seed = 20260320)simulate_neuroscience(n_per_arm = 140, seed = 20260320)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and adeff.
cns <- simulate_neuroscience(n_per_arm = 15) plot_change(cns$adeff, title = "MADRS")cns <- simulate_neuroscience(n_per_arm = 15) plot_change(cns$adeff, title = "MADRS")
Subject-level survival, RECIST response, and longitudinal tumor measurements. Data are synthetic. The random-number stream is restored on exit.
simulate_oncology(n_per_arm = 80, seed = 20260315)simulate_oncology(n_per_arm = 80, seed = 20260315)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl and adtr.
onc <- simulate_oncology(n_per_arm = 10) plot_waterfall(onc$adsl)onc <- simulate_oncology(n_per_arm = 10) plot_waterfall(onc$adsl)
Adverse events and peak liver tests in multiples of the upper limit of normal. Synthetic data.
simulate_safety(n_per_arm = 160, seed = 20260321)simulate_safety(n_per_arm = 160, seed = 20260321)
n_per_arm |
Subjects per arm. |
seed |
Random seed, restored after the call. |
A list with adsl, adae and adlb.
saf <- simulate_safety(n_per_arm = 20) plot_edish(saf$adlb)saf <- simulate_safety(n_per_arm = 20) plot_edish(saf$adlb)
Working catalog of figures commonly discussed in a clinical review. Not a regulatory requirement.
ta_plot_catalog()ta_plot_catalog()
A data frame with therapeutic area, display, function name and anchor.
head(ta_plot_catalog())head(ta_plot_catalog())