Chapter 1: Introduction

Prerequisites

We already installed R, RStudio, and the packages we need for this course (tidyverse, a package for financial analysis, plotly for interactive visualization, and the packages needed for PDF output). tidyverse only needs to be installed once — after that, we just need to load it with library() at the top of the file so it’s available throughout.

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Running Code

Code can be run directly in the console, but anything run there disappears once the R session ends. Writing code in a file (an R script, or here in an R Markdown file) keeps a permanent, shareable record. R Markdown lets us mix text and analysis in the same document.

1 + 2
## [1] 3

Getting Help

If you hit an error, copy the error message and search it on Google (Stack Overflow is usually the best source). You can also look up a function’s documentation by placing your cursor on the function name and pressing F1, or by using package::function (e.g. ggplot2::geom_point) to see suggestions from a specific package.

Chapter 2: Introduction to Data Exploration

Chapter 2 is a short overview chapter that introduces the data science project workflow (import -> tidy -> transform/visualize/model [iterative] -> communicate). No code chunk needed here — just read the chapter.

Chapter 3: Data Visualization

Setup

library(tidyverse)

The mpg Data Frame

The mpg dataset comes bundled with ggplot2 (part of the tidyverse). Each row is a car model; each column is a variable describing that model (engine size, highway mileage, class, etc.).

mpg
## # A tibble: 234 × 11
##    manufacturer model      displ  year   cyl trans drv     cty   hwy fl    class
##    <chr>        <chr>      <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
##  1 audi         a4           1.8  1999     4 auto… f        18    29 p     comp…
##  2 audi         a4           1.8  1999     4 manu… f        21    29 p     comp…
##  3 audi         a4           2    2008     4 manu… f        20    31 p     comp…
##  4 audi         a4           2    2008     4 auto… f        21    30 p     comp…
##  5 audi         a4           2.8  1999     6 auto… f        16    26 p     comp…
##  6 audi         a4           2.8  1999     6 manu… f        18    26 p     comp…
##  7 audi         a4           3.1  2008     6 auto… f        18    27 p     comp…
##  8 audi         a4 quattro   1.8  1999     4 manu… 4        18    26 p     comp…
##  9 audi         a4 quattro   1.8  1999     4 auto… 4        16    25 p     comp…
## 10 audi         a4 quattro   2    2008     4 manu… 4        20    28 p     comp…
## # ℹ 224 more rows

Creating a ggplot

We build a plot by starting with ggplot(data = ...), adding a geom layer with +, and mapping variables to aesthetics (axes, color, etc.) inside aes().

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

There’s a clear negative relationship: cars with bigger engines (displ) tend to get worse highway mileage (hwy). A few points don’t fit the trend — worth investigating further.

Aesthetic Mappings

We can map a third variable to an additional aesthetic, like color, to see if it explains those outliers.

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy, color = class))

Coloring by class shows the outliers are 2-seater sports cars — big engines, but better mileage than other cars with similarly sized engines.

Common problem: if nothing happens when you run the code, check that the + is at the end of the line, not the start of the next one.

Facets

Instead of mapping a categorical variable to color, we can split the plot into small multiples — one panel per category — with facet_wrap().

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_wrap(~class, nrow = 2)

You can also facet on the combination of two variables with facet_grid():

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~ cyl)

Geometric Objects

A geom is the visual object used to represent the data. The same data/mappings can be drawn as points or as a smooth trend line, or both at once.

# Points only
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

# Smooth line only
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

# Both geoms in the same plot
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  geom_smooth(mapping = aes(x = displ, y = hwy))
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Note: not every aesthetic works with every geom — e.g. linetype works for geom_smooth() but not geom_point(), since points don’t have lines.

Statistical Transformations & Position Adjustments

Bar charts are useful for categorical variables with a limited number of values. Here we switch to the diamonds dataset (also from ggplot2) and look at diamond cut and clarity.

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))

Adding fill = clarity stacks a second categorical variable within each bar:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut, fill = clarity))

The position argument controls how those stacked segments are arranged. Try each of the following:

# "identity": default-like stacking
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut, fill = clarity), position = "identity")

# "fill": rescales each bar to 100%, useful for comparing proportions across categories
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut, fill = clarity), position = "fill")

# "dodge": places overlapping bars side-by-side
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge")

Scatterplot adjustment: the mpg scatterplot only shows ~126 distinct points even though there are 234 rows, because many points overlap. position = "jitter" adds a small amount of random noise so overlapping points become visible.

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy), position = "jitter")

Coordinate Systems

# coord_flip(): swaps the x and y axes
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
  geom_boxplot() +
  coord_flip()

# coord_quickmap(): sets the correct aspect ratio for maps
# (shown here conceptually — requires map data, e.g. from the maps package)
# coord_polar(): uses polar coordinates
bar <- ggplot(data = diamonds) +
  geom_bar(
    mapping = aes(x = cut, fill = cut),
    show.legend = FALSE,
    width = 1
  ) +
  theme(aspect.ratio = 1) +
  labs(x = NULL, y = NULL)

bar + coord_polar()

The Layered Grammar of Graphics

Every ggplot2 plot follows the same template:

ggplot(data = <DATA>) +
  <GEOM_FUNCTION>(
    mapping = aes(<MAPPINGS>),
    stat = <STAT>,
    position = <POSITION>
  ) +
  <COORDINATE_FUNCTION> +
  <FACET_FUNCTION>

We start with a dataset, add a geom (with its mappings), optionally adjust the statistical transformation and position, then layer on a coordinate system and facets as needed — all combined with +.