---
title: "Custom sensitivity contour plots"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Custom sensitivity contour plots}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

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`](https://cran.r-project.org/package=geomtextpath) package for
labeled contours and [`ggrepel`](https://cran.r-project.org/package=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")`.

```{r, eval = FALSE}
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)
    )
```

