The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

confoundvis

R-CMD-check License: GPL-3

Overview

confoundvis is an R package for visualizing sensitivity analysis to unmeasured confounding in observational studies. It implements visualization tools for three major sensitivity frameworks — the Impact Threshold (Frank, 2000), Partial R-squared / Robustness Value (Cinelli & Hazlett, 2020), and E-value style metrics (VanderWeele & Ding, 2017) — helping researchers assess how strong omitted confounding would need to be to attenuate, invalidate, or reverse an estimated causal effect, and communicate those findings through clear, publication-ready graphics.

Core functions:

Function Description
new_confoundsens() Construct a sensitivity analysis object
plot_sensitivity_contour() Contour plot of robustness values
plot_robustness_curve() Robustness curve across effect sizes
plot_sensitivity_love() Love-plot style sensitivity display
plot_local_taylor() Local Taylor approximation around estimates
fit_local_quadratic() Fit local quadratic to sensitivity path
simulate_taylor_demo() Simulate data for Taylor approximation demos

Installation

Install the development version from GitHub:

# install.packages("pak")
pak::pak("causalfragility-lab/confoundvis")

Quick Start

library(confoundvis)

# Construct a sensitivity analysis object
obj <- new_confoundsens(
  estimate       = 0.5,
  se             = 0.1,
  df             = 200,
  r2dz_x         = seq(0.01, 0.4, by = 0.01),
  r2yz_dx        = seq(0.01, 0.4, by = 0.01),
  benchmark_r2dz = 0.1,
  benchmark_r2yz = 0.15
)

# Contour plot of robustness values
plot_sensitivity_contour(obj)

# Robustness curve across effect sizes
plot_robustness_curve(obj)

# Love-plot style sensitivity display
plot_sensitivity_love(obj)

# Local Taylor approximation
plot_local_taylor(obj)

Plot Methods

# Contour plot
plot_sensitivity_contour(obj)

# Robustness curve
plot_robustness_curve(obj)

# Love plot
plot_sensitivity_love(obj)

# Taylor approximation
plot_local_taylor(obj)

Sensitivity Frameworks

The package operationalises three sensitivity frameworks:

Framework Reference Key Quantity
Impact threshold Frank (2000) % cases that must be replaced to nullify effect
Partial R-squared / Robustness value Cinelli & Hazlett (2020) R² of confounder with treatment and outcome
E-value style metrics VanderWeele & Ding (2017) Minimum confounding risk ratio to explain away effect

Simulation Results

Taylor Approximation Bias Across Window Widths

A simulation with theta0 = 0.5, slope = -0.7, kappa3 = 0.6, and window widths w ∈ {0.05, 0.1, 0.2, 0.3, 0.4, 0.5} confirms that local quadratic approximation consistently outperforms first-order (tangent) approximation as the local window widens.

library(confoundvis)
library(ggplot2)
library(scales)

set.seed(2025)

theta0     <- 0.5
slope      <- -0.7
kappa3     <- 0.6
windows    <- c(0.05, 0.1, 0.2, 0.3, 0.4, 0.5)
kappa_vals <- c(-0.4, 0, 0.4)
delta      <- seq(0, 0.5, length.out = 1000)

results <- list()
for (kap in kappa_vals) {
  theta_true <- theta0 + slope * delta + kap * delta^2 + kappa3 * delta^3
  simdat <- data.frame(delta = delta, theta = theta_true)
  for (w in windows) {
    local_dat <- subset(simdat, delta <= w)
    pred_t <- theta0 + slope * local_dat$delta
    mae_t  <- mean(abs(local_dat$theta - pred_t))
    fit_q  <- lm(theta ~ delta + I(delta^2), data = local_dat)
    mae_q  <- mean(abs(local_dat$theta - predict(fit_q, newdata = local_dat)))
    results[[length(results) + 1]] <- data.frame(
      kappa = kap, window = w, mae_tangent = mae_t, mae_quadratic = mae_q
    )
  }
}

df <- do.call(rbind, results)
df_long <- rbind(
  data.frame(kappa = df$kappa, window = df$window,
             type = "Tangent",   mae = df$mae_tangent),
  data.frame(kappa = df$kappa, window = df$window,
             type = "Quadratic", mae = df$mae_quadratic)
)
df_long$kappa_lab <- factor(
  df_long$kappa,
  levels = c(-0.4, 0, 0.4),
  labels = c("Concave (K < 0)", "Linear (K = 0)", "Convex (K > 0)")
)

ggplot(df_long, aes(x = window, y = mae, color = type, linetype = type)) +
  geom_line(linewidth = 0.9) +
  geom_point(size = 1.8) +
  scale_y_log10(labels = label_scientific()) +
  facet_wrap(~ kappa_lab, nrow = 1) +
  labs(
    title    = "Taylor Approximation MAE vs Local Window Width",
    subtitle = "True path includes cubic term (kappa3 = 0.6)",
    x        = "Local window width (w)",
    y        = "Mean Absolute Error (log scale)",
    color    = "Approximation",
    linetype = "Approximation"
  ) +
  theme_bw(base_size = 11) +
  theme(
    legend.position  = "bottom",
    panel.grid.minor = element_blank(),
    strip.background = element_rect(fill = "grey92", colour = NA),
    plot.title       = element_text(face = "bold")
  )

Key finding: Quadratic approximation yields substantially lower error than the tangent at every window width. Approximation error grows with window width, and curvature accelerates the deterioration of first-order linear approximation.

Zero-Crossing Error Under Linearity Mis-specification

When the true sensitivity path is nonlinear, using a linear approximation to locate the zero-crossing introduces systematic bias.

theta0_vals <- c(0.2, 0.4, 0.6)
slope_vals  <- c(-0.3, -0.6, -0.9)
kappa_vals2 <- seq(-0.6, 0.6, by = 0.2)

find_true <- function(t0, s, k) {
  if (abs(k) < 1e-10) return(-t0 / s)
  disc  <- s^2 - 4 * k * t0
  if (disc < 0) return(NA_real_)
  roots <- c((-s + sqrt(disc)) / (2 * k), (-s - sqrt(disc)) / (2 * k))
  roots <- roots[roots > 0]
  if (length(roots) == 0) return(NA_real_)
  min(roots)
}

res2 <- list()
for (t0 in theta0_vals) {
  for (s in slope_vals) {
    for (k in kappa_vals2) {
      l_lin  <- -t0 / s
      l_true <- find_true(t0, s, k)
      if (is.na(l_true)) next
      res2[[length(res2) + 1]] <- data.frame(
        theta0 = t0, slope = s, kappa = k,
        lambda_linear = l_lin, lambda_true = l_true,
        error = l_lin - l_true
      )
    }
  }
}

df2 <- do.call(rbind, res2)
df2$theta0_lab <- factor(
  df2$theta0,
  levels = c(0.2, 0.4, 0.6),
  labels = c("theta0 = 0.2", "theta0 = 0.4", "theta0 = 0.6")
)

ggplot(df2, aes(x = kappa, y = error,
                color = factor(slope), group = factor(slope))) +
  geom_hline(yintercept = 0, linetype = "dashed", colour = "grey50") +
  geom_line(linewidth = 0.9) +
  geom_point(size = 1.8) +
  facet_wrap(~ theta0_lab, nrow = 1) +
  labs(
    title    = "Signed Error in lambda* Under Linearity Mis-specification",
    subtitle = "Positive = over-estimation by linear approximation",
    x        = "True curvature (kappa)",
    y        = "Estimated lambda* (linear) - true lambda*",
    color    = "Slope"
  ) +
  theme_bw(base_size = 11) +
  theme(
    legend.position  = "bottom",
    panel.grid.minor = element_blank(),
    strip.background = element_rect(fill = "grey92", colour = NA),
    plot.title       = element_text(face = "bold")
  )

Key finding: Under concavity, linear approximation over-estimates the fragility threshold; under convexity, it under-estimates. The bias grows with both curvature magnitude and initial effect size.

References

Citation

citation("confoundvis")

Hait, S. (2025). confoundvis: Visualization Tools for Sensitivity Analysis to Unmeasured Confounding. R package version 0.1.0. https://github.com/causalfragility-lab/confoundvis

Author

Subir Hait
Michigan State University
haitsubi@msu.edu
ORCID: 0009-0004-9871-9677

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.