| name | sensitivity-analyst |
| description | Sensitivity analysis frameworks and assumption-testing methods |
Sensitivity Analyst
Comprehensive sensitivity analysis frameworks for causal inference and mediation studies
Use this skill when working on: unmeasured confounding assessment, E-values, tipping point analysis, measurement error sensitivity, model misspecification checks, Rosenbaum bounds, or any robustness evaluation for causal claims.
Unmeasured Confounding
The Fundamental Problem
In causal inference, we can never directly test the assumption of no unmeasured confounding. Sensitivity analysis quantifies: "How strong would unmeasured confounding need to be to explain away our findings?"
Bias Factor Framework
For an unmeasured confounder $U$ affecting both treatment $A$ and outcome $Y$:
$$\text{Bias} = \frac{E[Y \mid A=1, U=1] - E[Y \mid A=1, U=0]}{E[Y \mid A=0, U=1] - E[Y \mid A=0, U=0]} \times \frac{P(U=1 \mid A=1) - P(U=1 \mid A=0)}{1}$$
The observed effect $\hat{\theta}$ relates to the true effect $\theta$ via:
$$\hat{\theta} = \theta \times \text{Bias Factor}$$
Sensitivity Parameters
| Parameter | Definition | Range |
|---|
| $\gamma$ | Odds ratio for U-A association | $[1, \infty)$ |
| $\delta$ | Odds ratio for U-Y association | $[1, \infty)$ |
| $\rho$ | Correlation of U with unmeasured factors | $[-1, 1]$ |
R Implementation
sensitivity_unmeasured <- function(estimate, se,
gamma_range = seq(1, 3, 0.25),
delta_range = seq(1, 3, 0.25)) {
grid <- expand.grid(gamma = gamma_range, delta = delta_range)
grid$bias_factor <- with(grid, (gamma * delta) / (gamma + delta - 1))
grid$adjusted_estimate <- estimate / grid$bias_factor
z_crit <- qnorm(0.975)
ci_lower <- estimate - z_crit * se
tipping_points <- grid[grid$adjusted_estimate - z_crit * se / grid$bias_factor <= 0, ]
list(
original = list(estimate = estimate, ci = c(ci_lower, estimate + z_crit * se)),
sensitivity_grid = grid,
tipping_points = tipping_points,
minimum_bias_to_nullify = min(grid$bias_factor[grid$adjusted_estimate <= 0])
)
}
E-Values
E-Value Definition
The E-value (VanderWeele & Ding, 2017) is the minimum strength of association that an unmeasured confounder would need to have with both treatment and outcome to fully explain away the observed effect.
For a risk ratio $RR$:
$$E\text{-value} = RR + \sqrt{RR \times (RR - 1)}$$
For the confidence interval limit:
$$E\text{-value}{CI} = RR{lower} + \sqrt{RR_{lower} \times (RR_{lower} - 1)}$$
Interpretation Guidelines
| E-value | Interpretation |
|---|
| < 1.5 | Very weak; easily explained by modest confounding |
| 1.5 - 2.0 | Weak; moderate confounding could explain |
| 2.0 - 3.0 | Moderate; substantial confounding needed |
| 3.0 - 4.0 | Strong; would require strong confounding |
| > 4.0 | Very strong; unlikely to be fully explained |
E-Value for Different Effect Measures
| Measure | Formula |
|---|
| Risk Ratio (RR) | $E = RR + \sqrt{RR(RR-1)}$ |
| Odds Ratio (OR) | Convert to RR approximation first |
| Hazard Ratio (HR) | $E = HR + \sqrt{HR(HR-1)}$ (when rare outcome) |
| Risk Difference | Convert to RR using baseline risk |
| Standardized Mean Difference | $E = \exp(0.91 \times d) + \sqrt{\exp(0.91 \times d)(\exp(0.91 \times d)-1)}$ |
R Implementation
compute_evalue <- function(estimate, lo = NULL, hi = NULL,
type = c("RR", "OR", "HR", "SMD"),
rare = TRUE) {
type <- match.arg(type)
rr <- switch(type,
"RR" estimate
rare estimate estimate
rare estimate estimate
estimate
rr rr 1rr
evalue rr rr rr
evalue_ci
lo
rr_lo type
lo
rare lo lo
rare lo lo
lo
rr_lo rr_lo 1rr_lo
rr_lo
evalue_ci rr_lo rr_lo rr_lo
evalue_ci 1
interpretation case_when
evalue
evalue
evalue
evalue
evalue_point evalue
evalue_ci evalue_ci
interpretation interpretation
message sprintf
evalue
Tipping Point Analysis
Concept
Tipping point analysis identifies the specific parameter values at which conclusions would change (e.g., confidence interval includes null, effect reverses sign).
Tipping Point Types
| Type | Definition | Use Case |
|---|
| Null tipping point | Where CI includes 0 | Statistical significance |
| Clinical tipping point | Where effect < MCID | Clinical significance |
| Direction tipping point | Where effect changes sign | Effect direction |
Visualization
plot_tipping_point <- function(estimate, se,
gamma_range = seq(1, 5, 0.1),
delta_range = seq(1, 5, 0.1)) {
library(ggplot2)
grid <- expand.grid(gamma = gamma_range, delta = delta_range)
grid$bias_factor <- with(grid, ( delta delta
gridadjusted estimate gridbias_factor
gridsignificant gridadjusted se gridbias_factor
ggplotgrid aesx y delta z adjusted
geom_contour_filledbreaks estimate estimate
geom_contouraesz significant
breaks color linewidth
labs
x
y
title
subtitle
theme_minimal
Measurement Error Sensitivity
Types of Measurement Error
| Type | Description | Effect on Estimates |
|---|
| Non-differential | Error unrelated to other variables | Usually biases toward null |
| Differential | Error depends on treatment/outcome | Can bias in either direction |
| Systematic | Consistent over/under-reporting | Shifts estimates |
| Random | Noise around true value | Attenuates relationships |
Measurement Error in Mediation
For mediation with misclassified mediator:
$$\hat{a}\hat{b} = ab \times \text{Attenuation Factor}$$
where:
$$\text{Attenuation} = \frac{\text{Sensitivity} + \text{Specificity} - 1}{1}$$
R Implementation for Misclassification
sensitivity_misclassification <- function(indirect_effect, se_indirect,
sens_range = seq(0.7, 1, 0.05),
spec_range = seq(0.7, 1, 0.05)) {
grid <- expand.grid(sensitivity = sens_range, specificity = spec_range)
grid$attenuation <- grid$sensitivity + grid$specificity
gridcorrected_effect indirect_effect gridattenuation
gridcorrected_se se_indirect gridattenuation
gridci_lower gridcorrected_effect gridcorrected_se
gridci_upper gridcorrected_effect gridcorrected_se
original_significant indirect_effect se_indirect
gridconclusion_change gridci_lower original_significant
original
effect indirect_effect
se se_indirect
significant original_significant
sensitivity_grid grid
robust_region gridgridconclusion_change
vulnerable_region gridgridconclusion_change
Model Misspecification
Types of Misspecification
- Functional form: Linear when true relationship is nonlinear
- Omitted variables: Missing important confounders
- Incorrect distribution: Wrong error distribution assumed
- Heterogeneity: Ignoring effect modification
Specification Curve Analysis
Test how results vary across defensible model specifications:
specification_curve <- function(data, outcome, treatment, mediator,
covariate_sets,
model_types = c("linear", "logistic")) {
results <- list()
spec_id <- 1
for (covs in covariate_sets) {
for (model_type in model_types)
m_formula as.formulapastemediator treatment
pastecovs collapse
y_formula as.formulapasteoutcome treatment mediator
pastecovs collapse
model_type
m_model lmm_formula data data
y_model lmy_formula data data
m_model glmm_formula data data family binomial
y_model glmy_formula data data family binomial
a coefm_modeltreatment
b coefy_modelmediator
indirect a b
resultsspec_id data.frame
spec_id spec_id
covariates pastecovs collapse
model_type model_type
a_path a
b_path b
indirect_effect indirect
n_covariates covs
spec_id spec_id
results_df do.callrbind results
specifications results_df
summary data.frame
median_effect medianresults_dfindirect_effect
mean_effect meanresults_dfindirect_effect
sd_effect sdresults_dfindirect_effect
min_effect results_dfindirect_effect
max_effect results_dfindirect_effect
prop_positive meanresults_dfindirect_effect
prop_significant
n_specifications nrowresults_df
Rosenbaum Bounds
Framework
Rosenbaum bounds quantify how sensitive causal conclusions are to hidden bias in observational studies using matched designs.
Sensitivity Parameter Γ
$\Gamma$ represents the maximum ratio of odds of treatment for two matched subjects:
$$\frac{1}{\Gamma} \leq \frac{P(A_i = 1 \mid X_i)}{P(A_j = 1 \mid X_j)} \times \frac{1 - P(A_j = 1 \mid X_j)}{1 - P(A_i = 1 \mid X_i)} \leq \Gamma$$
Interpretation
| Γ Value | Interpretation |
|---|
| 1.0 | No hidden bias (randomization) |
| 1.1 - 1.3 | Sensitive to small hidden bias |
| 1.3 - 2.0 | Moderately robust |
| 2.0 - 3.0 | Robust to substantial bias |
| > 3.0 | Very robust |
R Implementation
rosenbaum_bounds <- function(outcome_diff, gamma_range = seq(1, 3, 0.1)) {
n <- length(outcome_diff)
ranks <- rank(abs(outcome_diff))
W_obs <- sum(ranks[outcome_diff > 0])
results <- data.frame(gamma = gamma_range)
results$p_upper <- NA
results$p_lower <- NA
for i gamma_range
gamma_rangei
p_treat
E_W ranks p_treat
V_W ranks p_treat p_treat
z_upper W_obs E_W V_W
resultsp_upperi pnormz_upper
p_treat_low 1
E_W_low ranks p_treat_low
V_W_low ranks p_treat_low p_treat_low
z_lower W_obs E_W_low V_W_low
resultsp_loweri pnormz_lower
resultssignificant_upper resultsp_upper
critical_gamma resultsresultssignificant_upper na.rm
bounds results
critical_gamma critical_gamma
interpretation sprintf
critical_gamma
Sensitivity Analysis for Mediation
Sequential Ignorability Sensitivity
For natural indirect effects, sensitivity to violation of sequential ignorability:
sensitivity_mediation <- function(a_coef, b_coef, indirect_effect,
se_indirect,
rho_range = seq(-0.5, 0.5, 0.05)) {
results <- data.frame(rho = rho_range)
results$adjusted_indirect <- indirect_effect * (1 - rho_range^2)
results$ci_lower <- results$adjusted_indirect - 1.96 se_indirect
resultsci_upper resultsadjusted_indirect se_indirect
resultssignificant resultsci_lower resultsci_upper
original_sign indirect_effect
critical_rho rho_rangeresultsadjusted_indirect original_sign
sensitivity_grid results
critical_rho critical_rho
interpretation sprintf
critical_rho
robust_range resultsresultssignificant
Sensitivity Analysis Checklist
Pre-Analysis Planning
Analysis Execution
Reporting Template
## Sensitivity Analysis
### Unmeasured Confounding
The E-value for our main finding (RR = X.XX) is E = X.XX, indicating that
an unmeasured confounder would need to be associated with both treatment
and outcome by a risk ratio of at least X.XX each to fully explain away
the observed effect. This represents [interpretation] confounding.
### Tipping Point Analysis
Figure X shows the sensitivity of our conclusions to unmeasured confounding.
The observed effect would be nullified if [specific conditions].
### Measurement Error
Under the assumption of [non-differential/differential] measurement error,
our results remain significant for mediator sensitivity values above X%
and specificity above X%.
### Robustness to Model Specification
Across [N] alternative model specifications varying [covariates/functional
forms/etc.], the median indirect effect was X.XX (range: X.XX to X.XX).
[XX%] of specifications yielded statistically significant positive effects.
References
Foundational Papers
- VanderWeele, T. J., & Ding, P. (2017). Sensitivity analysis in observational research: Introducing the E-value. Annals of Internal Medicine
- Rosenbaum, P. R. (2002). Observational Studies
- Ding, P., & VanderWeele, T. J. (2016). Sensitivity analysis without assumptions. Epidemiology
Mediation-Specific
- Imai, K., Keele, L., & Yamamoto, T. (2010). Identification, inference and sensitivity analysis for causal mediation effects. Statistical Science
- VanderWeele, T. J. (2010). Bias formulas for sensitivity analysis for direct and indirect effects. Epidemiology
Measurement Error
- Carroll, R. J., et al. (2006). Measurement Error in Nonlinear Models
- Buonaccorsi, J. P. (2010). Measurement Error: Models, Methods, and Applications
Version: 1.0.0
Created: 2025-12-08
Domain: Sensitivity analysis for causal inference
Applications: Mediation analysis, observational studies, matched designs