Data Visualization

ggplot2 is a package in R that is used for creating graphics. This package is found in the package tidyverse. To build a graph, you first need to tell r what information or data you want it to use in the function ggplot().

ggplot(data)

Next, you need to tell it what aesthetics you want it to use, which includes what x and y variables.

ggplot(data, aes(x, y)

After, you need to tell r how you want it to graph the data.

ggplot(data, aes(x, y) + geom_point()

ggplot has several different geom_ that allow you to graph various different things. We will focus on four different graphs: scatter plots, histograms, box plots, and time series.

However, we will only scratch the surface of the ggplot2 package, which includes many more commands and options (aesthetics, geoms, scales, statistics, etc.).

Scatter Plots

The first plot style we will be looking at is a scatter plot. To plot a scatter plot in ggplot you will use the format discussed above and to tell r you want it to graph a scatter plot you will use the geom function; geom_point().

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
# remotes::install_github("hbdaarstad/RandomData")

library(RandomData)

# ggplot(data, aes(x, y) + geom_point()

In the following example, we will plot the qualifying position for the 20 drivers and the final points they were awarded for the Las Vegas Grand Prix in 2023.

vegas_gp <- race_stats |>
  filter(circuit == "Las Vegas Strip Street Circuit" & year == "2023")

ggplot(data = vegas_gp, aes(x = quali_position, y = points)) + 
        geom_point()

Let’s add a main title and adjust the x and y labels.

ggplot(data = vegas_gp, aes(x = quali_position, y = points)) + 
        geom_point() +
        labs(
          x = "Qualifying Position",
          y = "Points Awarded",
          title = "Qualifying Position vs. Points Awarded at Las Vegas GP 2023"
        )

If we wanted to identify the constructors of each team we would add color = constructor in the aes().

Note

Change the name of the legend by changing the name of the grouping, in this case color, in labs().

ggplot(data = vegas_gp, aes(x = quali_position, y = points, color = constructor)) + 
        geom_point() +
        labs(
          x = "Qualifying Position",
          y = "Points Awarded",
          title = "Qualifying Position vs. Points Awarded at Las Vegas GP 2023",
          color = "F1 Team"
        )

Let’s change the colors for the teams now. We can assign our own colors or we can make our own palette.

If we wanted to add a smoother line we would use the geom_smooth() and we will be fitting a linear regression line, thus inside geom_smooth() we method = "lm".

Caution

When using geom_smooth() move any groupings, like color or shape, into the geom_point() by applying the aesthetics in that part of the function.

ggplot(data = vegas_gp, aes(x = quali_position, y = points)) + 
        geom_point(aes(color = constructor)) +
        labs(
          x = "Qualifying Position",
          y = "Points Awarded",
          title = "Qualifying Position vs. Points Awarded at Las Vegas GP 2023",
          color = "F1 Team"
        ) +
       scale_color_manual(values = f1_colors) +
        geom_smooth(method = "lm")
`geom_smooth()` using formula = 'y ~ x'

Time Series

geom_line() in ggplot2 is used to create a line plot by connecting data points with a continuous line, which is ideal for visualizing trends over time. It is particularly useful for time series data because it clearly shows how a variable changes across ordered time intervals, allowing for easy identification of patterns and trends.

?constructors_stats

# Calculate total wins per year for Ferrari
ferrari_wins <- constructors_stats |>
  filter(constructor == "Ferrari") |>
  group_by(year) |> 
  summarize(total_wins = sum(max(constructor_wins)))

ggplot(data = ferrari_wins, aes(x = year, y = total_wins)) +
  geom_line(color = "red", linewidth = 1.2) +  # Creates a red line plot
  labs(title = "Ferrari Wins 1958 to 2024 (Before the Summer Break)", 
       x = "Year", 
       y = "Wins") +
  theme_classic()

  • aes(x = year, y = total_wins):

    • x = year: Puts the years on the x-axis.

    • y = total_wins: Puts the total number of wins on the y-axis.

  • geom_line(color = "red", linewidth = 1.2):

    • geom_line(): Creates a line plot instead of points.

    • color = "red": Adds a color to the line

    • linewidth = 1.2: Makes the line slightly thicker for better visibility.

Histograms

In ggplot2, to build a histogram we use geom_histogram(), and only one numeric variable is needed as input. In the following example, we will look at the distribution of the final positions of the McLaren 2023 season for both of their drivers. To set up this histogram, I construct a new variable called, final_position, and save it as a new object called McLarenStandings_2023.

McLarenStandings_2023 <- McLarenStandings_2023 |>
  mutate(final_position_numeric = ifelse(final_position == "DNF", 0, as.numeric(final_position)))

ggplot(McLarenStandings_2023, aes(x = final_position_numeric)) +
  ## we use fill for bars not color and we can adjust bins here
  geom_histogram(fill = "orange", bins = 10) +
  labs(
        title = "McLaren Race Results 2023", 
        x = "Race Results", 
        y = "Count") +
  theme_minimal()

  • aes(x = final_position_numeric): Defines the variable for the histogram.

  • geom_histogram(): Creates the histogram.

  • fill = "orange": Fills the bars with orange.

  • bins = 10: Adjust the number of bins (change as needed).

  • labs(): Adds title and axis labels.

  • theme_minimal(): Uses a cleaner theme.

In a histogram, bins represent the intervals (or ranges) into which data points are grouped. Each bin covers a specific range of values, and the height of the bar represents the number of observations (or frequency) that fall within that range.

Bar Plot

Now, let’s say we wanted to compare this across different categories, therefore instead we can use bar plots instead of histograms. In the following example, we will look at the distribution of the final positions of the McLaren 2023 season for both of their drivers. To set up this histogram, I construct a new variable called, final_position, and save it as a new object called McLarenStandings_2023.

# Summarize mean race positions by driver
barplot <- McLarenStandings_2023 |>
  group_by(surname) |>
  summarize(mean_position = mean(final_position_numeric))

# Create a bar plot of mean race positions
ggplot(barplot, aes(x = mean_position, y = surname, fill = surname)) +
  geom_col() +
  labs(title = "McLaren Mean Race Results 2023", 
       x = "Mean Race Position", 
       y = "McLaren Driver") +
  scale_fill_manual(values = c("orange", "grey")) + 
  theme_minimal()

  • aes(x = mean_position, y = surname, fill = surname):

    • x = mean_position: The x-axis represents the mean race position of each driver.

    • y = surname: The y-axis represents the driver’s name.

    • fill = surname: Each driver gets a unique color for their bar.

  • geom_col():

    • Creates a bar chart where the bar length represents the mean race position
  • scale_fill_manual(values = c("orange", "grey")):

    • Manually assigns colors to the bars.

    • "orange" for one driver and "grey" for the other (representing McLaren’s team colors).

Box Plots

To make a box plot we use the geom_boxplot() function! In the following example, we will look at the distribution of the final positions of the McLaren 2023 season for both of their drivers. To set up this histogram, I construct a new variable called, final_position, and save it as a new object called McLarenStandings_2023.

ggplot(McLarenStandings_2023, aes(x = surname, y = final_position_numeric, fill = surname)) +
  geom_boxplot() +
  scale_fill_manual(values = c("orange", "grey")) +  # Assign McLaren colors
  labs(title = "McLaren Race Results", 
       x = "Driver", 
       y = "Final Position") +
  theme_minimal()

  • aes(x = surname, y = final_position_numeric, fill = surname):

    • x = surname: Drivers on the x-axis.

    • y = final_position_numeric: Final race position on the y-axis.

    • fill = surname: Colors the boxes based on the driver.

  • geom_boxplot(): Creates a boxplot to show the distribution of race positions.

The box and whisker plot represents the middle 50% of race finishes for each driver, while the horizontal line inside the box is the median race position, the whiskers show the range of most race finishes (excluding outliers), and any dots outside the whiskers indicate outliers.

Class Examples

Presidential elections code-through

We’re working with elections_historic from the {socviz} package. This code loads {socviz}, {tidyverse} (where ggplot() lives) and the dataset:

library(tidyverse)
library(socviz)
elections_historic
# A tibble: 49 × 19
   election  year winner      win_party ec_pct popular_pct popular_margin  votes
      <int> <int> <chr>       <chr>      <dbl>       <dbl>          <dbl>  <int>
 1       10  1824 John Quinc… D.-R.      0.322       0.309        -0.104  1.13e5
 2       11  1828 Andrew Jac… Dem.       0.682       0.559         0.122  6.43e5
 3       12  1832 Andrew Jac… Dem.       0.766       0.547         0.178  7.03e5
 4       13  1836 Martin Van… Dem.       0.578       0.508         0.142  7.63e5
 5       14  1840 William He… Whig       0.796       0.529         0.0605 1.28e6
 6       15  1844 James Polk  Dem.       0.618       0.495         0.0145 1.34e6
 7       16  1848 Zachary Ta… Whig       0.562       0.473         0.0479 1.36e6
 8       17  1852 Franklin P… Dem.       0.858       0.508         0.0695 1.61e6
 9       18  1856 James Buch… Dem.       0.588       0.453         0.122  1.84e6
10       19  1860 Abraham Li… Rep.       0.594       0.396         0.101  1.86e6
# ℹ 39 more rows
# ℹ 11 more variables: margin <int>, runner_up <chr>, ru_part <chr>,
#   turnout_pct <dbl>, winner_lname <chr>, winner_label <chr>, ru_lname <chr>,
#   ru_label <chr>, two_term <lgl>, ec_votes <dbl>, ec_denom <dbl>

We start with ggplot():

ggplot()

Tell it what data we are plotting:

ggplot(data = elections_historic)

Map the x and y-axis within aes():

ggplot(data = elections_historic, aes(x = popular_pct, y = ec_pct))

Add a point geometry:

ggplot(data = elections_historic, aes(x = popular_pct, y = ec_pct)) + geom_point()

Go back and map color and shape aesthetics:

ggplot(data = elections_historic, aes(x = popular_pct, y = ec_pct,
                               color = win_party, shape = two_term)) + geom_point()

Add labels by mapping the label aesthetic and adding a geom_text geometry:

ggplot(data = elections_historic, aes(x = popular_pct, y = ec_pct,
                               color = win_party, shape = two_term,
                               label = winner_label)) +
  geom_point() +
  geom_text()

Here’s the final code, all put together, changing the axis titles using labs():

# load libraries
library(tidyverse)
library(socviz)

# make plot
ggplot(elections_historic, aes(x = popular_pct, y = ec_pct, color = win_party,
                               shape = two_term, label = winner_label)) +
  geom_point() + geom_text() + labs(x = "Percent of popular vote",
                                    y = "Percent of Electoral College vote",
                                    title = "Presidential Elections (1824-2016)",
                                    color = NULL, size = NULL)

Bells and whistles

Here’s some other stuff I’m adding to make this graph better (that you’re not responsible for):

# load libraries
library(tidyverse)
library(socviz)
library(ggrepel)

ggplot(elections_historic, aes(x = popular_pct, y = ec_pct,
                               color = win_party, shape = two_term,
                               label = winner_label)) +
  geom_point() +
  geom_text_repel() +
  labs(x = "Percent of popular vote",
                                    y = "Percent of Electoral College vote",
                                    title = "Presidential Elections (1824-2016)",
                                    color = NULL, size = NULL) +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(labels = scales::percent) +
  scale_color_manual(values = c("yellow", "red", "blue", "gray")) +
  geom_hline(yintercept = 0.5, size = 1.4, color = "gray80") +
    geom_vline(xintercept = 0.5, size = 1.4, color = "gray80") +  theme(legend.position = "none")

Recessions

The recession graph from class:

ggplot(data = economics, aes(x = date, y = unemploy)) +
  geom_line()

Organs

The organ data plot:

ggplot(data = organdata, aes(x = donors, fill = opt)) +
  geom_histogram()

ggplot2 Resources:

ggplot2 Website: https://ggplot2.tidyverse.org/reference/index.html

Aesthetic specifications: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html

  • Specifications for lines, shapes, etc.

https://r-graph-gallery.com/ggplot2-package.html

List of ggplot2 Resources: https://github.com/erikgahner/awesome-ggplot2

ggplot2 book: https://ggplot2-book.org

ggplot2 cheat sheet: https://rstudio.github.io/cheatsheets/html/data-visualization.html