---
title: "Exploring data"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Exploring data}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{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 things paint one. 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)

library(depictr)
```

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, and it is
off by default because the empty corner depends on the data.

```{r}
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:

```{r}
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).

```{r}
ecdf_plot(lexical_decision, RT, group = condition,
          reference_quantiles = c(0.25, 0.5, 0.75), legend_inside = TRUE)
```

```{r}
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.

```{r}
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.

```{r}
scatter_trend(crop_yield, fertiliser, yield, group = treatment)
```

## Many variables at once

`explore_pairs()` is a scatter-plot matrix, and `correlation_heatmap()`
condenses the same information into a single coloured grid.

```{r, fig.height = 6}
explore_pairs(crop_yield,
              cols = c("rainfall", "fertiliser", "soil_ph", "yield"))
```

```{r, fig.height = 5}
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:

```{r, fig.height = 5}
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.

```{r}
raincloud_plot(lexical_decision, RT, group = condition)
```

```{r}
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:

```{r, fig.height = 4}
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).

```{r, fig.height = 5}
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).

```{r, fig.height = 5}
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 can be read off directly. Here it contrasts
younger and older respondents' life satisfaction by region.

```{r}
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()` draws the distribution as a box or violin plot and highlights
the points beyond the 1.5 * IQR fences, so unusual values are easy to see before
any modelling begins.

```{r}
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.

```{r, fig.height = 5}
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()`.

```{r}
tab <- summary_table(
  wellbeing_survey,
  vars = c("life_satisfaction", "income", "stress", "education"),
  group = "region"
)
knitr::kable(tab)
```

## 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 `+`:

```{r}
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:

```{r}
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):

```{r}
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")`).
