Skip to contents

depictr provides a coherent set of exploratory plots, estimation plots and a descriptive table, all sharing the package theme and palette. Column names can be given quoted or unquoted. The examples use the bundled lexical_decision, wellbeing_survey and crop_yield datasets. A closing section shows how to customise any of these plots with ordinary ggplot2 code.

One variable

explore_distribution() for a numeric variable, explore_categorical() for a categorical one. A unimodal density leaves its upper corners empty, so legend_inside = TRUE tucks the colour legend into the top-right rather than spending a right-hand margin on it. Several plots take this argument; it is off by default because the empty corner depends on the data.

explore_distribution(lexical_decision, RT, group = condition, type = "density",
                     legend_inside = TRUE)

An overlay gets crowded once there are several groups, so facet = TRUE gives each group its own panel:

explore_distribution(wellbeing_survey, life_satisfaction, group = region,
                     type = "both", facet = TRUE)

ecdf_plot() is a bin-free alternative: the empirical cumulative distribution lets you read medians and quantiles straight off the curve and makes a shift between groups obvious (here the related condition is faster throughout).

ecdf_plot(lexical_decision, RT, group = condition,
          reference_quantiles = c(0.25, 0.5, 0.75), legend_inside = TRUE)

explore_categorical(wellbeing_survey, education, group = region,
                    proportion = TRUE, position = "dodge")

Two variables, any types

explore_bivariate() selects the appropriate plot automatically: a scatter plot for two numeric variables, box plots for a numeric variable against a categorical one, and a filled bar chart for two categorical variables.

explore_bivariate(lexical_decision, condition, RT)

For a focused scatter with a fitted trend, use scatter_trend(). The crop-yield trial has a real fertiliser-by-treatment interaction, visible here as two diverging slopes.

scatter_trend(crop_yield, fertiliser, yield, group = treatment)

Many variables at once

explore_pairs() is a scatter-plot matrix; correlation_heatmap() condenses the same information into a single coloured grid.

explore_pairs(crop_yield, cols = c("rainfall", "fertiliser", "soil_ph", "yield"))

correlation_heatmap(wellbeing_survey)

reorder = TRUE orders the variables by hierarchical clustering, so blocks of mutually correlated variables sit together and the structure is easier to read:

correlation_heatmap(wellbeing_survey, reorder = TRUE)

Distributions across groups

raincloud_plot() shows the density, the box summary and the raw points together, and group_comparison_plot() adds the group means with confidence intervals over the raw data. By showing the estimate alongside its uncertainty, the latter conveys whether the groups differ more faithfully than a bar chart.

raincloud_plot(lexical_decision, RT, group = condition)

group_comparison_plot(lexical_decision, RT, condition)

To compare the shape of a distribution across several groups at once, ridgeline_plot() stacks one partially overlapping density per group:

ridgeline_plot(wellbeing_survey, life_satisfaction, region)

Estimation plots: effect size rather than a p-value

An estimation plot puts the effect size and its uncertainty at the centre of the comparison. estimation_plot() draws the classic Gardner-Altman two-group plot: the raw data with group means on top, and the mean difference with a bootstrap confidence interval beneath, aligned so a difference of zero sits under the reference group’s mean. With two groups it also annotates a standardised effect size (Hedges’ g by default).

set.seed(1)
estimation_plot(lexical_decision, RT, condition,
                title = "RT difference: unrelated vs. related priming")

group_comparison_plot(differences = TRUE) is the same idea reached from the group-means plot: it appends the difference panel, turning a means comparison into a full estimation plot. With more than two groups every other group is compared against a chosen reference (a Cumming plot).

set.seed(1)
group_comparison_plot(crop_yield, yield, treatment, differences = TRUE,
                      title = "Yield difference: enhanced vs. standard")

Comparing two groups across categories

dumbbell_plot() compares one value between two groups across a set of categories: the two group values per category are joined by a segment, so the size and direction of each gap is clear at a glance. Here it contrasts younger and older respondents’ life satisfaction by region.

wb <- wellbeing_survey
wb$age_group <- ifelse(wb$age < median(wb$age), "younger", "older")
dumbbell_plot(wb, region, life_satisfaction, age_group, legend_inside = TRUE)

Data quality: outliers and missingness

outlier_plot(crop_yield, yield)

wellbeing_survey has informative missingness (income is missing more often at higher stress), so the missingness map is worth a look before modelling.

missingness_map(wellbeing_survey, legend_inside = TRUE)

A descriptive summary table

summary_table() builds a ‘Table 1’: mean (SD) for numeric variables, counts and percentages for categorical ones, optionally by group. The first row always reports the sample size (N), and any variable with missing values gets a Missing, n (%) row, both visible here for the wellbeing survey. It returns a plain data frame, ready for knitr::kable().

tab <- summary_table(
  wellbeing_survey,
  vars = c("life_satisfaction", "income", "stress", "education"),
  group = "region"
)
knitr::kable(tab)
variable statistic Overall East North South West
N 300 69 82 67 82
life_satisfaction Mean (SD) 4.3 (1.1) 4.7 (0.9) 4.8 (1.0) 3.6 (1.0) 4.1 (1.0)
Missing, n (%) 7 (2%) 1 (1%) 3 (4%) 1 (1%) 2 (2%)
income Mean (SD) 31343.7 (14099.0) 32410.2 (13381.5) 36070.5 (15999.9) 25508.6 (11332.3) 30094.6 (12902.8)
Missing, n (%) 23 (8%) 6 (9%) 4 (5%) 9 (13%) 4 (5%)
stress Mean (SD) 4.1 (1.4) 3.7 (1.2) 3.4 (1.5) 5.0 (1.1) 4.3 (1.3)
education secondary 140 (47%) 31 (45%) 37 (45%) 33 (49%) 39 (48%)
undergraduate 113 (38%) 28 (41%) 29 (35%) 22 (33%) 34 (41%)
postgraduate 47 (16%) 10 (14%) 16 (20%) 12 (18%) 9 (11%)

Customising and extending the plots

Every depictr function returns a plain ggplot2 object (composite figures return a patchwork), so you can keep adding layers, scales, labels and theme tweaks with the usual +:

library(ggplot2)

scatter_trend(crop_yield, fertiliser, yield, group = treatment) +
  labs(title = "Yield rises with fertiliser",
       subtitle = "More steeply under the enhanced treatment") +
  theme(legend.position = "bottom")

Tidying the legend

depictr centres a legend title over its keys by default. When the levels speak for themselves, though, the title is just clutter, so drop it by mapping it to NULL. Reversing a discrete colour legend at the same time makes it read top-to-bottom in the order the curves are stacked:

ecdf_plot(lexical_decision, RT, group = condition) +
  labs(colour = NULL) +
  guides(colour = guide_legend(reverse = TRUE))

Moving the legend into the plot

Several plots take legend_inside = TRUE to tuck the legend into a corner they usually leave empty (see e.g. ?ecdf_plot). For any plot that does not, the same move is one theme() call. A dodged bar chart, for instance, leaves the top-right clear when the right-most category is short, so the legend fits there. Because the regions are self-evident, we drop the title too (with element_blank(), since this plot sets the legend name on its fill scale):

explore_categorical(wellbeing_survey, education, group = region,
                    proportion = TRUE, position = "dodge") +
  theme(legend.position = "inside",
        legend.position.inside = c(0.98, 0.98),
        legend.justification = c(1, 1),
        legend.title = element_blank())

Built-in layout controls

Many functions also expose layout controls directly. explore_distribution(facet = TRUE) and ridgeline_plot() separate groups, correlation_heatmap(reorder = TRUE) clusters variables, and the model-estimate plots take facet and standardise to keep coefficients on very different scales legible (see vignette("model-estimates")).