---
title: "Population pyramids with ggpyramid"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Population pyramids with ggpyramid}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 4.5,
  out.width = "100%",
  dpi = 96
)
```

A population pyramid shows a population by age group and sex as two bar
charts set back to back, with the age groups stacked from youngest at the
bottom to oldest at the top. ggpyramid builds it from ordinary ggplot2 layers,
so scales, facets, themes and further layers work as they do in any other
plot.

```{r setup}
library(ggplot2)
library(ggpyramid)
```

The examples use `europe_pop`, the population of four European countries in
2004 and 2024 by five-year age group and sex, from Eurostat:

```{r}
head(europe_pop)
```

## A first pyramid

`geom_pyramid()` draws the bars. Map age to `y`, the side to `fill`, and the
population to `x`. `coord_pyramid()` labels both halves of the value axis
without a minus sign and gives them the same range.

```{r first}
france <- subset(europe_pop, country == "France" & year == 2024)

ggplot(france, aes(x = population, y = age, fill = sex)) +
  geom_pyramid() +
  scale_x_continuous(labels = scales::label_comma()) +
  coord_pyramid()
```

The first level of the side variable goes on the left, so `Male` is drawn on
the left here because it is the first level of `sex`. `left` in
`coord_pyramid()` chooses the other side for every layer of the plot at once.
The legend keeps the order of the levels, and `breaks` in the fill scale
reorders it to match:

```{r left}
ggplot(france, aes(x = population, y = age, fill = sex)) +
  geom_pyramid() +
  scale_x_continuous(labels = scales::label_comma()) +
  scale_fill_discrete(breaks = c("Female", "Male")) +
  coord_pyramid(left = "Female")
```

A single layer can take another side with `position_pyramid(left = ...)`,
which overrides the coordinate system. Without `coord_pyramid()`, as in a
vertical pyramid, reorder the levels of the side variable instead.

`geom_pyramid()` sums the value it is given within each age group, and counts
rows when no value is mapped, so the same code works for aggregated tables and
for individual records:

```{r, eval = FALSE}
# One row per person
ggplot(people, aes(y = age_group, fill = sex)) +
  geom_pyramid()
```

## Placing the age axis

`coord_pyramid()` places the age axis on the `"left"` (the default), on the
`"right"`, on `"both"` sides, or in the `"centre"`, where the labels sit in a
gap between the two halves. `theme_pyramid()` removes the grid lines that run
along the bars and moves the legend to the top.

```{r centre}
p <- ggplot(france, aes(x = population, y = age, fill = sex)) +
  geom_pyramid(width = 1, colour = "white") +
  scale_x_continuous(
    labels = scales::label_number(scale = 1e-6, suffix = "M")
  ) +
  labs(x = NULL, y = NULL, fill = NULL) +
  theme_pyramid()

p + coord_pyramid(age_axis = "centre")
```

The width of the gap is a proportion of the panel, `0.12` by default. Widen it
with `gap` when the age labels are long, or open a narrow gap with the age
axis elsewhere. A gap draws the zero of the value axis on both of its edges,
so keep it wide enough for two labels:

```{r both}
p + coord_pyramid(age_axis = "both", gap = 0.08)
```

## Proportions and facets

`denominator = "total"` shows each bar as a share of the whole population in
the panel, and `denominator = "side"` as a share of its own side. Shares make
populations of different sizes comparable, and each facet panel gets its own
denominator.

```{r facets, fig.height = 6}
ggplot(europe_pop, aes(x = population, y = age, fill = sex)) +
  geom_pyramid(denominator = "total") +
  scale_x_continuous(labels = scales::label_percent()) +
  coord_pyramid() +
  facet_grid(year ~ country) +
  labs(x = "Share of the population", y = NULL, fill = NULL) +
  theme_pyramid()
```

## Labels

`geom_pyramid_text()` writes the value of each bar at its end. It aggregates
the data in the same way as the bars, and `labels` formats the numbers.
Labels go `"outside"` the bars by default, or `"inside"` them.

```{r labels}
ireland <- subset(europe_pop, country == "Ireland" & year == 2024)

ggplot(ireland, aes(x = population, y = age, fill = sex)) +
  geom_pyramid(denominator = "side") +
  geom_pyramid_text(
    denominator = "side",
    labels = scales::label_percent(accuracy = 0.1),
    size = 2.6
  ) +
  scale_x_continuous(
    labels = scales::label_percent(),
    expand = expansion(mult = 0.15)
  ) +
  coord_pyramid(age_axis = "centre") +
  labs(x = "Share of each sex", y = NULL, fill = NULL) +
  theme_pyramid()
```

## Comparing two populations

`geom_pyramid_outline()` traces the edge of a pyramid as a stepped line. Over
the bars of one year it shows how the population has changed since another.
Here Italy in 2024 is drawn as bars and Italy in 2004 as the outline.

```{r outline}
italy <- subset(europe_pop, country == "Italy")

ggplot(italy, aes(x = population, y = age, fill = sex)) +
  geom_pyramid(data = subset(italy, year == 2024), width = 1, alpha = 0.8) +
  geom_pyramid_outline(data = subset(italy, year == 2004), linewidth = 0.6) +
  scale_x_continuous(
    labels = scales::label_number(scale = 1e-6, suffix = "M")
  ) +
  coord_pyramid(age_axis = "centre") +
  labs(
    x = NULL,
    y = NULL,
    fill = "2024",
    caption = "The outline shows 2004."
  ) +
  theme_pyramid()
```

## Subgroups within each side

The side of a bar comes from `fill` unless the `side` aesthetic is mapped.
Mapping `side` separately frees `fill` for subgroups, which are stacked within
each side:

```{r subgroups}
recent <- subset(europe_pop, year == 2024 & country %in% c("Ireland", "Poland"))

ggplot(recent, aes(x = population, y = age, fill = country, side = sex)) +
  geom_pyramid(width = 1) +
  scale_x_continuous(
    labels = scales::label_number(scale = 1e-6, suffix = "M")
  ) +
  coord_pyramid(age_axis = "centre") +
  labs(x = NULL, y = NULL, fill = NULL) +
  theme_pyramid()
```

## Vertical pyramids

Map age to `x` for a vertical pyramid. `coord_pyramid()` is for horizontal
pyramids only, so use `scale_y_pyramid()` to label the value axis and
`theme_pyramid(flipped = TRUE)` for the grid.

```{r vertical}
ggplot(france, aes(x = age, y = population, fill = sex)) +
  geom_pyramid() +
  scale_y_pyramid(labels = scales::label_number(scale = 1e-6, suffix = "M")) +
  labs(x = NULL, y = NULL, fill = NULL) +
  theme_pyramid(flipped = TRUE) +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
```

## How the sides are drawn

`position_pyramid()` makes the values on the left side negative inside the
plot, and stacks subgroups on each side away from the centre. Your data stays
as it is. The signs matter when you set limits or draw reference lines on the
left side, which take negative values:

```{r, eval = FALSE}
geom_vline(xintercept = c(-1e6, 1e6), linetype = "dashed")
coord_pyramid(xlim = c(-3e6, 3e6))
```

Because the mirroring is done by the position, any layer can join a pyramid
with `position = "pyramid"`, and the plot stays correct with another
coordinate system. Only the presentation that `coord_pyramid()` adds, such as
the absolute labels and the centre gap, depends on the coordinate system.
