Author: Joseph Larmarange

# Geometries for diverging bar plots

#### Joseph Larmarange

    library(ggstats)
    library(dplyr)
    #>
    #> Attachement du package : 'dplyr'
    #> Les objets suivants sont masqués depuis 'package:stats':
    #>
    #>     filter, lag
    #> Les objets suivants sont masqués depuis 'package:base':
    #>
    #>     intersect, setdiff, setequal, union
    library(ggplot2)
    library(patchwork)

Note : if you are looking for an all-in-one function to display Likert-type items, please refer to `gglikert()` and `vignette("gglikert")`.

## New positions

Diverging bar plots could be achieved using `position_diverging()` or `position_likert()`.

`position_diverging()` stacks bars on top of each other and centers them around zero (the same number of categories are displayed on each side).

    base <-
      ggplot(diamonds) +
      aes(y = clarity, fill = cut) +
      theme(legend.position = "none")

    p_stack <-
      base +
      geom_bar(position = "stack") +
      ggtitle("position_stack()")

    p_diverging <-
      base +
      geom_bar(position = "diverging") +
      ggtitle("position_diverging()")

    p_stack + p_diverging

`position_likert()` is similar but uses proportions instead of counts.

    p_fill <-
      base +
      geom_bar(position = "fill") +
      ggtitle("position_fill()")

    p_likert <-
      base +
      geom_bar(position = "likert") +
      ggtitle("position_likert()")

    p_fill + p_likert

By default, the same number of categories is displayed on each side, i.e. if you have 4 categories, 2 will be displayed negatively and 2 positively. If you have an odd number of categories, half of the central category will be displayed negatively and half positively.

The center could be changed with the `cutoff` argument, representing the number of categories to be displayed negatively: `2` to display negatively the two first categories, `2.5` to display negatively the two first categories and half of the third, `2.2` to display negatively the two first categories and a fifth of the third.

    p_1 <-
      base +
      geom_bar(position = position_diverging(cutoff = 1)) +
      ggtitle("cutoff = 1")

    p_2 <-
      base +
      geom_bar(position = position_diverging(cutoff = 2)) +
      ggtitle("cutoff = 2")

    p_null <-
      base +
      geom_bar(position = position_diverging(cutoff = NULL)) +
      ggtitle("cutoff = NULL")

    p_3.75 <-
      base +
      geom_bar(position = position_diverging(cutoff = 3.75)) +
      ggtitle("cutoff = 3.75")

    p_5 <-
      base +
      geom_bar(position = position_diverging(cutoff = 5)) +
      ggtitle("cutoff = 5")

    wrap_plots(p_1, p_2, p_null, p_3.75, p_5)

## New scales

For a diverging bar plot, it is recommended to use a diverging palette, as provided in the Brewer palettes. Sometimes, the number of available colors is insufficient in the palette. In that case, you could use `pal_extender()` or `scale_fill_extended()`. However, if you use a custom `cutoff`, it is also important to change the center of the palette as well.

Therefore, for diverging bar plots, we recommend to use `scale_fill_likert()`.

    wrap_plots(
      p_1 + scale_fill_likert(cutoff = 1),
      p_null + scale_fill_likert(),
      p_3.75 + scale_fill_likert(cutoff = 3.75)
    )

## Improving axes

You may also want have centered axes. That could be easily achieved with `symmetric_limits()`.

You could also use `label_number_abs()` or `label_percent_abs()` to display absolute numbers.

    wrap_plots(
      p_3.75,
      p_3.75 +
        scale_x_continuous(
          limits = symmetric_limits,
          labels = label_number_abs()
        )
    )

## New geometries

To facilitate the creation of diverging bar plots, you could use variants of `geom_bar()` and `geom_text()`.

### geom_diverging() & geom_diverging_text()

Let’s consider the following plot:

    ggplot(diamonds) +
      aes(y = clarity, fill = cut) +
      geom_bar(position = "diverging") +
      geom_text(
        aes(
          label =
            label_number_abs(hide_below = 800)
            (after_stat(count))
        ),
        stat = "count",
        position = position_diverging(.5)
      ) +
      scale_fill_likert() +
      scale_x_continuous(
        labels = label_number_abs(),
        limits = symmetric_limits
      )

The same could be achieved quicker with `geom_diverging()` and `geom_diverging_text()`.

    ggplot(diamonds) +
      aes(y = clarity, fill = cut) +
      geom_diverging() +
      geom_diverging_text(
        aes(
          label =
            label_number_abs(hide_below = 800)
            (after_stat(count))
        )
      ) +
      scale_fill_likert() +
      scale_x_continuous(
        labels = label_number_abs(),
        limits = symmetric_limits
      )

### geom_likert() & geom_likert_text()

`geom_likert()` and `geom_likert_text()` works similarly. `geom_likert_text()` takes advantages of `stat_prop()` for computing the proportions to be displayed (see `vignette("stat_prop")`).

    ggplot(diamonds) +
      aes(y = clarity, fill = cut) +
      geom_likert() +
      geom_likert_text() +
      scale_fill_likert() +
      scale_x_continuous(
        labels = label_percent_abs()
      )

### geom_pyramid() & geom_pyramid_text()

Finally, `geom_pyramid()` and `geom_pyramid_text()` are variations adapted to display an age-sex pyramid. It uses proportions of the total.

    d <- Titanic |> as.data.frame()
    ggplot(d) +
      aes(y = Class, fill = Sex, weight = Freq) +
      geom_pyramid() +
      geom_pyramid_text() +
      scale_x_continuous(
        labels = label_percent_abs(),
        limits = symmetric_limits
      )