depictr: One visual language from first look to final figure

The figures in an analysis are rarely made all at once. An exploratory density plot may survive into a talk, a model adds a forest plot and a reviewer asks for a diagnostic or a Bayesian comparison. When each stage uses different defaults, one analysis acquires several palettes, type sizes and conventions. Accessibility is then checked, if at all, after the visual decisions have already spread through the project.

I wrote depictr to give those stages a shared visual language. It provides plotting functions for common statistical objects, but each function returns an ordinary ggplot2 object that can still be changed. It also measures the finished figure against explicit checks for contrast, text size and reliance on colour. The same conventions are available in the R package and the Python package.

The example data come from a lexical-decision experiment bundled with the package. Participants responded to words preceded by semantically related or unrelated primes, presented visually or auditorily.

library(depictr)
library(ggplot2)
packageVersion('depictr')
#> [1] '0.3.0'
data('lexical_decision', package = 'depictr')
correct <- subset(lexical_decision, accuracy == 1)
c(participants = length(unique(lexical_decision$participant)),
  items = length(unique(lexical_decision$item)),
  trials = nrow(lexical_decision))
#> participants        items       trials 
#>           24           40          960

Begin with the observed distribution

Reaction times are skewed, so a mean and standard deviation do not show the whole pattern. An empirical cumulative distribution avoids a choice of bins and makes the quartiles directly visible.

ecdf_plot(
  lexical_decision,
  RT,
  group = condition,
  reference_quantiles = c(0.25, 0.5, 0.75),
  legend_inside = TRUE,
  title = 'Related primes are faster across the distribution'
) +
  labs(x = 'Reaction time (ms)', colour = NULL)

Empirical cumulative distributions of lexical-decision reaction times for related and unrelated primes, with horizontal and vertical guides at the quartiles

The separation is not confined to a handful of slow responses. Related trials remain shifted towards shorter reaction times through much of the distribution, which gives the later model result a visible empirical context.

Keep the same language for the model

The model includes priming condition, presentation modality and word frequency, with crossed random intercepts for participants and items. coefficient_plot() reads the fitted object directly and places the fixed effects on a common standardised scale.

fit <- lmerTest::lmer(
  RT ~ condition + modality + word_frequency +
    (1 | participant) + (1 | item),
  data = correct
)

term_labels <- c(
  conditionunrelated = 'Unrelated priming',
  modalityauditory = 'Auditory modality',
  word_frequency = 'Word frequency (Zipf)'
)

tidy_estimates(fit)
#>                 term  estimate std.error   conf.low conf.high
#> 1        (Intercept) 708.01334 27.253962 654.596553 761.43012
#> 2 conditionunrelated  31.95745  8.119043  16.044418  47.87048
#> 3   modalityauditory  25.34713  8.123180   9.425993  41.26827
#> 4     word_frequency -21.89472  5.860301 -33.380696 -10.40874
coefficient_plot(
  fit,
  standardise = TRUE,
  order = 'ascending',
  labels = term_labels,
  title = 'Estimated effects on reaction time',
  x_lab = 'Change in reaction time (ms) per SD of predictor'
)

Forest plot of the standardised fixed effects for unrelated priming, auditory presentation and word frequency, with 95 per cent confidence intervals

This continuity keeps the colour, typography and interval conventions stable as the analysis moves from observations to estimates, so the reader can concentrate on the result.

Audit the figure that will be published

A colourblind-safe palette does not guarantee an accessible figure. Contrast against the background and the use of a second visual channel depend on the particular plot. The package therefore audits a built plot, not only its palette.

first_draft <- explore_distribution(
  lexical_decision,
  RT,
  group = condition,
  type = 'density',
  legend_inside = TRUE,
  title = 'Lexical-decision time by priming condition'
)
check_figure(first_draft)
#>                        check measured threshold verdict
#> 1        colour_separability   119.21       5.0    pass
#> 2 colour_separability_protan   108.92       5.0    pass
#> 3 colour_separability_deutan   121.29       5.0    pass
#> 4 colour_separability_tritan    77.90       5.0    pass
#> 5     greyscale_separability    33.34       5.0    pass
#> 6                  text_size     8.80       6.0    pass
#> 7              text_contrast     7.15       4.5    pass
#> 8          geometry_contrast     2.25       3.0    fail
#> 9         redundant_encoding     0.00       1.0    fail
#>                                                              detail
#> 1           Closest pair #005b96 and #e69f00 of 2 encoding colours.
#> 2           Closest pair #005b96 and #e69f00 of 2 encoding colours.
#> 3           Closest pair #005b96 and #e69f00 of 2 encoding colours.
#> 4           Closest pair #005b96 and #e69f00 of 2 encoding colours.
#> 5                Closest pair #005b96 and #e69f00 in CIE lightness.
#> 6 Smallest text 8.80 pt, drawn at 17.78 cm and printed at 17.78 cm.
#> 7                          Lowest-contrast text #005b96 on #ffffff.
#> 8                        Lowest-contrast colour #e69f00 on #ffffff.
#> 9                            Colour alone distinguishes the groups.

The default density fails two checks. Its orange fill falls below the package’s contrast threshold against white, and colour is the only channel separating the conditions. The corrected density plot uses a darker vermilion and adds line type. Those changes address the measured failures without changing the data or the statistical display.

final_figure <- explore_distribution(
  lexical_decision,
  RT,
  group = condition,
  type = 'density',
  palette = c('#005B96', '#B44600'),
  legend_inside = TRUE,
  title = 'Priming conditions separated by colour and line type'
) +
  aes(linetype = condition) +
  labs(x = 'Reaction time (ms)', colour = NULL, fill = NULL, linetype = NULL)

check_figure(final_figure)[, c('check', 'measured', 'threshold', 'verdict')]
#>                        check measured threshold verdict
#> 1        colour_separability   102.76       5.0    pass
#> 2 colour_separability_protan    81.30       5.0    pass
#> 3 colour_separability_deutan    96.85       5.0    pass
#> 4 colour_separability_tritan    93.25       5.0    pass
#> 5     greyscale_separability     7.09       5.0    pass
#> 6                  text_size     8.80       6.0    pass
#> 7              text_contrast     7.15       4.5    pass
#> 8          geometry_contrast     5.50       3.0    pass
#> 9         redundant_encoding     1.00       1.0    pass
final_figure

Corrected density curves of reaction times for related and unrelated primes, distinguished by both colour and line type

The palette starts from the colours proposed by Okabe and Ito (2008), and colour-vision simulations use the model of Machado et al. (2009). The audit still reports measurements beside thresholds because a journal, display size or printing process may require a different decision.

Limits

depictr draws and checks figures. It does not choose the model, contrasts or prior. A polished plot can faithfully present a poor analysis. The checks also cover measurable properties, not every barrier a reader may face. Alt text, a meaningful caption and a sensible figure order remain editorial work.

The R reference and Python reference cover diagnostics, posterior plots, power curves, model comparisons and multi-panel composition. Their place is in the reference material. The useful habit for an analysis is smaller: use one set of conventions throughout, then audit the exact figure that will leave the project.

References

Machado, G. M., Oliveira, M. M., & Fernandes, L. A. F. (2009). A physiologically-based model for simulation of color vision deficiency. IEEE Transactions on Visualization and Computer Graphics, 15(6), 1291–1298. https://doi.org/10.1109/TVCG.2009.113

Okabe, M., & Ito, K. (2008). Color universal design (CUD): How to make figures and presentations that are friendly to colorblind people. https://jfly.uni-koeln.de/color/ (Original work published 2002)

Comments are provided by Disqus and are not loaded automatically. Loading them connects your browser to Disqus, which may use cookies and process data under its privacy policy. See this site's privacy notice for details.