# Custom sensitivity contour plots

The `plot()` method for objects returned by `ei_sens()` produces a standard sensitivity contour plot. For publication-quality figures with more control over styling, labeled contour lines, and benchmark annotations, a custom `ggplot2` implementation is often preferable. This vignette shows a generic version of such a plot, using the `geomtextpath` package for labeled contours and `ggrepel` for non-overlapping benchmark labels.

The code below assumes `sens` is an object returned by `ei_sens()` and `bench` is a benchmarking data frame from `ei_bench()`, both filtered to a single outcome of interest. Both are described in `vignette("sensitivity")`.

    library(ggplot2)
    library(geomtextpath)
    library(ggrepel)

    # --- filter to one outcome --------------------------------------------------
    outcome_name = "my_outcome"
    d_sens = subset(sens, outcome == outcome_name)
    d_bench = subset(bench, outcome == outcome_name)

    # clamp any Inf bias values for plotting
    d_sens$bias_bound = with(d_sens, ifelse(
        is.finite(bias_bound),
        bias_bound,
        1.5 * max(bias_bound[is.finite(bias_bound)])
    ))

    # --- contour break structure -------------------------------------------------
    # major breaks get text labels; semi-major and minor add visual density
    contour_exp = -1:1
    breaks_minor   = 10^c(contour_exp, 2) %x% c(2:4, 6:9)
    breaks_semimaj = 10^contour_exp %x% 5
    breaks_maj     = 10^contour_exp %x% 10
    c_col = "#f08f88"   # contour line color

    # --- build plot --------------------------------------------------------------
    ggplot(d_sens, aes(c_predictor, c_outcome)) +
        # minor contour lines (no labels)
        geom_contour(aes(z = bias_bound), breaks = breaks_minor,
                     color = c_col, lwd = 0.12) +
        # semi-major contour lines (no labels)
        geom_contour(aes(z = bias_bound), breaks = breaks_semimaj,
                     color = c_col, lwd = 0.48) +
        # major contour lines with numeric labels
        geom_textcontour(
            aes(z = bias_bound),
            breaks = breaks_maj,
            color = c_col, lwd = 0.7,
            hjust = 0.55, vjust = 1.25,
            halign = "left", size = 3.0,
            fontface = "bold", upright = TRUE, straight = TRUE
        ) +
        # a labeled contour for a specific reference value (e.g. the point estimate)
        geom_textcontour(
            aes(z = bias_bound, label = "Estimated effect"),
            breaks = d_sens$estimate[1],
            color = "#425682", lwd = 0.7,
            hjust = 0.55, vjust = 1.25,
            size = 3.0, fontface = "bold"
        ) +
        # benchmark points and labels
        geom_point(data = d_bench, size = 0.7) +
        geom_text_repel(
            aes(label = covariate),
            data = d_bench,
            hjust = 1.25, nudge_x = 0.002, nudge_y = 0.002,
            size = 3
        ) +
        # square-root scale spreads out the lower-left corner
        scale_x_continuous(breaks = seq(0, 1, 0.1), transform = "sqrt") +
        scale_y_continuous(breaks = seq(0, 1, 0.1), transform = "sqrt") +
        coord_cartesian(xlim = 0:1, ylim = 0:1, expand = FALSE) +
        labs(
            x = bquote(1 - {R^2}[alpha^A ~ "~" ~ alpha]),
            y = bquote({R^2}[bar(Y) ~ "~" ~ A ~ "|" ~ bar(X) ~ "," ~ Z]),
            title = paste("Sensitivity contour plot:", outcome_name)
        ) +
        theme_bw() +
        theme(
            panel.grid = element_line(color = "#aaa", linetype = "dotted",
                                      linewidth = 0.24),
            panel.grid.minor = element_blank(),
            axis.title = element_text(size = 12)
        )