| Title: | Stress-Test a Pre-Specified Mixed Model for Repeated Measures |
|---|---|
| Description: | Refits one pre-specified mixed model for repeated measures (MMRM) under families of data and analytic perturbations (covariance structure, degrees-of-freedom method and variance estimator, covariate set, analysis population, alternative analysis data sets, leave-one-out deletions, crossed specification grids, re-randomisation tests, delta-adjusted and reference-based multiple imputation, and alternative estimators) and judges every refit against the pre-specified estimate, confidence interval and p-value. Classifies the result as robust, sensitive or fragile, with a separate flag for marginal significance, and estimates by simulation the probability that a planned design gives a significant and robust result. Fitting uses 'mmrm' (Sabanes Bove et al., 2024, <https://openpharma.github.io/mmrm/>) with 'SAS' 'PROC MIXED' conventions for the least-squares-mean contrast, Kenward-Roger adjustment and the corrected Akaike criterion. Reference-based multiple imputation follows Carpenter, Roger and Kenward (2013) <doi:10.1080/10543406.2013.834911> and was checked against 'rbmi' (Gower-Page, Noci and Wolbers, 2022) <doi:10.21105/joss.04251>. |
| Authors: | Adam Sullivan [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-9429-3611>), Nathan Hogan [ctb] (validation of the SAS reference results), Capricor Therapeutics [fnd] |
| Maintainer: | Adam Sullivan <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.6.1 |
| Built: | 2026-10-30 21:10:32 UTC |
| Source: | https://github.com/cran/ballast |
stress_inference()
Default inference settings for stress_inference()
default_inference()default_inference()
A named list of list(method = , vcov = ) pairs: Satterthwaite,
Kenward-Roger, linear Kenward-Roger (the SAS DDFM=KR default), residual,
sandwich with between-within degrees of freedom (SAS EMPIRICAL) and the
jackknife sandwich.
names(default_inference())names(default_inference())
Replaces subject identifiers in the labels of the leave-one-subject-out and
deletion-search rows with sequence numbers (S001, S002, ...) in the
order of the spec's subject levels, so that a census, report or page can
leave the organisation without participant identifiers. The mapping is
returned as attr(, "id_map").
mask_ids(census, spec)mask_ids(census, spec)
census |
A |
spec |
The |
The same object with masked labels.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:40), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_loo(spec)) head(mask_ids(cen, spec)$label)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:40), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_loo(spec)) head(mask_ids(cen, spec)$label)
The empirical cumulative distribution of the p-values of every counted refit, against the uniform diagonal that a result with no true effect would produce (the p-value of each refit of a null result is uniform, so the curve of a null census hugs the diagonal; a census far above it is far from random). One curve per census when several are given, with the pooled curve in black; the share below alpha is annotated.
plot_pcdf(x, alpha = NULL, pooled = TRUE)plot_pcdf(x, alpha = NULL, pooled = TRUE)
x |
A |
alpha |
Significance level; defaults to the census's own. |
pooled |
Draw the pooled curve over all censuses. |
The refits are not independent, so the curve is a description, not a test.
A ggplot object.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariance(spec, c("cs", "ar1h")), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pcdf(cen)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariance(spec, c("cs", "ar1h")), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pcdf(cen)
Draws the p-value against the parameter for every family of rows that
carries a param value (from stress_tipping() or stress_curve()): one
line per family, the alpha reference line, the anchor point (the row of
kind = "alternative", usually the unshifted analysis) emphasised, and a
dotted vertical line at each interpolated crossing with its value.
plot_pcurve( rows, alpha = NULL, xlab = "Perturbation", ylab = "p-value", log_p = FALSE )plot_pcurve( rows, alpha = NULL, xlab = "Perturbation", ylab = "p-value", log_p = FALSE )
rows |
A |
alpha |
Significance level; defaults to the rows' own |
xlab, ylab
|
Axis labels. |
log_p |
Draw the p-value axis on a log scale. |
A ggplot object.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) tp <- stress_tipping(spec, deltas = c(0, -4, -8), m = 3) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pcurve(tp)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) tp <- stress_tipping(spec, deltas = c(0, -4, -8), m = 3) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pcurve(tp)
Each dot is one refit with one unit removed, on a p-value axis (linear by default, log on request), one strip per leave-one-out family; the diamond is the pre-specified analysis with nothing removed and the dashed line is alpha. Counts of deletions below alpha label each strip.
plot_pstrip(x, alpha = NULL, log_p = FALSE, xmax = NULL, label_family = TRUE)plot_pstrip(x, alpha = NULL, log_p = FALSE, xmax = NULL, label_family = TRUE)
x |
A |
alpha |
Significance level; defaults to the census's own. |
log_p |
Draw the p-value axis on a log scale (spreads out the small p-values; harder to read for most audiences). |
xmax |
Upper limit of the linear p-value axis; |
label_family |
Include the deletion family in each strip label; set
|
A ggplot object.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_loo(spec, by = "RACE"), stress_loo(spec, by = "SEX")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pstrip(cen)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_loo(spec, by = "RACE"), stress_loo(spec, by = "SEX")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_pstrip(cen)
One row per census. Each bubble is one distinct estimate among the counted
alternative analyses (rounded to digits), with area proportional to the
number of analyses giving that value and shade by the share of those
analyses that are significant; bubbles holding five or more analyses are
numbered. A ring marks the pre-specified estimate, the shaded band holds
the central 95% of the distinct values, the heavy vertical line is no
difference, and the least favourable alternative is labelled. Deletions
are a strip of small light dots beneath the swarm.
plot_swarm( x, scale = c("percent", "estimate"), digits = 2, r0 = 0.004, favours = NULL, xlab = NULL, xlim = NULL )plot_swarm( x, scale = c("percent", "estimate"), digits = 2, r0 = 0.004, favours = NULL, xlab = NULL, xlim = NULL )
x |
A |
scale |
|
digits |
Rounding used to decide which estimates are "the same value". |
r0 |
Radius of a count-one bubble as a fraction of the x-axis range. |
favours |
Labels for the two sides of no difference, in the order (reference arm, active arm). Defaults to the treatment level names. |
xlab |
Axis label; |
xlim |
Axis limits; |
A ggplot object (uses a fixed aspect ratio so bubbles are round).
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs", "ar1h", "toep")), stress_covariates(spec), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_swarm(cen)spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs", "ar1h", "toep")), stress_covariates(spec), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot_swarm(cen)
Each counted analysis is a point at its estimate as a percent of the base estimate, filled by significance; the shaded band is the base confidence interval on the same axis and the vertical line is the base estimate. Deletions are drawn in a separate panel from alternative analyses.
## S3 method for class 'stress_census' plot(x, ...)## S3 method for class 'stress_census' plot(x, ...)
x |
A |
... |
Unused. |
A ggplot object.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariance(spec, c("cs", "ar1h")), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot(cen)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariance(spec, c("cs", "ar1h")), stress_loo(spec, by = "RACE")) if (requireNamespace("ggplot2", quietly = TRUE)) plot(cen)
Plot power and robustness power against the sample size
## S3 method for class 'stress_design' plot(x, target = 0.8, ...)## S3 method for class 'stress_design' plot(x, target = 0.8, ...)
x |
A |
target |
Reference line for the robustness power. |
... |
Ignored. |
A ggplot: the probability of a significant result and of a significant and robust result against subjects per arm, one panel per effect size.
Runs every family in a stress_plan(), assembles the census and the
verdict, and keeps the rows by family.
run_plan(plan, cores = 1L, seed = 1)run_plan(plan, cores = 1L, seed = 1)
plan |
A |
cores |
Worker processes; the cluster is started once and shared by every family. |
seed |
Seed passed to the families that take one, unless the plan sets it. |
An object of class stress_result: plan, rows (a list of
stress_rows by family), census, verdict, base and elapsed.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = list(n = 9), reference = NULL, estimator = list(estimators = "ancova"), fragility = list(max_k = 1)) res <- run_plan(plan) ressmall <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = list(n = 9), reference = NULL, estimator = list(estimators = "ancova"), fragility = list(max_k = 1)) res <- run_plan(plan) res
Run several plans (several endpoints or comparisons) and compare
run_plans(plans, cores = 1L, seed = 1)run_plans(plans, cores = 1L, seed = 1)
plans |
A named list of |
cores, seed
|
Passed to |
A named list of stress_result objects of class stress_results;
summary() gives one line per plan and plot_swarm() accepts the
censuses (lapply(results, function(r) r$census)).
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") mk <- function(s) stress_plan(s, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = NULL, permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) res <- run_plans(list(VIS4 = mk(spec), VIS3 = mk(spec_update(spec, contrast_visit = "VIS3")))) summary(res)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") mk <- function(s) stress_plan(s, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = NULL, permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) res <- run_plans(list(VIS4 = mk(spec), VIS3 = mk(spec_update(spec, contrast_visit = "VIS3")))) summary(res)
Multivariate-normal responses at n_visits post-baseline visits with an
AR(1) correlation, a baseline covariate, a treatment effect growing
linearly to delta at the last visit, and dropout after each visit whose
probability rises as the current value falls (missing at random).
simulate_trial( n_per_arm = 50, delta = 0, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, errors = c("normal", "t"), t_df = 3, hetero = 1, mnar = 0, interaction = 0, seed = NULL )simulate_trial( n_per_arm = 50, delta = 0, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, errors = c("normal", "t"), t_df = 3, hetero = 1, mnar = 0, interaction = 0, seed = NULL )
n_per_arm |
Subjects per arm. |
delta |
Treatment effect at the last visit (active minus reference). |
n_visits |
Number of post-baseline visits. |
sigma |
Residual standard deviation at each visit. |
rho |
AR(1) correlation between adjacent visits. |
dropout |
Baseline per-visit dropout probability. |
baseline_beta |
Slope of the response on the baseline covariate. |
errors |
|
t_df |
Degrees of freedom for |
hetero |
Multiplier of the residual standard deviation at the last visit relative to the first (1 = homoscedastic); the SD grows linearly. |
mnar |
Strength of missing-not-at-random dropout: dropout after a visit depends on the value the subject would have had at the NEXT visit (0 = missing at random, as the default). |
interaction |
Treatment effect modification by the baseline covariate:
the effect at the last visit is |
seed |
Random seed. |
A long data frame with id, arm ("REF", "ACT"), base,
visit ("V1", ...) and y, missing rows removed.
d <- simulate_trial(40, delta = 3, seed = 1); head(d)d <- simulate_trial(40, delta = 3, seed = 1); head(d)
Returns a copy of spec with the named settings replaced. Settings that
change the data conventions (reference, visit, treatment, subject,
response) cannot be changed here; build a new spec instead.
spec_update(spec, ...)spec_update(spec, ...)
spec |
A |
... |
Settings to replace: |
The modified stress_spec.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) spec_update(spec, covariance = "ar1h", method = "Satterthwaite", vcov = "Asymptotic")spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) spec_update(spec, covariance = "ar1h", method = "Satterthwaite", vcov = "Asymptotic")
A recommended, citable set of families with fixed settings, so that a statistical analysis plan can say "the standard stress test of ballast" and mean one thing. It contains, in order:
standard_plan( spec, site = NULL, strata = site, baseline = NULL, deltas = NULL, shift_visits = NULL, ice = NULL, m = 25, n_perm = 2000, max_k = 5 )standard_plan( spec, site = NULL, strata = site, baseline = NULL, deltas = NULL, shift_visits = NULL, ice = NULL, m = 25, n_perm = 2000, max_k = 5 )
spec |
|
site |
Optional site or centre column (deletions, random intercept,
re-randomisation strata unless |
strata |
Optional stratification column for the re-randomisation test. |
baseline |
Optional baseline column for cLDA. |
deltas |
Optional shifts for the tipping-point family. |
shift_visits |
Visits whose imputed values the tipping family shifts;
|
ice |
Optional intercurrent-event table for |
m |
Imputations for the multiple-imputation families. |
n_perm |
Permutations for the re-randomisation test. |
max_k |
Maximum removals in the deletion search. |
covariance structure: us, ar1h, ar1, cs, csh, toep, toeph;
inference: Satterthwaite, Kenward-Roger, linear Kenward-Roger, residual, sandwich with between-within df, jackknife sandwich;
covariate set: each covariate dropped, all dropped;
LS-mean weighting: equal and observed margins;
leave-one-out: every subject, and every level of site if given;
re-randomisation test: 2,000 permutations, within strata if given;
reference-based multiple imputation: MAR, J2R, CR, CIR, 25 imputations;
delta-adjusted multiple imputation over deltas if given;
estimator class: complete cases, GEE independence, ANCOVA, robust
ANCOVA, plus a random intercept for site and cLDA for baseline if
given;
greedy deletion search for the loss of significance, up to 5 subjects;
intercurrent-event strategies if ice is given (treatment policy,
hypothetical, while on treatment).
A stress_plan.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) standard_plan(spec, site = "RACE", baseline = "FEV1_BL")spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) standard_plan(spec, site = "RACE", baseline = "FEV1_BL")
Simulates trials from a planned design and runs a light stress test on
each (covariance, inference, covariate, weighting and leave-one-out
families, plus the greedy deletion search if fragility = TRUE), so that
a sponsor can see, before unblinding, how often a trial of this design
would be classed robust, sensitive or fragile at each power, and how often
the marginal flag would be set.
stress_calibrate( n_per_arm = 50, power = c(0, 0.5, 0.8, 0.95), n_trials = 100, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, errors = "normal", t_df = 3, hetero = 1, mnar = 0, interaction = 0, fragility = FALSE, cores = 1L, seed = 1 )stress_calibrate( n_per_arm = 50, power = c(0, 0.5, 0.8, 0.95), n_trials = 100, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, errors = "normal", t_df = 3, hetero = 1, mnar = 0, interaction = 0, fragility = FALSE, cores = 1L, seed = 1 )
n_per_arm |
Subjects per arm. |
power |
Target powers for the pre-specified analysis; the effect size giving each is derived from the design (0 gives the null). |
n_trials |
Trials per power. |
n_visits |
Number of post-baseline visits. |
sigma |
Residual standard deviation at each visit. |
rho |
AR(1) correlation between adjacent visits. |
dropout |
Baseline per-visit dropout probability. |
baseline_beta |
Slope of the response on the baseline covariate. |
errors, t_df, hetero, mnar, interaction
|
Misspecification settings passed to
|
fragility |
Run the deletion search (slower). |
cores |
Worker processes (the trials are distributed, so each trial's families run on one core). |
seed |
Base seed. |
A data frame of class stress_calibration with one row per power:
the effect size, observed power, and the proportion of trials with each
verdict, the marginal flag, and (if run) a one-subject fragility;
attr(, "trials") holds the per-trial rows.
stress_calibrate(n_per_arm = 20, power = c(0, 0.8), n_trials = 2)stress_calibrate(n_per_arm = 20, power = c(0, 0.8), n_trials = 2)
Binds the rows returned by the family functions and scores each against
the pre-specified (base) fit: percent of the base estimate, shift in base
standard errors, whether the estimate lies inside the base confidence
interval, keeps the base direction, is significant at alpha, and whether
significance changed. Rows that reproduce the base (is_base), did not
converge, are tipping-point searches, or duplicate an earlier row are not
counted.
stress_census(spec, ..., base = NULL)stress_census(spec, ..., base = NULL)
spec |
A |
... |
|
base |
Optional pre-computed base row, to avoid refitting. |
A data frame of class stress_census with the family rows plus
pct_of_base, shift_se, inside_ci, same_direction, significant,
crossed, duplicate (a later row numerically identical to an earlier
counted row, so the same specification reached by two families), counted;
the base row is attr(, "base").
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs", "ar1h")), stress_covariates(spec)) summary(cen) stress_verdict(cen)spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs", "ar1h")), stress_covariates(spec)) summary(cen) stress_verdict(cen)
Refits the pre-specified model under each covariance structure. The row that
reproduces the spec's own structure is flagged is_base.
stress_covariance( spec, structures = c("us", "ar1h", "ar1", "cs", "csh", "toep", "toeph"), cores = 1L )stress_covariance( spec, structures = c("us", "ar1h", "ar1", "cs", "csh", "toep", "toeph"), cores = 1L )
spec |
|
structures |
Covariance structure names (see |
cores |
Number of worker processes; |
A stress_rows data frame, one row per structure.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_covariance(spec, c("us", "cs", "ar1h"))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_covariance(spec, c("us", "cs", "ar1h"))
Refits the model with each covariate dropped in turn, with no covariates, and with any user-supplied covariate sets.
stress_covariates( spec, drop_each = TRUE, drop_all = TRUE, sets = list(), cores = 1L )stress_covariates( spec, drop_each = TRUE, drop_all = TRUE, sets = list(), cores = 1L )
spec |
|
drop_each |
Drop each pre-specified covariate term in turn. |
drop_all |
Fit with treatment and visit only. |
sets |
Named list of additional covariate sets (formulas or character vectors of term labels); each is fitted as given. |
cores |
Number of worker processes; |
A stress_rows data frame.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_covariates(spec, sets = list("with weight" = ~ RACE + SEX + FEV1_BL + WEIGHT))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_covariates(spec, sets = list("with weight" = ~ RACE + SEX + FEV1_BL + WEIGHT))
Applies a user-supplied modification to the spec's data at each value of a
continuous parameter, refits the pre-specified model and returns one row
per value with the value in param. This generalises tipping-point
searches that are not multiple-imputation based: assigning a contested
participant a post-deviation trajectory, shifting one arm's observed
values, moving a baseline definition, and so on. Rows are
kind = "search" except the row at anchor, which is a counted
alternative.
stress_curve( spec, values, modify, family = "perturbation curve", anchor = NULL, role = "sensitivity", cores = 1L )stress_curve( spec, values, modify, family = "perturbation curve", anchor = NULL, role = "sensitivity", cores = 1L )
spec |
|
values |
Numeric vector of parameter values. |
modify |
A function |
family |
Family label for the rows. |
anchor |
The parameter value (if any) that represents an alternative
analysis in its own right rather than a search point; |
role |
|
cores |
Number of worker processes; |
A stress_rows data frame, one row per value, with param set.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) # shift every observed value of one subject on the active arm shift_pt3 <- function(d, v) { k <- d$USUBJID == "PT3"; d$FEV1[k] <- d$FEV1[k] + v; d } cv <- stress_curve(spec, values = c(-20, -10, 0, 10, 20), modify = shift_pt3, anchor = 0) tipping_point(cv)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) # shift every observed value of one subject on the active arm shift_pt3 <- function(d, v) { k <- d$USUBJID == "PT3"; d$FEV1[k] <- d$FEV1[k] + v; d } cv <- stress_curve(spec, values = c(-20, -10, 0, 10, 20), modify = shift_pt3, anchor = 0) tipping_point(cv)
The generic data-decision family. Each alternative is a data frame in the spec's layout (an intercurrent-event handling, a different baseline definition, a contested record reversed, a different visit window). The pre-specified model is refitted on each.
stress_data( spec, alternatives, family = "data handling", role = "supplementary", cores = 1L )stress_data( spec, alternatives, family = "data handling", role = "supplementary", cores = 1L )
spec |
|
alternatives |
Named list of data frames. |
family |
Family label for the rows. |
role |
|
cores |
Number of worker processes; |
A stress_rows data frame, one row per alternative.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) alt <- mmrm::fev_data alt$FEV1[alt$USUBJID == "PT1"] <- NA # one subject's records set missing stress_data(spec, list("PT1 removed" = alt))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) alt <- mmrm::fev_data alt$FEV1[alt$USUBJID == "PT1"] <- NA # one subject's records set missing stress_data(spec, list("PT1 removed" = alt))
Simulates trials from a planned design over a grid of sample sizes and effect sizes, runs the light stress test on each (covariance, inference, covariate, weighting and leave-one-subject-out families) and reports, for every cell, the conventional power next to the robustness power: the probability that the pre-specified analysis is significant and classed robust. The deletion search and a delta-adjusted tipping grid are optional and give the expected fragility index and the shift the result can absorb. This is the design-stage use of the stress test: choose the sample size so that a true effect is expected to come out robust rather than only significant, and write the expected verdict rates into the statistical analysis plan before unblinding so that the observed verdict has a pre-registered comparison.
stress_design( n_per_arm, delta, n_trials = 100, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, covariance = "us", method = "Satterthwaite", vcov = "Asymptotic", alpha = 0.05, alternatives = c("us", "ar1h", "cs"), loo = TRUE, fragility = FALSE, max_k = 5, tipping = FALSE, shifts = seq(0, 2, by = 0.25), m = 5, errors = "normal", t_df = 3, hetero = 1, mnar = 0, interaction = 0, cores = 1L, seed = 1 ) ## S3 method for class 'stress_design' print(x, target = 0.8, ...)stress_design( n_per_arm, delta, n_trials = 100, n_visits = 4, sigma = 10, rho = 0.6, dropout = 0.08, baseline_beta = 0.5, covariance = "us", method = "Satterthwaite", vcov = "Asymptotic", alpha = 0.05, alternatives = c("us", "ar1h", "cs"), loo = TRUE, fragility = FALSE, max_k = 5, tipping = FALSE, shifts = seq(0, 2, by = 0.25), m = 5, errors = "normal", t_df = 3, hetero = 1, mnar = 0, interaction = 0, cores = 1L, seed = 1 ) ## S3 method for class 'stress_design' print(x, target = 0.8, ...)
n_per_arm |
Subjects per arm to evaluate (a vector). |
delta |
Treatment effects at the last visit to evaluate (a vector); include 0 to see the null. |
n_trials |
Trials per cell. |
n_visits |
Number of post-baseline visits. |
sigma |
Residual standard deviation at each visit. |
rho |
AR(1) correlation between adjacent visits. |
dropout |
Baseline per-visit dropout probability. |
baseline_beta |
Slope of the response on the baseline covariate. |
covariance, method, vcov
|
The pre-specified analysis model, as in
|
alpha |
Two-sided significance level. |
alternatives |
Covariance structures offered as alternatives; the pre-specified one is removed from the set. |
loo |
Run the leave-one-subject-out family. |
fragility |
Run the greedy deletion search for significance up to
|
max_k |
Removals allowed in the deletion search. |
tipping |
Run a delta-adjusted tipping grid (imputed active-arm values
shifted down) and record the shift at which |
shifts |
Shifts for the tipping grid as multiples of |
m |
Imputations per shift. |
errors |
|
t_df |
Degrees of freedom for |
hetero |
Multiplier of the residual standard deviation at the last visit relative to the first (1 = homoscedastic); the SD grows linearly. |
mnar |
Strength of missing-not-at-random dropout: dropout after a visit depends on the value the subject would have had at the NEXT visit (0 = missing at random, as the default). |
interaction |
Treatment effect modification by the baseline covariate:
the effect at the last visit is |
cores |
Worker processes. Trials are distributed, so each trial's families run on one core. |
seed |
Base seed; every trial's seed is derived from it. |
x |
A |
target |
Robustness power to reach; the print method names the smallest sample size per arm that reaches it for each non-zero effect. |
... |
Ignored. |
The light family set offers the alternative covariance structures, three
inference methods, the model without the baseline covariate, observed-margin
weighting and, if loo = TRUE, every single-subject deletion. Deletions
dominate the run time at large sample sizes; loo = FALSE gives a fast
first pass whose verdicts omit the deletion family.
A data frame of class stress_design with one row per cell:
n_per_arm, delta, trials, power (proportion significant),
robustness_power (significant and robust), robust_given_sig,
sensitive_given_sig, fragile_given_sig, marginal_given_sig,
k1_given_sig (one deletion loses significance), k_reached_given_sig
and k_median (deletion search, if run), tipping_margin (median shift
that crosses alpha among significant trials, as a multiple of delta
or sigma) and tipping_absorbed (share of significant trials with no
crossing within the grid), and max_shift_se (median largest shift in
pre-specified standard errors). attr(, "trials") holds the per-trial
rows and attr(, "design") the settings. plot.stress_design() draws
power and robustness power against the sample size.
stress_calibrate() for the verdict rates of one design at a set
of planned powers; stress_sap() for the analysis-plan text.
des <- stress_design(n_per_arm = c(40, 80), delta = c(0, 5), n_trials = 4, loo = FALSE) desdes <- stress_design(n_per_arm = c(40, 80), delta = c(0, 5), n_trials = 4, loo = FALSE) des
Declares the intercurrent events that occurred and derives the analysis data set implied by each handling strategy, then refits the pre-specified model on each. Strategies per event type:
stress_estimand( spec, ice, strategies = c("treatment_policy", "hypothetical", "while_on_treatment", "composite"), composite_value = NULL, cores = 1L )stress_estimand( spec, ice, strategies = c("treatment_policy", "hypothetical", "while_on_treatment", "composite"), composite_value = NULL, cores = 1L )
spec |
|
ice |
A data frame with one row per intercurrent event: the subject
identifier (column named as the spec's |
strategies |
Named list, one element per event type, each a character vector of strategies to apply; or a single character vector applied to every type. |
composite_value |
A number or function used by the composite strategy. |
cores |
Number of worker processes; |
"treatment_policy": use the data as observed, whatever happened.
"hypothetical": set the responses at and after the event to missing;
the model's missing-at-random assumption then supplies the value the
subject would have had without the event.
"while_on_treatment": drop the records at and after the event.
"composite": replace the responses at and after the event with an
unfavourable value: a number, or a function function(data, rows)
returning one value per affected row (the worst observed change in the
subject's arm at that visit, say).
With several event types the strategies are crossed, one row per
combination, and the row that applies "treatment_policy" to every type
reproduces the pre-specified data and is flagged is_base. Every other row
targets a different estimand, so they are role = "supplementary"; a
hypothetical strategy can be followed by stress_reference() on the
derived data for its reference-based sensitivity analyses.
A stress_rows data frame, one row per strategy combination;
attr(, "data") holds the derived data sets by label.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") ice <- data.frame(USUBJID = c("PT2", "PT5", "PT9"), AVISIT = c("VIS3", "VIS2", "VIS4"), type = c("discontinuation", "discontinuation", "rescue")) strat <- list(discontinuation = c("treatment_policy", "hypothetical"), rescue = c("treatment_policy", "composite")) stress_estimand(spec, ice, strategies = strat, composite_value = 20)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") ice <- data.frame(USUBJID = c("PT2", "PT5", "PT9"), AVISIT = c("VIS3", "VIS2", "VIS4"), type = c("discontinuation", "discontinuation", "rescue")) strat <- list(discontinuation = c("treatment_policy", "hypothetical"), rescue = c("treatment_policy", "composite")) stress_estimand(spec, ice, strategies = strat, composite_value = 20)
Refits the question with estimators other than the pre-specified MMRM:
stress_estimator( spec, estimators = c("complete_cases", "gee_independence", "ancova", "robust_ancova", "random_intercept", "clda"), random = NULL, baseline = NULL, seed = 1, cores = 1L )stress_estimator( spec, estimators = c("complete_cases", "gee_independence", "ancova", "robust_ancova", "random_intercept", "clda"), random = NULL, baseline = NULL, seed = 1, cores = 1L )
spec |
|
estimators |
Which estimators to run. |
random |
Name of a grouping column for |
baseline |
Name of the baseline column for |
seed |
Seed for the robust regression's random initial estimate. |
cores |
Number of worker processes; |
"complete_cases": the pre-specified MMRM on subjects observed at every
visit (no reliance on missing-at-random within the likelihood).
"gee_independence": working-independence weighted least squares with a
visit-specific residual variance and a cluster-robust (sandwich)
variance, between-within degrees of freedom; no within-subject
covariance is modelled. Needs nlme.
"ancova": analysis of covariance at the contrast visit, observed cases,
residual degrees of freedom. Covariate terms involving the visit variable
are dropped.
"robust_ancova": MM-type robust regression at the contrast visit
(MASS::rlm), normal-theory inference. Needs MASS.
"clda": constrained longitudinal data analysis: the raw outcome
(baseline plus the response, which must be a change from baseline) is
modelled at baseline and every post-baseline visit with one common
baseline mean across arms, an unstructured covariance over all visits and
the other covariates as fixed effects; the contrast is the difference in
the arms' means at the contrast visit. Needs baseline.
"random_intercept": the pre-specified fixed effects with random as a
random intercept instead of a fixed covariate, the spec's covariance
structure within subject where nlme has an equivalent (us, ar1h,
ar1, cs, csh); normal-theory inference. Needs nlme and random.
Every estimator reports the same least-squares-mean contrast at the contrast visit (class covariates at equal weights, continuous covariates at their means).
A stress_rows data frame, one row per estimator.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_estimator(spec, c("complete_cases", "ancova"))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_estimator(spec, c("complete_cases", "ancova"))
For one row of a census (an alternative that crossed alpha, say), attributes the difference between its estimate and the pre-specified estimate to subjects. Each subject's influence is the change in the contrast when that subject is removed, computed under the pre-specified settings and again under the alternative's settings; the difference of the two influences says how much more (or less) that subject drives the result under the alternative. Subjects are ranked by the absolute difference.
stress_explain(spec, row, data = NULL, top = 10, cores = 1L)stress_explain(spec, row, data = NULL, top = 10, cores = 1L)
spec |
|
row |
One row of a |
data |
The alternative's data set, if it differs from the spec's. |
top |
Number of subjects to print. |
cores |
Number of worker processes; |
The alternative's model settings are read from the row (covariance,
method, vcov, covariates). For an alternative that used different
data, pass that data as data.
A data frame of class stress_explanation with one row per
subject: subject, arm, n_visits, infl_base, infl_alt,
difference, sorted by absolute difference; the row explained is
attr(, "row") and the two full-data estimates are attr(, "estimates").
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:30), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariates(spec)) stress_explain(spec, cen[1, ], top = 5)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:30), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") cen <- stress_census(spec, stress_covariates(spec)) stress_explain(spec, cen[1, ], top = 5)
Fits the model described by a stress_spec() (optionally on alternative
data or with alternative settings) and returns the treatment contrast at the
contrast visit as a single row in the package's common schema. This is the
engine every family function calls.
stress_fit( spec, data = NULL, label = spec$label, family = "base", kind = "alternative", is_base = is.null(data), note = "", covariates = spec$covariates, covariance = spec$covariance, method = spec$method, vcov = spec$vcov, at = list(), role = "sensitivity", weights = spec$weights )stress_fit( spec, data = NULL, label = spec$label, family = "base", kind = "alternative", is_base = is.null(data), note = "", covariates = spec$covariates, covariance = spec$covariance, method = spec$method, vcov = spec$vcov, at = list(), role = "sensitivity", weights = spec$weights )
spec |
A |
data |
Alternative data set in the same layout as |
label, family, kind
|
Labels for the output row. |
is_base |
|
note |
Free text carried into the row. |
covariates, covariance, method, vcov
|
Overrides of the spec's settings. |
at |
Named list of continuous covariates fixed at a value instead of
their mean when forming the least-squares means (SAS |
role |
|
weights |
LS-mean weighting, |
A one-row data frame of class stress_rows with columns family,
label, kind, role, is_base, converged, note, estimate, stderr,
df, lcl, ucl, p, n_subj, n_obs, aicc, param (the numeric value of a
continuous perturbation, NA otherwise), covariance, method, vcov,
covariates, alpha.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_fit(spec)spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_fit(spec)
A greedy forward search: at each step every remaining subject is removed
in turn from the current data, the pre-specified model is refitted, and
the subject whose removal moves the result furthest towards the target is
dropped for good. The search stops when the target is reached or after
max_k steps. The number of steps needed is a continuous-outcome analogue
of the fragility index; because the search is greedy it is an upper bound
on the true minimum.
stress_fragility( spec, target = c("significance", "ci", "direction"), max_k = 5, candidates = NULL, cores = 1L )stress_fragility( spec, target = c("significance", "ci", "direction"), max_k = 5, candidates = NULL, cores = 1L )
spec |
|
target |
What has to change: |
max_k |
Maximum number of subjects to remove. |
candidates |
Optional character vector of subject identifiers to restrict the search to (one arm, say). |
cores |
Number of worker processes; |
A stress_rows data frame with one row per step
(kind = "search", so the rows are not counted in a census, since the
search crosses the target by construction; param = number removed,
label = the subjects removed so far). attr(, "fragility") is a list with target, k (the
step at which the target was reached, NA if never), subjects and
reached.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") fr <- stress_fragility(spec, max_k = 2) attr(fr, "fragility")small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") fr <- stress_fragility(spec, max_k = 2) attr(fr, "fragility")
Fits every combination of the supplied covariance structures, inference
settings, covariate sets and data sets (a specification curve). The cell
that reproduces the spec is flagged is_base.
stress_grid( spec, covariance = spec$covariance, inference = list(base = list(method = spec$method, vcov = spec$vcov)), covariates = list(base = spec$covariates), data = list(base = NULL), cores = 1L )stress_grid( spec, covariance = spec$covariance, inference = list(base = list(method = spec$method, vcov = spec$vcov)), covariates = list(base = spec$covariates), data = list(base = NULL), cores = 1L )
spec |
|
covariance |
Character vector of covariance structures. |
inference |
Named list of |
covariates |
Named list of covariate sets (formulas or term labels). |
data |
Named list of data frames; a |
cores |
Number of worker processes; |
A stress_rows data frame, one row per cell, labelled
"covariance | inference | covariates | data".
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_grid(spec, covariance = c("us", "cs"), covariates = list(full = ~ RACE + SEX + FEV1_BL, baseline = ~ FEV1_BL))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_grid(spec, covariance = c("us", "cs"), covariates = list(full = ~ RACE + SEX + FEV1_BL, baseline = ~ FEV1_BL))
Writes an HTML page (its JavaScript dependencies in a lib folder beside
it) with the verdict, the summary table and two interactive figures built
with plotly: the bubble swarm, where
hovering a bubble lists the specifications that give that estimate, and
the deletion strip, where hovering a dot names the subject removed and
shows the refit's estimate and p-value. Needs plotly and htmlwidgets.
stress_html(result, file, digits = 2, mask_ids = TRUE)stress_html(result, file, digits = 2, mask_ids = TRUE)
result |
A |
file |
Path of the HTML file to write. There is no default, so nothing is written unless a path is given. |
digits |
Rounding used to group estimates into bubbles. |
mask_ids |
Replace subject identifiers with sequence numbers (on by default: the page is the deliverable most likely to leave the organisation). |
The file path, invisibly.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) if (requireNamespace("plotly", quietly = TRUE) && requireNamespace("htmlwidgets", quietly = TRUE)) stress_html(run_plan(plan), file.path(tempdir(), "stress.html"))small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) if (requireNamespace("plotly", quietly = TRUE) && requireNamespace("htmlwidgets", quietly = TRUE)) stress_html(run_plan(plan), file.path(tempdir(), "stress.html"))
Perturbation family: degrees-of-freedom method and variance estimator
stress_inference(spec, settings = default_inference(), cores = 1L)stress_inference(spec, settings = default_inference(), cores = 1L)
spec |
|
settings |
Named list of |
cores |
Number of worker processes; |
A stress_rows data frame, one row per setting.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_inference(spec, default_inference()[c("Satterthwaite", "Kenward-Roger (linear)")])spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_inference(spec, default_inference()[c("Satterthwaite", "Kenward-Roger (linear)")])
Records the plan text and its MD5 hash, the seed, the package and R
versions, the platform, the elapsed time and the verdict, as JSON if
jsonlite is installed and as plain text otherwise. The hash of the plan
text lets a reviewer confirm that the stress test run is the one that was
pre-specified.
stress_log(result, file = NULL, seed = 1)stress_log(result, file = NULL, seed = 1)
result |
A |
file |
Path to write; |
seed |
The seed that was passed to |
The log as a list, invisibly.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = NULL, permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) str(stress_log(run_plan(plan)))small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = NULL, permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) str(stress_log(run_plan(plan)))
Refits the model with each subject removed, or with each level of a
grouping variable (site, cohort, country) removed. Rows are kind = "deletion" and are summarised separately from alternative analyses.
stress_loo(spec, by = NULL, cores = 1L)stress_loo(spec, by = NULL, cores = 1L)
spec |
|
by |
Name of a grouping column; |
cores |
Number of worker processes; |
A stress_rows data frame, one row per deleted unit.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:40), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_loo(spec, by = "RACE")small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:40), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_loo(spec, by = "RACE")
Permutes treatment labels across subjects, optionally within strata, refits
the pre-specified model each time and returns the permutation p-value for
the observed contrast. The returned row carries the observed estimate and
the permutation p; the null distribution is attached as attr(, "null").
stress_permute( spec, n = 1000, strata = NULL, statistic = c("estimate", "t"), seed = 1, cores = 1L )stress_permute( spec, n = 1000, strata = NULL, statistic = c("estimate", "t"), seed = 1, cores = 1L )
spec |
|
n |
Number of permutations. |
strata |
Name of a subject-level column to permute within; |
statistic |
|
seed |
Random seed. |
cores |
Number of worker processes; |
A one-row stress_rows data frame with p equal to
(1 + number of |null| >= |observed|) / (n + 1).
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_permute(spec, n = 19)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_permute(spec, n = 19)
A plan names the families to run and their settings before unblinding, so
the stress test can be written into a statistical analysis plan and run
with one call (run_plan()). Each family argument is NULL to skip the
family, an empty list() to run it with its defaults, or a named list of
arguments for the family function. Printing the plan gives the text a
statistical analysis plan can carry.
stress_plan( spec, covariance = list(), inference = list(), covariates = list(), weights = TRUE, population = NULL, data = NULL, loo = list(), grid = NULL, permute = list(n = 2000), tipping = NULL, reference = list(), estimator = list(), fragility = list(), curve = NULL, estimand = NULL, visits = NULL, title = "Pre-specified stress test" )stress_plan( spec, covariance = list(), inference = list(), covariates = list(), weights = TRUE, population = NULL, data = NULL, loo = list(), grid = NULL, permute = list(n = 2000), tipping = NULL, reference = list(), estimator = list(), fragility = list(), curve = NULL, estimand = NULL, visits = NULL, title = "Pre-specified stress test" )
spec |
A |
covariance |
Arguments for |
inference |
Arguments for |
covariates |
Arguments for |
weights |
|
population |
Arguments for |
data |
Arguments for |
loo |
Arguments for |
grid |
Arguments for |
permute |
Arguments for |
tipping |
Arguments for |
reference |
Arguments for |
estimator |
Arguments for |
fragility |
Arguments for |
curve |
Arguments for |
estimand |
Arguments for |
visits |
Arguments for |
title |
A title for the plan. |
An object of class stress_plan.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) plan <- stress_plan(spec, covariance = list(structures = c("us", "cs", "ar1h")), inference = list(), covariates = list(), loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = list(estimators = "ancova"), fragility = list(max_k = 2)) planspec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) plan <- stress_plan(spec, covariance = list(structures = c("us", "cs", "ar1h")), inference = list(), covariates = list(), loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = list(estimators = "ancova"), fragility = list(max_k = 2)) plan
Each rule is a function of the spec's data returning a logical vector of rows to keep (a per-protocol flag, a completer definition, a cohort).
stress_population(spec, rules, role = "supplementary", cores = 1L)stress_population(spec, rules, role = "supplementary", cores = 1L)
spec |
|
rules |
Named list of functions |
role |
|
cores |
Number of worker processes; |
A stress_rows data frame, one row per rule.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_population(spec, list("female" = function(d) d$SEX == "Female"))spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_population(spec, list("female" = function(d) d$SEX == "Female"))
Model-free tests of the treatment difference in the response at the
contrast visit: the Wilcoxon rank-sum test (normal approximation with the
tie correction and continuity correction, as SAS PROC NPAR1WAY reports
it) and, if strata is given, the van Elteren test (the Cochran-Mantel-
Haenszel row-mean-score statistic on modified ridit scores within strata).
The rows carry a p-value and the Hodges-Lehmann shift as the estimate, with
no standard error or interval; they count towards significance only.
stress_ranktest(spec, strata = NULL, cores = 1L)stress_ranktest(spec, strata = NULL, cores = 1L)
spec |
|
strata |
Name of a subject-level stratification column for the van
Elteren test; |
cores |
Number of worker processes; |
A stress_rows data frame with one or two rows.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_ranktest(spec, strata = "RACE")spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_ranktest(spec, strata = "RACE")
Imputes the missing responses from a multivariate-normal imputation model (an MMRM on the observed data with treatment by visit, the subject-level covariates and an unstructured covariance) under the standard reference-based assumptions (Carpenter, Roger and Kenward, 2013), refits the pre-specified model on each completed data set and pools with Rubin's rules:
stress_reference( spec, methods = c("MAR", "J2R", "CR", "CIR"), m = 25, seed = 1, delta = 0, arm = NULL, impute_covariates = NULL, same_cov = TRUE, max_tries = 3L, cores = 1L )stress_reference( spec, methods = c("MAR", "J2R", "CR", "CIR"), m = 25, seed = 1, delta = 0, arm = NULL, impute_covariates = NULL, same_cov = TRUE, max_tries = 3L, cores = 1L )
spec |
|
methods |
Assumptions to run, from |
m |
Number of imputations. |
seed |
Random seed (the same seed is used for every delta). |
delta |
Shift added to the active arm's imputed values (response
units); |
arm |
Treatment level receiving the shift; defaults to the active arm. |
impute_covariates |
Subject-level covariates used as predictors in the
imputation model (with treatment and the visit columns); |
same_cov |
If |
max_tries |
Bootstrap refits attempted per imputation before giving up. |
cores |
Number of worker processes; |
MAR: every subject's missing values follow their own arm.
J2R (jump to reference): an active-arm subject's visits after the last
observed one take the reference arm's means.
CR (copy reference): an active-arm subject with any missing value
follows the reference arm's means at every visit.
CIR (copy increments in reference): after the last observed visit the
subject keeps their own-arm mean at that visit and then changes as the
reference arm does.
Visits missing before the last observed one are treated as missing at
random under every assumption. Parameter uncertainty is carried by
approximate Bayesian draws: for each imputation the imputation model is
refitted on a bootstrap sample of subjects (stratified by arm) and its
fixed effects and covariance are used to draw the missing values from the
conditional normal distribution given the subject's observed values.
An optional delta is added to every imputed value of the active arm.
Imputation i uses seed seed + i on whichever worker draws it, so
the result is the same for any number of cores.
A stress_rows data frame, one counted row per method
(role = "sensitivity"), with the number of imputations and the Monte
Carlo standard error in note.
Carpenter JR, Roger JH, Kenward MG (2013). Analysis of longitudinal trials with protocol deviation: a framework for relevant, accessible assumptions, and inference via multiple imputation. Journal of Biopharmaceutical Statistics 23, 1352-1371.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:100), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") stress_reference(spec, methods = c("MAR", "J2R"), m = 3)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:100), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") stress_reference(spec, methods = c("MAR", "J2R"), m = 3)
Writes a Markdown report with the plan, the base analysis, the per-family summary table, the verdict and the standard figures (census dot plot, bubble swarm, deletion p-value strip, p-value curves where the result has rows with a parameter) as PNG files beside it.
stress_report( result, dir, name = "stress_report", width = 9, height = 5, dpi = 150, mask_ids = FALSE )stress_report( result, dir, name = "stress_report", width = 9, height = 5, dpi = 150, mask_ids = FALSE )
result |
A |
dir |
Directory to write into (created if needed). There is no default, so nothing is written unless a location is given. |
name |
File stem for the report and figures. |
width, height, dpi
|
Figure size in inches and resolution. |
mask_ids |
Replace subject identifiers with sequence numbers. |
The path of the Markdown file, invisibly.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) stress_report(run_plan(plan), dir = tempdir())small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) stress_report(run_plan(plan), dir = tempdir())
Returns the paragraphs a statistical analysis plan can carry to
pre-specify a stress test: the purpose, the pre-specified analysis, the
families and their settings (from the plan), the counting rule, the
classification, what will be reported, and the plan's MD5 hash so the run
after unblinding can be shown to match what was pre-specified. The same
text is installed as a template at
system.file("templates", "sap_stress_test.md", package = "ballast").
stress_sap(plan, file = NULL, trial = "the study")stress_sap(plan, file = NULL, trial = "the study")
plan |
|
file |
Optional path to write the text to (Markdown). |
trial |
A short name for the trial, used in the text. |
A character vector of paragraphs, invisibly if file is given.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cat(stress_sap(standard_plan(spec, site = "RACE"), trial = "Study 001"), sep = "\n\n")spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cat(stress_sap(standard_plan(spec, site = "RACE"), trial = "Study 001"), sep = "\n\n")
A stress_spec holds the analysis data and every decision of the
pre-specified mixed model for repeated measures: the response, subject,
visit and treatment variables, the covariates, the visit at which the
treatment contrast is taken, the within-subject covariance structure, the
degrees-of-freedom method, the variance estimator and the significance
level. Every family function in the package takes a spec and returns rows in
the schema of stress_fit().
stress_spec( data, response, subject, visit, treatment, reference = NULL, active = NULL, covariates = NULL, contrast_visit = NULL, covariance = "us", method = "Kenward-Roger", vcov = "Kenward-Roger-Linear", alpha = 0.05, weights = c("equal", "observed"), label = "pre-specified", sas_conventions = TRUE, time = NULL )stress_spec( data, response, subject, visit, treatment, reference = NULL, active = NULL, covariates = NULL, contrast_visit = NULL, covariance = "us", method = "Kenward-Roger", vcov = "Kenward-Roger-Linear", alpha = 0.05, weights = c("equal", "observed"), label = "pre-specified", sas_conventions = TRUE, time = NULL )
data |
A data frame in long format, one row per subject and visit. |
response |
Name of the response column (change from baseline, say). |
subject |
Name of the subject identifier column. |
visit |
Name of the visit column; coerced to a factor whose level order is the visit order. |
treatment |
Name of the treatment column (two or more arms). |
reference |
The reference (control) level of |
active |
The treatment level compared with the reference. Defaults to
the first non-reference level. With three or more arms, change it with
|
covariates |
Fixed-effect covariates besides treatment and visit, as a
one-sided formula ( |
contrast_visit |
The visit level at which the treatment contrast is estimated. Defaults to the last visit level. |
covariance |
Covariance structure name as used by |
method |
Degrees-of-freedom method passed to |
vcov |
Variance estimator passed to |
alpha |
Two-sided significance level. |
weights |
How class covariates are weighted in the least-squares means:
|
label |
Label for the base analysis in output rows. |
sas_conventions |
If |
time |
Name of a numeric time column for the |
The fixed effects are covariates + treatment + visit + treatment:visit;
the treatment contrast at contrast_visit is the difference of
least-squares means (active minus reference) with class covariates weighted
equally over their levels and continuous covariates at their means, the
convention of SAS LSMEANS.
An object of class stress_spec.
spec <- stress_spec(mmrm::fev_data, response = "FEV1", subject = "USUBJID", visit = "AVISIT", treatment = "ARMCD", reference = "PBO", covariates = ~ RACE + SEX + FEV1_BL, contrast_visit = "VIS4") specspec <- stress_spec(mmrm::fev_data, response = "FEV1", subject = "USUBJID", visit = "AVISIT", treatment = "ARMCD", reference = "PBO", covariates = ~ RACE + SEX + FEV1_BL, contrast_visit = "VIS4") spec
Subsets a CDISC ADaM BDS data set (ADEFF, ADQS and the like) to one parameter, the analysis-flagged post-baseline records, orders the visits by their numeric code and builds the spec with the baseline value as a covariate by default.
stress_spec_adam( bds, paramcd, response = "CHG", subject = "USUBJID", visit = "AVISIT", visitn = "AVISITN", treatment = "TRTP", reference = NULL, active = NULL, covariates = ~BASE, contrast_visit = NULL, analysis_flag = "ANL01FL", drop_baseline = TRUE, filter = NULL, covariance = "us", method = "Kenward-Roger", vcov = "Kenward-Roger-Linear", alpha = 0.05, weights = "equal", label = paste(paramcd, response) )stress_spec_adam( bds, paramcd, response = "CHG", subject = "USUBJID", visit = "AVISIT", visitn = "AVISITN", treatment = "TRTP", reference = NULL, active = NULL, covariates = ~BASE, contrast_visit = NULL, analysis_flag = "ANL01FL", drop_baseline = TRUE, filter = NULL, covariance = "us", method = "Kenward-Roger", vcov = "Kenward-Roger-Linear", alpha = 0.05, weights = "equal", label = paste(paramcd, response) )
bds |
A BDS data frame. |
paramcd |
The |
response |
Response column, usually |
subject, visit, visitn, treatment
|
Column names ( |
reference, active, contrast_visit, covariance, method, vcov, alpha, weights, label
|
As in |
covariates |
Covariates; the default is the baseline value |
analysis_flag |
Name of a flag column kept where equal to |
drop_baseline |
Drop records flagged |
filter |
Optional function of the data returning the rows to keep, applied after the other subsets (an analysis-phase restriction, say). |
A stress_spec.
bds <- transform(mmrm::fev_data, PARAMCD = "FEV1", CHG = FEV1 - FEV1_BL, BASE = FEV1_BL, AVISITN = VISITN, TRTP = ARMCD, ANL01FL = "Y") stress_spec_adam(bds, "FEV1", response = "CHG", visit = "AVISIT", reference = "PBO")bds <- transform(mmrm::fev_data, PARAMCD = "FEV1", CHG = FEV1 - FEV1_BL, BASE = FEV1_BL, AVISITN = VISITN, TRTP = ARMCD, ANL01FL = "Y") stress_spec_adam(bds, "FEV1", response = "CHG", visit = "AVISIT", reference = "PBO")
Takes the data, response, subject, visit, covariance structure,
degrees-of-freedom method and variance estimator from an mmrm fit.
The treatment variable is the factor that interacts with the visit
variable unless given.
stress_spec_mmrm( fit, treatment = NULL, reference = NULL, active = NULL, contrast_visit = NULL, alpha = 0.05, weights = "equal", label = "pre-specified", sas_conventions = TRUE )stress_spec_mmrm( fit, treatment = NULL, reference = NULL, active = NULL, contrast_visit = NULL, alpha = 0.05, weights = "equal", label = "pre-specified", sas_conventions = TRUE )
fit |
An object of class |
treatment |
Name of the treatment column; |
reference, active, contrast_visit, alpha, weights, label, sas_conventions
|
As in |
A stress_spec.
fit <- mmrm::mmrm(FEV1 ~ RACE + SEX + FEV1_BL + ARMCD * AVISIT + us(AVISIT | USUBJID), data = mmrm::fev_data) stress_spec_mmrm(fit, reference = "PBO")fit <- mmrm::mmrm(FEV1 ~ RACE + SEX + FEV1_BL + ARMCD * AVISIT + us(AVISIT | USUBJID), data = mmrm::fev_data) stress_spec_mmrm(fit, reference = "PBO")
Writes the per-family summary, the rows that crossed alpha or left the
interval, the deletion-search path and the full census. CSV files are
always written; with flextable and officer installed, a Word document
with the tables in a standard clinical-report layout is written too, and
an RTF version of each table.
stress_tables( result, dir, name = "stress", formats = c("csv", "docx", "rtf"), digits = 3, mask_ids = FALSE )stress_tables( result, dir, name = "stress", formats = c("csv", "docx", "rtf"), digits = 3, mask_ids = FALSE )
result |
A |
dir |
Directory to write into (created if needed). There is no default, so nothing is written unless a location is given. |
name |
File stem. |
formats |
Any of |
digits |
Decimal places in the formatted tables. |
mask_ids |
Replace subject identifiers with sequence numbers. |
The paths written, invisibly.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) stress_tables(run_plan(plan), dir = tempdir(), formats = "csv")small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL, covariance = "cs", method = "Satterthwaite", vcov = "Asymptotic") plan <- stress_plan(spec, covariance = list(structures = c("cs", "ar1h")), inference = NULL, covariates = NULL, weights = FALSE, loo = list(by = "RACE"), permute = NULL, reference = NULL, estimator = NULL, fragility = NULL) stress_tables(run_plan(plan), dir = tempdir(), formats = "csv")
Imputes the missing responses by fully conditional specification
(regression method, one column per visit, subject-level covariates and
treatment as predictors), adds delta to every imputed value of the active
arm, refits the pre-specified model on each completed data set and pools
with Rubin's rules. delta = 0 is the missing-at-random multiple-imputation
analysis and is a counted alternative; other deltas are tipping-point
searches (kind = "search"). Rows carry the delta in param.
stress_tipping( spec, deltas = seq(0, -10, by = -2), m = 25, seed = 1, arm = NULL, shift_visits = NULL, impute_covariates = NULL, nbiter = 10, shift_every = TRUE, cores = 1L )stress_tipping( spec, deltas = seq(0, -10, by = -2), m = 25, seed = 1, arm = NULL, shift_visits = NULL, impute_covariates = NULL, nbiter = 10, shift_every = TRUE, cores = 1L )
spec |
|
deltas |
Shifts added to the imputed values of the active arm, in response units. |
m |
Number of imputations. |
seed |
Random seed (the same seed is used for every delta). |
arm |
Treatment level receiving the shift; defaults to the active arm. |
shift_visits |
Visit levels whose imputed values are shifted; |
impute_covariates |
Subject-level covariates used as predictors in the
imputation model (with treatment and the visit columns); |
nbiter |
Burn-in iterations of the FCS sampler. |
shift_every |
If |
cores |
Number of worker processes; |
Covariates that vary within subject are excluded from the imputation model and are kept in the analysis model; subject-visit rows absent from the data are created for the imputed response with those covariates missing, so they drop out of the refit.
A stress_rows data frame, one row per delta; note records the
number of imputations and the Monte Carlo standard error of the pooled
estimate.
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_tipping(spec, deltas = c(0, -5), m = 3)small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:80), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) stress_tipping(spec, deltas = c(0, -5), m = 3)
Applies the definitions: fragile if any counted row reverses the base
direction or lies outside the base confidence interval; otherwise
sensitive if any counted row changes significance at alpha; otherwise
robust. Marginal is a separate flag on the base p-value, true when it
lies within alpha * marginal_band.
stress_verdict(census, marginal_band = c(0.5, 2))stress_verdict(census, marginal_band = c(0.5, 2))
census |
A |
marginal_band |
Multiplicative band around |
A list of class stress_verdict: stability over every counted
row, stability_sensitivity over the rows that keep the estimand
(role = "sensitivity"), marginal, the counts behind them, and the rows
that drove the classification.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs")), stress_covariates(spec)) v <- stress_verdict(cen) v$stability vspec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) cen <- stress_census(spec, stress_covariance(spec, c("us", "cs")), stress_covariates(spec)) v <- stress_verdict(cen) v$stability v
Fits the pre-specified model once per visit and reports the treatment
contrast at each; the contrast at the pre-specified visit is flagged
is_base. A different visit is a different estimand, so the rows are
role = "supplementary".
stress_visits(spec, visits = NULL, cores = 1L)stress_visits(spec, visits = NULL, cores = 1L)
spec |
|
visits |
Visit levels; |
cores |
Number of worker processes; |
A stress_rows data frame, one row per visit.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_visits(spec)spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_visits(spec)
Refits the pre-specified model and forms the treatment contrast with the
other weighting of the class covariates: equal weights over levels (SAS
LSMEANS default) versus the observed covariate distribution of the
analysis rows (SAS OBSMARGINS, g-computation). The point estimate differs
only when a class covariate interacts with treatment or visit; the two
rows otherwise coincide.
stress_weights(spec, cores = 1L)stress_weights(spec, cores = 1L)
spec |
|
cores |
Number of worker processes; |
A stress_rows data frame with two rows, one flagged is_base.
spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_weights(spec)spec <- stress_spec(mmrm::fev_data, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ RACE + SEX + FEV1_BL) stress_weights(spec)
Summarise a census by family
## S3 method for class 'stress_census' summary(object, ...)## S3 method for class 'stress_census' summary(object, ...)
object |
A |
... |
Unused. |
A data frame with one row per family, kind and role: counted analyses,
number significant, number that crossed alpha, number outside the base
confidence interval, number reversing direction, the range of percent of
base, and the largest shift in base standard errors. Closing rows total
the sensitivity analyses (same estimand) and all counted rows.
For rows that carry a param value (from stress_tipping() or
stress_curve()), interpolates linearly between adjacent parameter values
to find where the p-value crosses alpha, separately for each family.
tipping_point(rows, alpha = NULL)tipping_point(rows, alpha = NULL)
rows |
A |
alpha |
Significance level; defaults to the rows' own |
A data frame with one row per family: family, n points,
param_min, param_max, p_min, p_max, crossing (the interpolated
parameter value, NA when the curve does not cross) and direction
("up" when p rises through alpha with increasing parameter, "down"
otherwise).
small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) shift_pt3 <- function(d, v) { k <- d$USUBJID == "PT3"; d$FEV1[k] <- d$FEV1[k] + v; d } tipping_point(stress_curve(spec, values = c(-20, 0, 20), modify = shift_pt3))small <- mmrm::fev_data[mmrm::fev_data$USUBJID %in% paste0("PT", 1:60), ] spec <- stress_spec(small, "FEV1", "USUBJID", "AVISIT", "ARMCD", "PBO", covariates = ~ FEV1_BL) shift_pt3 <- function(d, v) { k <- d$USUBJID == "PT3"; d$FEV1[k] <- d$FEV1[k] + v; d } tipping_point(stress_curve(spec, values = c(-20, 0, 20), modify = shift_pt3))