--- title: "Getting started with depictr" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with depictr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: ../inst/REFERENCES.bib csl: apa.csl link-citations: true --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = FALSE, comment = "", fig.width = 7, fig.height = 4.5, dpi = 96, dev.args = list(bg = "transparent")) # Console colour carries no meaning on a rendered page. pkgdown turns it on for # its own build, and the escape sequences then reach the reader as literal text, # so colour is switched off here for a plain vignette render and a site build # alike. The fixed width keeps printed output inside the documentation column. options(cli.num_colors = 1, cli.hyperlink = FALSE, crayon.enabled = FALSE, width = 80) # Figures on the package website sit on a warm off-white page in light mode and # are inverted by pkgdown in dark mode, so an opaque background would read as a # pale slab one way and a black plate the other. Two layers paint that # background, and both have to go. The device canvas is made transparent by # `dev.args` above, and theme_depictr() then inherits theme_minimal()'s white # plot.background, which is drawn over that canvas, so it is cleared as each # figure is printed. This is deliberately a vignette-level choice: theme_depictr() # keeps its opaque background, which is what a figure saved for a paper wants. transparent_bg <- ggplot2::theme( plot.background = ggplot2::element_rect(fill = NA, colour = NA), panel.background = ggplot2::element_rect(fill = NA, colour = NA) ) knit_print.ggplot <- function(x, ...) knitr::normal_print(x + transparent_bg) knit_print.patchwork <- function(x, ...) knitr::normal_print(x & transparent_bg) ``` depictr is a single, consistent toolkit of plots that span the whole analysis workflow, from a first look at the data, through model estimates and predictions, to diagnostics, uncertainty and reporting. Every plotting function returns a `ggplot2` object [@wickham2016] (or a `patchwork` for composite panels), so you can keep customising with the usual `+` syntax, and every plot shares one theme, one palette and one set of label conventions. ```{r} library(depictr) ``` ## Five datasets to explore The package ships with five reproducibly simulated datasets, each chosen to exercise a different family of plots. They are documented under their names (e.g. `?lexical_decision`) and load with `data()`: * `lexical_decision`: a counterbalanced, crossed reaction-time/accuracy experiment (participant, item, condition, modality, word frequency). For mixed models and the classification plots. * `wellbeing_survey`: a cross-sectional survey (life satisfaction, stress, sleep, income, age, ordered education, region) with *informative* missingness. For descriptives, correlations, regression and missing data. * `crop_yield`: a field trial with a genuine fertiliser-by-treatment interaction. For regression, scatter-trend and interaction plots. * `clinical_trial`: a two-arm trial with separating survival curves and a rare adverse-event outcome. For survival and imbalanced classification. * `monthly_sales`: two seasonal monthly series (indoor/outdoor). For the time-series plots. ## A tour by task Begin with the data. `explore_bivariate()` chooses a suitable plot for any pair of variables, here a scatter with a trend because both are numeric. ```{r} explore_bivariate(crop_yield, fertiliser, yield) ``` Turn next to the model. After fitting it, `coefficient_plot()` draws a forest plot of the estimates. ```{r} fit <- lm(yield ~ rainfall + fertiliser + soil_ph + treatment, data = crop_yield) coefficient_plot(fit, order = "descending", title = "Drivers of crop yield") ``` To see what the model implies, `effects_plot()` traces the predicted response as one predictor varies. ```{r} effects_plot(fit, "fertiliser") ``` `residual_diagnostics_plot()` gathers the usual checks of the fit into one panel. ```{r, fig.height = 6} residual_diagnostics_plot(fit) ``` For uncertainty, `posterior_plot()` summarises posterior or simulation draws as a distribution per parameter. These are the fixed-effect posterior draws from a Bayesian fit of the lexical-decision model, shipped with the package. ```{r} draws <- readRDS(system.file("extdata", "lexdec_draws.rds", package = "depictr")) posterior_plot(draws[c("conditionunrelated", "modalityauditory", "word_frequency")], labels = c(conditionunrelated = "condition", modalityauditory = "modality", word_frequency = "word frequency"), title = "Lexical-decision fixed effects (ms)") ``` ## The shared spine: `tidy_estimates()` Most of the model functions rest on `tidy_estimates()`, which turns a model, or a data frame of pre-computed estimates, into one standard table. Because the plotting functions also accept that table, estimates from any source (Bayesian posteriors, bootstrap intervals, or figures taken from a paper) can be supplied directly. ```{r} tidy_estimates(fit) ``` ## A consistent, accessible look `theme_depictr()`, `depictr_palette()` and `scale_colour_depictr()` style your own plots too: ```{r} library(ggplot2) ggplot(crop_yield, aes(fertiliser, yield, colour = treatment)) + geom_point(alpha = 0.7) + scale_colour_depictr() + theme_depictr() ``` `depictr_palette()` returns the underlying hex colours directly, ready to feed `scale_fill_manual()` or a base-graphics `col =` argument: ```{r} depictr_palette(4) ``` The qualitative palette is based on the Okabe-Ito set [@okabe2008], which stays distinguishable under the common forms of colour-vision deficiency, and sequential and diverging variants are available too. Preview them with: ```{r, fig.height = 5} palette_preview(type = "all") ``` `palette_preview()` can also *simulate* a colour-vision deficiency, so you can check a palette as a deuteranope (red-green) would see it: ```{r, fig.height = 2.6} palette_preview(cvd = "deutan") ``` The simulation is available on its own as `simulate_cvd()`, and `palette_safety()` turns it into a verdict: for normal vision and each deficiency at full severity it reports the smallest perceptual distance between any two colours in a palette, so the accessibility claim comes with a number attached. ```{r} palette_safety() ``` ## Auditing the figure you are about to submit A safe palette is not a safe figure. Once a plot has been extended with your own scale, shrunk to fit a journal column, or asked to distinguish groups by colour alone, the palette's guarantee no longer describes what a reader will see. `check_figure()` reads a built plot and reports what it measured, next to the threshold it was measured against, so each verdict can be argued with. ```{r} grouped <- ggplot(crop_yield, aes(fertiliser, yield, colour = treatment)) + geom_point(alpha = 0.7) + scale_colour_depictr() + theme_depictr() check_figure(grouped) ``` Two rows are worth dwelling on. `geometry_contrast` measures each encoding colour against the panel background, and the palette's orange sits at 2.25 against white, below the 3:1 that WCAG asks of a graphical object [@wcag22]. `redundant_encoding` is zero because nothing but colour tells the two treatments apart. Mapping shape as well, and letting the darker vermillion do the second colour's work, clears both: ```{r} mended <- ggplot(crop_yield, aes(fertiliser, yield, colour = treatment, shape = treatment)) + geom_point(alpha = 0.7) + scale_colour_manual(values = c("#005b96", "#d55e00")) + theme_depictr() check_figure(mended)[, c("check", "measured", "threshold", "verdict")] ``` The audit also takes a stated output width, which is where most figure text quietly fails. Text is drawn in points, so a figure saved seven inches wide and then printed in an 8.9 cm column arrives at half the size it looked on screen: ```{r} subset(check_figure(grouped, width_cm = 8.9), check == "text_size") ``` One limitation belongs here, beside the claim it qualifies, since the package is the one making that claim. The eight-colour qualitative palette clears every colour-vision check and fails the greyscale check: its orange and its sky blue differ by 0.79 in CIE lightness, so a black-and-white printer renders them as the same grey. The Okabe-Ito guarantee is about hue confusion and was never a claim about greyscale. The threshold stays where it is, the check reports the number, and the claim has been narrowed to match. A figure that may be printed in black and white wants fewer groups, a sequential palette, or a redundant shape or line type. ```{r} eight <- data.frame(g = factor(letters[1:8]), x = 1:8, y = 1:8) p8 <- ggplot(eight, aes(x, y, colour = g)) + geom_point() + scale_colour_depictr() + theme_depictr() subset(check_figure(p8), check == "greyscale_separability") ``` Set the look once for a whole script with `depictr_options()`. It carries the base size and family, the brand and accent colours and a custom palette, so the same arguments need not travel with every call. Called with no arguments it reports the current settings: ```{r} depictr_options() ``` Supplying arguments sets them for every later plot and returns the previous values, so you can put the look back afterwards: ```{r, fig.height = 4} old <- depictr_options(base_size = 13, accent = "#b3589a") coefficient_plot(fit, title = "Set once, applied everywhere") do.call(depictr_options, old) # restore the previous settings ``` ## Where to next The remaining articles go into each area in turn. `vignette("exploring-data")` covers distributions, categories, bivariate plots, scatter-plot matrices, correlations, missingness, outliers, summary tables and the estimation plots. `vignette("model-estimates")` is the flagship: forest plots, model comparison, predicted values, interactions, random effects, optimiser checks and the frequentist-over-Bayesian-posterior overlay. `vignette("diagnostics-and-uncertainty")` covers residuals, GLM-appropriate binned residuals, the classification suite (ROC, PR, gains, lift, calibration, thresholds) on an imbalanced outcome, and power curves. Two further articles, `vignette("multivariate-and-survival")` and `vignette("time-series")`, cover the remaining methods. ## References