---
title: "Multiple Legends"
author: "Gilles Colling"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Multiple Legends}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5
)
library(ggguides)
library(ggplot2)

# Theme with transparent backgrounds for pkgdown light/dark mode
theme_set(
  theme_grey() +
    theme(
      plot.background = element_rect(fill = "transparent", color = NA),
      panel.background = element_rect(fill = "transparent", color = NA),
      legend.background = element_rect(fill = "transparent", color = NA),
      legend.key = element_rect(fill = "transparent", color = NA),
      legend.box.background = element_rect(fill = "transparent", color = NA)
    )
)
```

## Overview

When a plot maps multiple aesthetics (colour, size, shape, etc.), ggplot2 creates separate legends for each. ggguides provides functions to control these legends individually:

- **Hide specific legends** with `legend_hide()`

- **Keep only certain legends** with `legend_select()`

- **Control display order** with `legend_order_guides()`

- **Force merge/split** with `legend_merge()` and `legend_split()`

- **Position legends separately** using `by` parameter on position functions

- **Style legends separately** using `by` parameter on `legend_style()`

## Example Plot

```{r example-plot}
# Plot with multiple aesthetics
p <- ggplot(mtcars, aes(mpg, wt,
                        color = factor(cyl),
                        size = hp,
                        shape = factor(am))) +
  geom_point() +
  labs(color = "Cylinders", size = "Horsepower", shape = "Transmission")

p
```

## Hiding Legends

Use `legend_hide()` to remove specific legends while keeping others:

```{r hide-legends}
# Hide the size legend
p + legend_hide(size)

# Hide multiple legends
p + legend_hide(size, shape)
```

## Selecting Legends

Use `legend_select()` to keep only certain legends (inverse of `legend_hide()`):

```{r select-legends}
# Keep only the colour legend
p + legend_select(colour)

# Keep colour and shape
p + legend_select(colour, shape)
```

## Controlling Legend Order

By default, legends appear in an unspecified order. Use `legend_order_guides()` to control the display order:

```{r order-legends}
# Default order
p

# Size legend first, then colour, then shape
p + legend_order_guides(size = 1, colour = 2, shape = 3)
```

## Merging and Splitting Legends

ggplot2 automatically merges legends when they have the same title and matching labels. Use `legend_merge()` and `legend_split()` to override this behavior.

### Forcing Merge

```{r merge-legends}
# Plot where colour and fill map to the same variable
p_merge <- ggplot(mtcars, aes(mpg, wt, color = factor(cyl), fill = factor(cyl))) +
  geom_point(shape = 21, size = 4, stroke = 1.5) +
  labs(color = "Cylinders", fill = "Cylinders")

# Legends merge automatically when titles and labels match
p_merge

# Explicitly request merge (reinforces default behavior)
p_merge + legend_merge(colour, fill)
```

### Forcing Split

```{r split-legends}
# Force separate legends even when they could merge
p_merge + legend_split(colour, fill)
```

## Positioning Legends Separately

Position functions (`legend_left()`, `legend_right()`, `legend_top()`, `legend_bottom()`) accept a `by` parameter to position specific legends:

```{r position-separately}
# Place colour legend on the left, size legend at bottom
p +
  legend_hide(shape) +
  legend_left(by = "colour") +
  legend_bottom(by = "size")
```

```{r position-top-right}
# Colour legend on top, size on right
p +
  legend_hide(shape) +
  legend_top(by = "colour") +
  legend_right(by = "size")
```

## Styling Legends Separately

Use the `by` parameter on `legend_style()` to apply different styles to different legends:

```{r style-separately}
p +
  legend_hide(shape) +
  legend_style(title_face = "bold", background = "grey95", by = "colour") +
  legend_style(size = 10, by = "size")
```

## Combining Multiple Controls

All functions work together:

```{r combined}
# Complex example: hide shape, position colour on left with bold title,
# position size at bottom with smaller text
p +
  legend_hide(shape) +
  legend_left(by = "colour") +
  legend_style(title_face = "bold", title_size = 14, by = "colour") +
  legend_bottom(by = "size") +
  legend_style(size = 9, direction = "horizontal", by = "size")
```

## Four Legends, One per Side

When a plot has four legends and you want one on each side (top, bottom, left, right), you can fine-tune each legend along three axes:

1. **Side placement** — `legend_top/bottom/left/right(by = "<aes>")`

2. **Distance from the panel** — `legend_style(by = "<aes>", margin = c(t, r, b, l))`

3. **Slide along the side** — `legend_style(by = "<aes>", justification = ...)`

For top/bottom legends, `justification` is `"left"`, `"center"`, `"right"` (or a number in `[0, 1]`). For left/right legends, it's `"top"`, `"center"`, `"bottom"` (or a number).

```{r four-sides, fig.width = 8, fig.height = 6}
p4 <- ggplot(mtcars, aes(mpg, wt,
                         colour = factor(cyl),
                         fill   = factor(gear),
                         size   = hp,
                         shape  = factor(am))) +
  geom_point(stroke = 1.2) +
  labs(colour = "Cyl", fill = "Gear", size = "HP", shape = "AM")

p4 +
  # 1. Send each legend to its side
  legend_top   (by = "colour") +
  legend_bottom(by = "fill")   +
  legend_left  (by = "size")   +
  legend_right (by = "shape")  +

  # 2. Slide each legend along its side
  legend_style(by = "colour", justification = "left") +
  legend_style(by = "fill",   justification = "right") +
  legend_style(by = "size",   justification = "top") +
  legend_style(by = "shape",  justification = "bottom") +

  # 3. Nudge each legend toward/away from the panel via margin (cm)
  legend_style(by = "colour", margin = c(0, 0, 0.3, 0)) +
  legend_style(by = "fill",   margin = c(0.3, 0, 0, 0)) +
  legend_style(by = "size",   margin = c(0, 0.3, 0, 0)) +
  legend_style(by = "shape",  margin = c(0, 0, 0, 0.3))
```

Each `legend_style(by = ...)` call is additive — you can chain as many as you need to tune one legend at a time without affecting the others.

## Six Legends, Stacked per Side

More than one legend can share a side. ggplot2 stacks them in the order it resolves them, and ggguides lets you pick which legend goes where, slide it along the rail, and style it without touching the others. The plot below has **six** legends: two on top, two on the right, one on the bottom, one on the left.

```{r six-legends, fig.width = 9, fig.height = 6.5}
p6 <- ggplot(mtcars, aes(mpg, wt)) +
  geom_smooth(aes(linetype = factor(vs)), method = "lm", se = FALSE,
              colour = "grey40") +
  geom_point(aes(colour = factor(cyl),
                 fill   = factor(gear),
                 size   = hp,
                 alpha  = qsec,
                 shape  = factor(am)),
             stroke = 1.2) +
  scale_shape_manual(values = c(21, 24)) +
  labs(colour = "Cyl", fill = "Gear", size = "HP",
       alpha = "QSec", shape = "AM", linetype = "VS")

p6 +
  # 1. Side placement — two legends share the top, two share the right
  legend_top   (by = "colour")   +
  legend_top   (by = "fill")     +
  legend_right (by = "size")     +
  legend_right (by = "alpha")    +
  legend_bottom(by = "shape")    +
  legend_left  (by = "linetype") +

  # 2. Slide each legend along its rail
  #    top/bottom rails: "left" / "center" / "right" (or a number in [0,1])
  #    left/right rails: "top"  / "center" / "bottom"
  legend_style(by = "colour",   justification = "left")   +
  legend_style(by = "fill",     justification = "right")  +
  legend_style(by = "size",     justification = "top")    +
  legend_style(by = "alpha",    justification = "bottom") +
  legend_style(by = "shape",    justification = "center") +
  legend_style(by = "linetype", justification = "center") +

  # 3. Appearance — per-legend title weight, key size, direction, text size
  legend_style(by = "colour",   title_face = "bold",
               key_width = 0.4, key_height = 0.4) +
  legend_style(by = "fill",     title_face = "bold",
               key_width = 0.4, key_height = 0.4) +
  legend_style(by = "size",     title_size = 9, size = 8)  +
  legend_style(by = "alpha",    title_size = 9, size = 8)  +
  legend_style(by = "shape",    direction = "horizontal")  +
  legend_style(by = "linetype", direction = "vertical")    +

  # 4. Nudge each legend toward/away from the panel via margin (cm)
  #    order is c(top, right, bottom, left)
  legend_style(by = "colour",   margin = c(0, 0, 0.2, 0)) +
  legend_style(by = "fill",     margin = c(0, 0, 0.2, 0)) +
  legend_style(by = "size",     margin = c(0, 0, 0, 0.3)) +
  legend_style(by = "alpha",    margin = c(0, 0, 0, 0.3)) +
  legend_style(by = "shape",    margin = c(0.3, 0, 0, 0)) +
  legend_style(by = "linetype", margin = c(0, 0.3, 0, 0))
```

### What each step is doing

**Step 1, side placement.** `legend_<side>(by = "<aes>")` sends one legend to one side. Call it twice with different aesthetics and both legends land on that side. In the example, `colour` and `fill` share the top rail, `size` and `alpha` share the right rail, and `shape` and `linetype` each get a side of their own.

**Step 2, slide along the rail.** `legend_style(by = "<aes>", justification = ...)` repositions a single legend without affecting its neighbours. The keyword interpretation depends on which rail the legend sits on:

- Top or bottom rail (horizontal): `"left"`, `"center"`, `"right"`, or a number in `[0, 1]` where 0 is flush left and 1 is flush right. In the example, `colour` sits at the left end of the top rail and `fill` at the right end, so the two top legends land in opposite corners.

- Left or right rail (vertical): `"top"`, `"center"`, `"bottom"`, or a number where 0 is bottom and 1 is top. `size` sits at the top of the right rail and `alpha` at the bottom.

The per-guide form is what you want when different legends need different alignments. For a single alignment applied to every legend at once, use `legend_style(justification = ...)` without `by`.

**Step 3, appearance.** Anything `legend_style()` accepts (`title_face`, `title_size`, `size` for label text, `key_width`, `key_height`, `direction`, `background`, and so on) can be scoped to a single legend by adding `by = "<aes>"`. Each call tunes one legend and leaves the others alone, so chaining several short calls is the intended shape. In the example the top legends get bold titles and smaller keys, the right legends get smaller text, `shape` is forced horizontal so it fits the bottom rail, and `linetype` stays vertical for the left rail.

**Step 4, padding.** `margin = c(top, right, bottom, left)` (in cm) moves a single legend toward or away from the panel. Pad on the side that faces the panel: a bottom legend uses `c(top, 0, 0, 0)`, a left legend uses `c(0, right, 0, 0)`. Without padding, legends often end up pressed against the axis text; a few tenths of a centimetre usually fixes it.

### Why one line per parameter

`legend_style(by = "<aes>", ...)` calls are additive. You can fold every style argument for `colour` into a single call, but splitting by concern (position, alignment, appearance, padding) keeps each parameter on a line that is easy to locate. When a reviewer asks to move the colour legend half a centimetre down, the edit is one number on one line.

## Summary

| Function | Purpose | Parameters |
|----------|---------|------------|
| `legend_hide()` | Hide specific legends | Aesthetic names (unquoted) |
| `legend_select()` | Keep only specific legends | Aesthetic names (unquoted) |
| `legend_order_guides()` | Control legend display order | Named args: `aes = order` |
| `legend_merge()` | Force legends to merge | Aesthetic names (unquoted) |
| `legend_split()` | Force legends to stay separate | Aesthetic names (unquoted) |
| `legend_left(by=)` | Position one legend on left | `by = "aesthetic"` |
| `legend_style(by=)` | Style one legend | `by = "aesthetic"` + style args |

**Learn more:**

- [Legend Positioning](positioning.html) for single-legend placement

- [Styling & Customization](styling.html) for legend appearance

- [Patchwork Integration](patchwork.html) for multi-panel plots
