---
title: "The Grammar of Graphics"
output:
  html_document:
    toc: yes
    code_download: true
---

<link rel="stylesheet" href="stat4580.css" type="text/css" />

```{r setup, include = FALSE}
source(here::here("setup.R"))
knitr::opts_chunk$set(collapse = TRUE, message = FALSE,
                      fig.height = 5, fig.width = 6, fig.align = "center")

library(dplyr)
library(ggplot2)
library(lattice)
library(gridExtra)
set.seed(12345)
```


## Background

The _Grammar of Graphics_ is a language proposed by Leland Wilkinson
for describing statistical graphs.

> Wilkinson, L. (2005), _The Grammar of Graphics_, 2nd ed., Springer.

The grammar of graphics has served as the foundation for the graphics
frameworks in [SPSS](https://www.ibm.com/products/spss-statistics),
[Vega-Lite](https://vega.github.io/vega-lite/) and several other
systems.

`ggplot2` represents an implementation and extension of the grammar
of graphics for R.

> Wickham, H. (2016), _ggplot2: Elegant Graphics for Data Analysis_,
>  2nd ed., Springer. [3rd ed. in progress](https://ggplot2-book.org/).

> On line documentation: <https://ggplot2.tidyverse.org/reference/index.html>.

> Wickham. H., and Grolemund, G. (2016),
>  [_R for Data Science_](https://r4ds.had.co.nz/), O'Reilly.

> [Data visualization cheatsheet](https://raw.githubusercontent.com/rstudio/cheatsheets/main/data-visualization.pdf)

> Winston Chang (2018), [_R Graphics Cookbook_, 2nd
> edition](https://r-graphics.org/), O’Reilly. ([Book source on
> GitHub](https://github.com/wch/rgcookbook))

The idea is that any basic plot can be built out of a combination of

* a data set;

* one or more geometrical representation (_geoms_);

* mappings of values to _aesthetic_ features of the geom;

* a _stat_ to produce values to be mapped;

* position adjustments;

* a coordinate system;

* a scale specification;

* a faceting scheme.

`ggplot2` provides tools for specifying these components and adjusting
their features.

Many components and features are provided by default and do not need
to be specified explicitly unless the defaults are to be changed.


## A Basic Template

The simplest graph needs a data set, a geom, and a mapping:

```r
ggplot(data = <DATA>) + <GEOM>(mapping = aes(<MAPPINGS>))
```

The appearance of geom objects is controlled by _aesthetic_ features.

Each geom has some required and some optional aesthetics.

For `geom_point` the required aesthetics are

* `x` position
* `y` position.

Optional aesthetics include

* `color`
* `fill`
* `shape`
* `size`

`geom_point` is used to produce a _scatter plot_.


## Scatter Plots Using `geom_point`

The `mpg` data set included in the `ggpllot2` package includes EPA
fuel economy data from 1999 to 2008 for 38 popular models of cars.

```{r}
mpg
```

A simple scatter plot:

```{r mpg-plain, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy))
```

```{r mpg-plain, echo = FALSE, fig.width = 5.75, fig.align = "center"}
```

Map color to vehicle class:

```{r mpg-color, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class))
```

```{r mpg-color, echo = FALSE, fig.width = 7, fig.align = "center"}
```

And map shape to number of cylinders:

```{r mpg-color-shape, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class,
                   shape = factor(cyl)))
```

```{r mpg-color-shape, echo = FALSE, fig.width = 7, fig.align = "center"}
```

<!-- --> Perception:
* Too many colors; 
* shapes are too small; 
* interference between shapes and colors.

Aesthetics can be mapped to a variable or set to a fixed common value.

This can be used to override default settings:

```{r mpg-fixed, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy),
               color = "blue",
               shape = 1)
```

```{r mpg-fixed, echo = FALSE, fig.width = 7, fig.align = "center"}
```

Changing the `size` aesthetics makes shapes easier to recognize:

```{r mpg-color-shape-large, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class,
                   shape = factor(cyl)),
               size = 3)
```

```{r mpg-color-shape-large, echo = FALSE, fig.width = 7, fig.align = "center"}
```

<!-- --> Perception: Still too many colors; still have interference.

Available point shapes are specified by number:

```{r, echo = FALSE, eval = FALSE}
generateRPointShapes <- function() {
    oldPar <- par()
    par(font = 2, mar = c(0.5, 0, 0, 0))
    y <- rev(c(rep(1, 6), rep(2, 5), rep(3, 5), rep(4, 5), rep(5, 5)))
    x <- c(rep(1 : 5, 5), 6)
    plot(x, y, pch = 0 : 25, cex = 1.5, ylim = c(1, 5.5), xlim = c(1, 6.5),
         axes = FALSE, xlab = "", ylab = "", bg = "blue")
    text(x, y, labels = 0 : 25, pos = 3)
    par(mar = oldPar$mar, font = oldPar$font)
}
generateRPointShapes()
```
```{r, echo = FALSE}
ggplot(NULL, aes(x = rep(1 : 5, 5), y = rev(rep(1 : 5, each = 5)))) +
    geom_point(shape = 1 : 25, size = 5, fill = "blue") +
    geom_text(aes(label = 1 : 25), nudge_y = 0.25, size = 6) +
    theme_void()
```

Shapes 1-20 have their color set by the `color` aesthetic and ignore
the `fill` aesthetic.

For shapes 21-25 the `color` aesthetic specifies the border color and
`fill` specifies the interior color.

Using `shape` 21 with `cyl` mapped to the `fill` aesthetic:

```{r mpg-fill-21, eval = FALSE}
ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 4)
```
```{r mpg-fill-21, echo = FALSE}
```

<!-- --> Perception: Borders, larger symbols, fewer colors help.

Specifying a new default is very different from specifying a constant
value as an aesthetic.

Constant aesthetic: Rarely what you want:

```{r mpg-bad-color, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = "blue"))
```

```{r mpg-bad-color, echo = FALSE, fig.height = 4.2}
```

Default: Probably what you want:
```{r mpg-good-color, eval = FALSE}
ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy),
               color = "blue")
```

```{r mpg-good-color, echo = FALSE, fig.height = 4.2}
```


## Geometric Objects

`ggplot2` provides a number of geoms:

```{r, echo = FALSE, results = "asis"}
showList <- function(v, ncol = 4, pad = 2) {
    w <- max(nchar(v)) + pad
    nrow <- ceiling(length(v) / ncol)
    v <- c(v, character(ncol * nrow - length(v)))

    cat("```r\n")
    for (i in seq_len(nrow)) {
        line <- v[ncol * (i - 1) + (1 : ncol)]
        for (j in 1 : ncol)
            if (j < ncol)
                cat(sprintf("%-*s", w, line[j]))
            else
                cat(sprintf("%s\n", line[j]))
        ## cat(sprintf("%-*s%-*s%-*s%s\n",
        ##             w, line[1], w, line[2], w, line[3], line[4]))
    }
    cat("```\n")
}
showList(ls("package:ggplot2", pat = "^geom_"))
```

Additional geoms are available in packages like `ggforce`, `ggridges`,
and others described on the [`ggplot2` extensions
site](https://exts.ggplot2.tidyverse.org/).

Geoms can be added as _layers_ to a plot.

Mappings common to all, or most, geoms can be specified in the
`ggplot` call:

```{r mpg-smooth, eval = FALSE}
ggplot(mpg,
       aes(x = displ,
           y = hwy)) +
    geom_smooth() +
    geom_point()
```

```{r mpg-smooth, echo = FALSE, message = FALSE}
```

Geoms can also use different data sets.

One way to highlight Europe in a plot of life expectancy against log
income for 2007 is to start with a plot of the full data:

```{r gm_2007, eval = FALSE}
library(dplyr)
library(gapminder)
gm_2007 <- filter(gapminder, year == 2007)

(p <- ggplot(gm_2007, aes(x = gdpPercap,
                          y = lifeExp)) +
     geom_point() +
     scale_x_log10())
```

```{r gm_2007, echo = FALSE}
```

Then add a layer showing only Europe:

```{r gm_2007_eu, eval = FALSE}
gm_2007_eu <- filter(gm_2007, continent == "Europe")

p + geom_point(data = gm_2007_eu,
               color = "red",
               size = 3)
```

```{r gm_2007_eu, echo = FALSE}
```


## Statistical Transformations

All geoms use a statistical transformation (_stat_) to convert raw
data to the values to be mapped to the object's features.

The available stats are

```{r, echo = FALSE, results = "asis"}
showList(ls("package:ggplot2", pat = "^stat_"), ncol = 3)
```

Each geom has a default stat, and each stat has a default geom.

* For `geom_point` the default stat is `stat_identity`.

* For `geom_bar` the default stat is `stat_count`.

* For `geom_histogram` the default is `stat_bin`.

Stats can provide _computed variables_ that can be mapped to aesthetic
features.

For `stat_bin` some of the computed variables are

* `count`: number of points in bin
* `density`: density of points in bin, scaled to integrate to 1

The `density` variable can be accessed as `after_stat(dentity)`.

Older approaches that also work but are now discouraged:

* `stat(dentity)`
* `..density..`

By default, `geom_histogram` uses `y = after_stat(count)`.

```{r geyser-count, eval = FALSE}
ggplot(faithful) +
    geom_histogram(aes(x = eruptions),
                   binwidth = 0.25,
                   fill = "grey",
                   color = "black")
```
```{r geyser-count, echo = FALSE}
```

Explicitly specifying `y = after_stat(count)` produces the same plot:

```{r geyser-count-exp, eval = FALSE}
ggplot(faithful) +
    geom_histogram(aes(x = eruptions,
                       y = after_stat(count)),
                   binwidth = 0.25,
                   fill = "grey",
                   color = "black")
```
```{r geyser-count-exp, echo = FALSE}
```

Using `y = after_stat(density)` produces a density scaled axis.

```{r geyser-dentity, eval = FALSE}
(p <- ggplot(faithful) +
     geom_histogram(aes(x = eruptions,
                        y = after_stat(density)),
                    binwidth = 0.25,
                    fill = "grey",
                    color = "black"))
```
```{r geyser-dentity, echo = FALSE}
```

`stat_function` can be used to add a density curve specified as a
mixture of two normal densities:

```{r}
(ms <- mutate(faithful,
              type = ifelse(eruptions < 3,
                            "short",
                            "long")) %>%
     group_by(type) %>%
     summarize(mean = mean(eruptions),
               sd = sd(eruptions),
               n = n()) %>%
     mutate(p = n / sum(n)))
```

```{r geyser-hist-dens, eval = FALSE}
f <- function(x)
    ms$p[1] * dnorm(x, ms$mean[1], ms$sd[1]) +
        ms$p[2] * dnorm(x, ms$mean[2], ms$sd[2])

p + stat_function(fun = f, color = "red")
```
```{r geyser-hist-dens, echo = FALSE}
```


## Position Adjustments

The available position adjustments:

```{r, echo = FALSE, results = "asis"}
showList(ls("package:ggplot2", pat = "^position_"), ncol = 3)
```

A bar chart showing the counts for the different `cut` categories in
the `diamonds` data:

```{r diamonds-cut, eval = FALSE}
ggplot(diamonds, aes(x = cut)) +
    geom_bar()
```
```{r diamonds-cut, echo = FALSE}
```

Mapping `clarity` to `fill` shows the breakdown by both `cut` and
`clarity` in a _stacked bar chart_:

```{r diamonds-stack1, eval = FALSE}
ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar()
```
```{r diamonds-stack1, echo = FALSE}
```

The default `position` for bar charts is `position_stack`:

```{r diamonds-stack2, eval = FALSE}
ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "stack")
```
```{r diamonds-stack2, echo = FALSE}
```

`position_dodge` produces _side-by-side bar charts_:

```{r diamonds-dodge, eval = FALSE}
ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "dodge")
```
```{r diamonds-dodge, echo = FALSE}
```

`position_fill` rescales all bars to be equal height to help compare
proportions within bars.

```{r diamonds-fill, eval = FALSE}
ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "fill")
```
```{r diamonds-fill, echo = FALSE}
```

Using the counts to scale the widths would produce a _spine plot_, a
variant of a _mosaic plot_.

This is easiest to do with the `ggmosaic` package.

`position_jitter` can be used with `geom_point` to avoid overplotting
or break up rounding artifacts.

Another version of the Old Faithful data available as `geyser` in
package `MASS` has some rounding in the `duration` variable:

```{r geyser2, eval = FALSE}
data(geyser, package = "MASS")

## Adjust for different meaning of `waiting` variable
geyser2 <- na.omit(mutate(geyser,
                          duration = lag(duration)))

p <- ggplot(geyser2, aes(x = duration, y = waiting))
p + geom_point()
```

```{r geyser2, echo = FALSE}
```

_Jittering_ can help break up the distracting _heaping_ of values on
durations of 2 and 4 minutes.

The default amount of jittering isn't quite enough in this case:

```{r geyser2-jit, eval = FALSE}
p + geom_point(position = "jitter")
```
```{r geyser2-jit, echo = FALSE}
```

To jitter only horizontally and by a larger amount you can use

```{r geyser2-jit2, eval = FALSE}
p + geom_point(position =
                   position_jitter(height = 0,
                                   width = 0.1))
```
```{r geyser2-jit2, echo = FALSE}
```


## Coordinate Systems

Coordinate system functions include

```{r, echo = FALSE, results = "asis"}
showList(ls("package:ggplot2", pat = "^coord_"))
```

The default coordinate system is `coord_cartesian`.


### Cartesian Coordinates

`coord_cartesian` can be used to _zoom in_ on a particular regiion:

```{r geyser2-zoom, eval = FALSE}
p + geom_point() +
    coord_cartesian(xlim = c(3, 4))
```
```{r geyser2-zoom, echo = FALSE}
```

`coord_fixed` and `coord_equal` fix the _aspect ratio_ for a cartesian
coordinate system.

The aspect ratio is the ratio of the number physical display units per
`y` unit to the number of physical display units per `x` unit.

The aspect ratio can be important for recognizing features and patterns.

```{r}
river <- scan("https://www.stat.uiowa.edu/~luke/data/river.dat")
r <- data.frame(flow = river, month = seq_along(river))
```

```{r river-flat, eval = FALSE}
ggplot(r, aes(x = month, y = flow)) +
    geom_point() +
    coord_fixed(ratio = 4)
```
```{r river-flat, echo = FALSE, fig.height = 2, fig.width = 8}
```


### Polar Coordinates

A filled bar chart

```{r diamonds-fill-1, eval = FALSE}
(p <- ggplot(diamonds) +
     geom_bar(aes(x = 1, fill = cut),
              position = "fill"))
```
```{r diamonds-fill-1, echo = FALSE}
```

is turned into a pie chart by changing to polar coordinates:

```{r diamonds-pie, eval = FALSE}
p + coord_polar(theta = "y")
```
```{r diamonds-pie, echo = FALSE}
```


### Coordinate Systems for Maps

Coordinate systems are particularly important for maps.

Polygons for many political and geographic boundaries are available
through the `map_data` function.

Boundaries for the lower 48 US states can be obtained as

```{r}
usa <- map_data("state")
```

Polygon vertices are encoded by longitude and latitude.

Plotting these in the default cartesian coordinate system usually does
not work well:

```{r usa-cart, eval = FALSE}
usa <- map_data("state")
m <- ggplot(usa, aes(x = long,
                     y = lat,
                     group = group)) +
    geom_polygon(fill = "white",
                 color = "black")
m
```
```{r usa-cart, echo = FALSE}
```

Using a fixed aspect ratio is better, but an aspect ratio of 1 does
not work well:

```{r}
m + coord_equal()
```

The problem is that away from the equator a one degree change in
latitude corresponds to a larger distance than a one degree change in
longitude.

The ratio of one degree longitude separation to one degree latitude
separation for the latitude at the middle of Iowa of 41 degrees is

```{r}
longlat <- cos(41 / 90 * pi / 2)
longlat
```

A better map is obtained using the aspect ratio `1 / longlat`:

```{r usa-fixed, eval = FALSE}
m + coord_fixed(1 / longlat)
```
```{r usa-fixed, echo = FALSE}
```

The best approach is to use a coordinate system designed specifically for
maps.

There are many _projections_ used in map making.

The default projection used by `coord_map` is the
[Mercator](https://en.wikipedia.org/wiki/Mercator_projection)
projection.

```{r usa-mercator, eval = FALSE}
m + coord_map()
```

```{r usa-mercator, echo = FALSE}
```

Proper map projections are non-linear; this is easier to see with an
Albers projection:

```{r usa-albers, eval = FALSE}
m + coord_map("albers", 20, 50)
```
```{r usa-albers, echo = FALSE}
```


## Scales

Scales are used for controlling the mapping of values to physical
representations such as colors, shapes, and positions.

Scale functions are also responsible for producing _guides_ for
translating physical representations back to values, such as

* axis labels and marks;

* color or shape legends.

There are currently `r length(ls("package:ggplot2", pat = "scale_"))`
scale functions; some examples are

```r
scale_color_gradient      scale_shape_manual     scale_x_log10
scale_color_manual        scale_size_area        scale_y_log10
scale_fill_gradient                              scale_x_sqrt
scale_fill_manual                                scale_y_sqrt

```

An [experimental tool](https://ggplot2tor.com/scales/) to help
choosing scales has recently been introduced.

Start with a basic scatter plot:

```{r mpg-basic, eval = FALSE}
(p <- ggplot(mpg, aes(x = displ,
                      y = hwy)) +
     geom_point())
```
```{r mpg-basic, echo = FALSE}
```

Remove the `x` tick marks and labels (this can also be done with theme
settings):

```{r mpg-no-ticks-labs, eval = FALSE}
p + scale_x_continuous(labels = NULL,
                       breaks = NULL)
```
```{r mpg-no-ticks-labs, echo = FALSE}
```

Change the tick locations and labels:

```{r mpg-new-ticks-labs, eval = FALSE}
p + scale_x_continuous(labels =
                           paste(c(2, 4, 6), "ltr"),
                       breaks = c(2, 4, 6))
```
```{r mpg-new-ticks-labs, echo = FALSE}
```

Use a logarithmic axis:

```{r mpg-log-x, eval = FALSE}
p + scale_x_log10(labels = paste(c(2, 4, 6), "ltr"),
                  breaks = c(2, 4, 6),
                  minor_breaks = c(3, 5, 7))
```
```{r mpg-log-x, echo = FALSE}
```

The
[Scales](https://r4ds.had.co.nz/graphics-for-communication.html#scales)
section in [R for Data Science](https://r4ds.had.co.nz/) provides some
more details.

Color assignment can also be controlled by scale functions.

For example, for some presidential approval ratings data

```{r, include = FALSE}
pr_appr <- data.frame(pres = c("Obama", "Carter", "Clinton",
                               "G.W. Bush", "Reagan", "G.H.W Bush", "Trump"),
                      appr = c(79, 78, 68, 65, 58, 56, 40),
                      party = c("D", "D", "D", "R", "R", "R", "R"),
                      year = c(2009, 1977, 1993, 2001, 1981, 1989, 2017))
pr_appr <- mutate(pr_appr, pres = reorder(pres, appr))
```
```{r}
pr_appr
```

the default color scale is not ideal:

```{r pr-appr0, eval = FALSE}
ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col()
```

```{r pr-appr0, echo = FALSE}
```

The common assignment of red for Republican and blue for Democrat can
be obtained by

```{r pr-appr, eval = FALSE}
ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col() +
    scale_fill_manual(values
                      = c(R = "red", D = "blue"))
```

```{r pr-appr, echo = FALSE}
```

A better choice is to use a well-designed [color
palette](https://hclwizard.org/#color-palettes):

```{r pr-appr-2, eval = FALSE}
ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col() +
    colorspace::scale_fill_discrete_diverging(
                    palette = "Blue-Red 2")
```

```{r pr-appr-2, echo = FALSE}
```


## Facets

Faceting uses the _small multiples_ approach to introduce additional
variables.

For a single variable `facet_wrap` is usually used:

```{r mpg-facet-wrap, eval = FALSE}
p <- ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy))
p + facet_wrap(~ class)
```
```{r mpg-facet-wrap, echo = FALSE}
```

For two variables, each with a modest number of categories,
`facet_grid` can be effective:

```{r mpg-facet-grid, eval = FALSE}
p + facet_grid(factor(cyl) ~ drv)
```
```{r mpg-facet-grid, echo = FALSE}
```

<!--
Using the previous mpg facet plot would be better but this is one of
the homework problems in HW3
-->

To show common data in all facets make sure the data does not contain the
faceting variable.

This was used to show muted views of the full data in faceted plots.

A faceted plot of the `gapminder` data:

```{r gapminder-not-muted, eval = FALSE}
library(gapminder)

years_to_keep <- c(1977, 1987, 1997, 2007)
gd <- filter(gapminder,
             year %in% years_to_keep)

ggplot(gd,
       aes(x = gdpPercap,
           y = lifeExp,
           color = continent)) +
    geom_point(size = 2.5) +
    scale_x_log10() +
    facet_wrap(~ year)
```

```{r gapminder-not-muted, echo = FALSE, fig.width = 8}
```

Add a muted version of the full data in the background of each panel:

<!-- variant of code in prercep.Rmd -->
```{r gapminder-muted, eval = FALSE}
library(gapminder)

years_to_keep <- c(1977, 1987, 1997, 2007)
gd <- filter(gapminder,
             year %in% years_to_keep)
gd_no_year <- mutate(gd, year = NULL)

ggplot(gd,
       aes(x = gdpPercap,
           y = lifeExp,
           color = continent)) +
    geom_point(data = gd_no_year,
               color = "grey80") +
    geom_point(size = 2.5) +
    scale_x_log10() +
    facet_wrap(~ year)
```

```{r gapminder-muted, echo = FALSE, fig.width = 8}
```

Usually facets use common axis scales, but one or both can be allowed
to vary.

A useful approach for showing time series data with a good aspect
ratio can be to split the data into facets for non-overlapping
portions of the time axis.

```{r river-facet, eval = FALSE}
pd <- rep(paste(seq(1, by = 32, length.out = 4),
                seq(32, by = 32, length.out = 4),
                sep = " - "),
         each =  32)
rd <- data.frame(month = seq_along(river),
                 flow = river,
                 panel = pd)
ggplot(rd, aes(x = month,
               y = flow)) +
    geom_point() +
    facet_wrap(~ panel,
               scale = "free_x",
               ncol = 1)
```
```{r river-facet, echo = FALSE}
```

Facet arrangement can also be used to convey other information, such
as geographic location.

The [`geofacet` package](https://hafen.github.io/geofacet/) allows
facets to be placed in approximate locations of different geographic
regions.

An example for data from US states:

```{r geofacet, eval = FALSE}
library(geofacet)
ggplot(state_unemp, aes(year, rate)) +
    geom_line() +
    facet_geo(~ state,
              grid = "us_state_grid2",
              label = "code") +
    scale_x_continuous(labels =
                           function(x) paste0("'", substr(x, 3, 4))) +
    labs(title = "Seasonally Adjusted US Unemployment Rate 2000-2016",
         caption = "Data Source: bls.gov",
         x = "Year",
         y = "Unemployment Rate (%)") +
    theme(strip.text.x = element_text(size = 6),
          axis.text = element_text(size = 5))
```
```{r geofacet, echo = FALSE, message = FALSE}
```

Arrangement according to a calendar can also be useful.


## Themes

`ggplot2` supports the notion of _themes_ for adjusting non-data
appearance aspects of a plot, such as

* plot titles

* axis and legend placement and titles

* background colors

* guide line placement

Theme elements can be customized in several ways:

* `theme()` can be used to adjust individual elements in a plot.

* `theme_set()` adjusts default settings for a session;

* pre-defined theme functions allow consistent style changes.

The
[full documentation](https://ggplot2.tidyverse.org/reference/theme.html)
of the `theme` function lists many customizable elements.

One simple example:

```{r theme-simple, eval = FALSE}
ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 3) +
    theme(legend.position = "top",
          axis.text = element_text(size = 12),
          axis.title = element_text(size = 14,
                                    face = "bold"))
```
```{r theme-simple, echo = FALSE}
```

Another example:

```{r theme-simple-2, eval = FALSE}
gthm <-
    theme(plot.background =
              element_rect(fill = "lightblue",
                           color = NA),
          panel.background =
              element_rect(fill = "pink"))
p + gthm
```
```{r theme-simple-2, echo = FALSE}
```

Some alternate complete themes provided by `ggplot2` are

```r
theme_bw        theme_gray      theme_minimal   theme_void
theme_classic   theme_grey      theme_dark      theme_light
```

Some examples:

```{r alt-themes, eval = FALSE}
p_bw <- p + theme_bw() + ggtitle("BW")

p_classic <- p + theme_classic() + ggtitle("Classic")

p_min <- p + theme_minimal() + ggtitle("Minimal")

p_void <- p + theme_void() + ggtitle("Void")

library(patchwork)
(p_bw + p_classic) / (p_min + p_void)
```
```{r alt-themes, echo = FALSE}
```

The
[`ggthemes`](http://www.rpubs.com/Mentors_Ubiqum/ggthemes_1)
package provides some additional themes.

Some examples:

```{r ggthemes-examples, eval = FALSE}
library(ggthemes)

p_econ <- p + theme_economist() + ggtitle("Economist")

p_wsj <- p + theme_wsj() + ggtitle("WSJ")

p_tufte <- p + theme_tufte() + ggtitle("Tufte")

p_few <- p + theme_few() + ggtitle("Few")

(p_econ + p_wsj) / (p_tufte + p_few)
```
```{r ggthemes-examples, echo = FALSE}
```

`ggthemes` also provides `theme_map` that removes unnecessary elements
from maps:

```{r}
m + coord_map() + theme_map()
```

The
[Themes](https://r4ds.had.co.nz/graphics-for-communication.html#themes)
section in [R for Data Science](https://r4ds.had.co.nz/) provides some
more details.


## A More Complete Template

```r
ggplot(data = <DATA>) +
    <GEOM>(mapping = aes(<MAPPINGS>),
           stat = <STAT>,
           position = <POSITION>) +
    < ... MORE GEOMS ... > +
    <COORDINATE_ADJUSTMENT> +
    <SCALE_ADJUSTMENT> +
    <FACETING> +
    <THEME_ADJUSTMENT>
```


## Labels and Annotations

A basic plot:

```{r mpg-ann, eval = FALSE}
p <- ggplot(mpg, aes(x = displ,
                     y = hwy))
p1 <- p + geom_point(aes(color = factor(cyl)),
                     size = 2.5)
p1
```
```{r mpg-ann, echo = FALSE}
```

Axis labels are based on the expressions given to `aes`.

This is convenient for exploration but usually not ideal for a report.

The `labs()` function can be used to change axis and legend labels:

```{r mpg-ann-labs, eval = FALSE}
p1 + labs(x = "Displacement (Liters)",
          y = "Highway Miles Per Gallon",
          color = "Cylinders")
```
```{r mpg-ann-labs, echo = FALSE}
```

The `labs()` function can also add a title, subtitle, and caption:

```{r mpg-ann-labs-2, eval = FALSE}
p2 <- p1 +
    labs(x = "Displacement (Liters)",
         y = "Highway Miles Per Gallon",
         color = "Cylinders",
         title = "Gas Mileage and Displacement",
         subtitle = paste("For models which had a new release every year",
                           "between 1999 and 2008"),
         caption = "Data Source: https://fueleconomy.gov/")
p2
```
```{r mpg-ann-labs-2, echo = FALSE}
```

Annotations can be used to provide popout that draws a viewer's
attention to particular features.

The `annotate()` function is one option:

```{r mpg-ann-popout, eval = FALSE}
p2 +
    annotate("label", x = 2.8, y = 43,
             label = "Volkswagens") +
    annotate("rect",
             xmin = 1.7, xmax = 2.1,
             ymin = 40, ymax = 45,
             fill = NA, color = "black")
```
```{r mpg-ann-popout, echo = FALSE}
```

Often more convenient are some `geom_mark` objects provided by the
`ggforce` package:

```{r mpg-ann-popout-2, eval = FALSE}
library(ggforce)
p2 +
    geom_mark_hull(aes(filter = class == "2seater"),
                   description =
                       paste("2-Seaters have high displacement",
                             "values, but also high fuel efficiency",
                             "for their displacement.")) +
    geom_mark_rect(aes(filter = hwy > 40),
                   description =
                       "These are Volkswagens") +
    geom_mark_circle(aes(filter = hwy == 12),
                     description =
                         "Three pickups and an SUV.")
```
```{r mpg-ann-popout-2, echo = FALSE, fig.width = 7, fig.height = 5.5}
#| warning: false
```

These annotations can be customized in a number of ways.


## Arranging Plots

There are several tools available for assembling ensemble plots.

The [`patchwork`](https://patchwork.data-imaginist.com/) package is a
good choice.

A simple example:

```{r mpg-patchwork, eval = FALSE}
p1 <- ggplot(mpg, aes(x = displ,
                      y = hwy)) +
    geom_point()
p2 <- ggplot(mpg, aes(x = cyl,
                      y = hwy,
                      group = cyl)) +
    geom_boxplot()
p3 <- ggplot(mpg, aes(x = cyl)) +
    geom_bar()

library(patchwork)
(p1 + p2) / p3
```
```{r mpg-patchwork, echo = FALSE}
```


## Interaction


### Plotly

The `ggplotly` function in the [`plotly` package](https://plotly.com/r/)
can be used to add some interactive features to a plot created with
`ggplot2`.

* In an R session a call to `ggplotly()` opens a browser window with the
  interactive plot.

* In an Rmarkdown document the interactive plot is embedded in the
  `html` file.

Another interactive plotting approach that can be used from R is
described in an [Infoworld
article](https://www.infoworld.com/article/3607068/plot-in-r-with-echarts4r.html).

A simple example using `ggplotly()`:

```{r mpg-plotly, eval = FALSE}
library(ggplot2)
library(plotly)
p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 3)
ggplotly(p)
```
```{r mpg-plotly, echo = FALSE, message = FALSE}
```

Adding a `text` aesthetic allows the tooltip display to be customized:

```{r mpg-plotly-2, eval = FALSE}
p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl,
                   text = paste(year,
                                manufacturer,
                                model)),
               shape = 21,
               size = 3)
ggplotly(p, tooltip = "text") %>%
    style(hoverlabel = list(bgcolor = "white"))
```
```{r mpg-plotly-2, echo = FALSE, warning = FALSE, message = FALSE}
```


### Ggiraph

The [`ggiraph` package](https://davidgohel.github.io/ggiraph/)
provides another approach.

```{r mpg-ggiraph, eval = FALSE}
library(ggplot2)
library(ggiraph)
p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point_interactive(aes(x = displ,
                   y = hwy,
                   fill = cyl,
                   tooltip = paste(year,
                                   manufacturer,
                                   model)),
               shape = 21,
               size = 3)
girafe(ggobj = p)
```

```{r mpg-ggiraph, echo = FALSE}
```


### Grammar of Interactive Graphics

There have been several efforts to develop a grammar of interactive
graphics, including [`ggvis`](https://ggvis.rstudio.com/) and
[`animint`](https://tdhock.github.io/animint/); neither seems to be
under active development at this time.

A promising approach is
[Vega-Lite](https://vega.github.io/vega-lite/), with a Python
interface [Altair](https://altair-viz.github.io/) and an R interface
[altair](https://vegawidget.github.io/altair/) to the Python
interface.

An example using the `altair` package:

```{r rubber-altair, eval = FALSE}
rub <- read.csv(here::here("rubber.csv"))

library(altair)

chartTH <- alt$Chart(rub)$
    mark_point()$
    encode(x = alt$X("H:Q", scale = alt$Scale(domain = range(rub$H))),
           y = alt$Y("T:Q", scale = alt$Scale(domain = range(rub$T))))

brush <- alt$selection_interval()

chartTH_brush <- chartTH$add_selection(brush)

chartTH_selection <-
    chartTH_brush$encode(color = alt$condition(brush,
                                               "Origin:N",
                                               alt$value("lightgray")))

chartAT <- chartTH_selection$
    encode(x = alt$X("T:Q", scale = alt$Scale(domain = range(rub$T))),
           y = alt$Y("A:Q", scale = alt$Scale(domain = range(rub$A))))

chartAT | chartTH_selection
```

The resulting linked plots:

```{r rubber-altair, echo = FALSE, error = TRUE, warning = FALSE}
```


## Notes

* A recent project
  [`gganimate`](https://github.com/thomasp85/gganimate) to add
  animation to `ggplot` looks very promising.

* A number of other [`ggplot`
  extensions](https://exts.ggplot2.tidyverse.org/) are available.

* A [blog
  post](https://medium.com/bbc-visual-and-data-journalism/how-the-bbc-visual-and-data-journalism-team-works-with-graphics-in-r-ed0b35693535)
  explains how the [BBC Visual and Data
  Journalism](https://medium.com/bbc-visual-and-data-journalism) team
  creates their graphics. More details are provided in an [_R cook
  book_](https://bbc.github.io/rcookbook/).

* A [blog
  post](https://blog.revolutionanalytics.com/2016/07/data-journalism-with-r-at-538.html)
  on use of R and `ggplot` by
  [FiveThirtyEight](https://fivethirtyeight.com/).  The `ggthemes`
  packages includes `theme_fivethirtyeight` to emulate their style.


## Reading

Chapters [_Data
visualization_](https://r4ds.had.co.nz/data-visualisation.html) and
[_Graphics for
communication_](https://r4ds.had.co.nz/graphics-for-communication.html)
in [_R for Data Science_](https://r4ds.had.co.nz/), O'Reilly.

Chapter [_Make a plot_](https://socviz.co/makeplot.html) in [_Data
Visualization_](https://socviz.co/).

Chapter [_ggplot2_](https://rafalab.dfci.harvard.edu/dsbook/ggplot2.html) in
[_Introduction to Data Science Data Analysis and Prediction Algorithms
with R_](https://rafalab.dfci.harvard.edu/dsbook/).


## Interactive Tutorial

An interactive [`learnr`](https://rstudio.github.io/learnr/) tutorial
for these notes is [available](`r WLNK("tutorials/ggplot.Rmd")`).

You can run the tutorial with

```{r, eval = FALSE}
STAT4580::runTutorial("ggplot")
```

You can install the current version of the `STAT4580` package with

```{r, eval = FALSE}
remotes::install_gitlab("luke-tierney/STAT4580")
```

You may need to install the `remotes` package from CRAN first.


## Exercises

1. In the following expression, which value of the `shape` aesthetic
   produces a plot with points represented as triangles outlined in
   black colored according to the number of cylinders?

<!-- ## nolint start -->
    ```r
    library(ggplot2)
    ggplot(mpg, aes(x = displ, y = hwy, fill = factor(cyl))) +
        geom_point(size = 4, shape = ---)
    ```
<!-- ## nolint end -->

    a. 15
    b. 17
    c. 21
    d. 24

2. It can sometimes be useful to plot text labels in a scatterplot
   instead of points. Consider the plot set up as

    ```r
    library(ggplot2)
    library(dplyr)
    data(gapminder, package = "gapminder")
    p <- filter(gapminder, year == 2007) %>%
        group_by(continent) %>%
        summarize(gdpPercap = mean(gdpPercap), lifeExp = mean(lifeExp)) %>%
        ggplot(aes(x = gdpPercap, y = lifeExp))
    ```
    Which of the following produces a plot with continent
    names on white rectangles?

    a. `p + geom_text(aes(label = continent))`
    b. `p + geom_label(aes(label = continent))`
    c. `p + geom_label(label = continent)`
    d. `p + geom_text(text = continent)`

3. The following code plots a _kernel density estimate_ for the
   `eruptions` variable in the `faithful` data set:

    ```r
    library(ggplot2)
    ggplot(faithful, aes(x = eruptions)) + geom_density(bw = 0.1)
    ```
    Look at the help page for `geom_density`. Which of the following best
    describes what specifying a value for `bw` does:

    a. Changes the _kernel_ used to construct the estimate.
    b. Changes the _smoothing bandwidth_ to make the result more or less smooth.
    c. Changes the `stat` used to `stat_bw`.
    d. Has no effect on the retult.

4. This code creates a map of Iowa counties.

    ```r
    library(ggplot2)
    p <- ggplot(map_data("county", "iowa"),
                aes(x = long, y = lat, group = group)) +
        geom_polygon(, fill = "White", color = "black")
    ```
   
    Which of these produces a plot with an aspect ratio that best
    matches the map on [this
    page](https://en.wikipedia.org/w/index.php?title=List_of_counties_in_Iowa&oldid=1001171082)?

    a. `p` 
    b. `p + coord_fixed(0.75)`
    c. `p + coord_fixed(1.25)`
    d. `p + coord_fixed(2)`

5. Consider the two plots created by this code (print the values of
   `p1` and `p2` to see the plots):

    ```r
    library(ggplot2)
    data(gapminder, package = "gapminder")
    p1 <- ggplot(gapminder, aes(x = log(gdpPercap), y = lifeExp)) +
        geom_point() +
        scale_x_continuous(name = "")
    p2 <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp)) +
        geom_point() +
        scale_x_log10(labels = scales::comma, name = "") 
    ```

    Which of these statements is true?

    a. The `x` axis labels are identical in both plots.
    b. The `x` axis labels in `p2` are in dollars; the labels in `p1`
       are in log dollars. 
    c. The `x` axis labels in `p1` are in dollars; the labels in `p2`
       are in log dollars.
    d. There are no labels on the `x` axis in `p2`.

6. Consider the plot created by

    ```r
    library(ggplot2)
    data(gapminder, package = "gapminder")
    p <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp)) +
        geom_point() +
        scale_x_log10(labels = scales::comma) 
    ```

    Which of these expressions produces a plot with a white background?

    a. `p`
    b. `p + theme_grey()`
    c. `p + theme_classic()`
    d. `p + ggthemes::theme_economist()`

7. There are many different ways to change the `x` axis label in
   `ggplot`.  Consider the plot created by

    ```r
    library(ggplot2)
    p <- ggplot(mpg, aes(x = displ, y = hwy)) +
        geom_point()
    ```

    Which of the following does **not** change the `x` axis label to
    _Displacement_?

    a. `p + labs(x = "Displacement")`
    b. `p + scale_x_continuous("Displacement")`
    c. `p + xlab("Displacement")`
    d. `p + theme(axis.title.x = "Displacement")`

