Heatmaps, Treemaps, Alluvials, and Streamgraphs

Author

G. Balasanov

Practicing visualizations

#install.packages("nycflights23")
#install.packages(c("viridis", "RColorBrewer", "treemap", "alluvial", "ggalluvial", "babynames"))

#install.packages("streamgraph")

library(nycflights23)
library(tidyverse)
data(flights)
data(airlines)
data(airports)

Clean the data

flights_nona <- flights |>
  filter(!is.na(distance) & !is.na(arr_delay) & !is.na(dep_delay))

# remove na's for distance, arr_delay, departure delay

# add the airline names
flights2 <- left_join(flights_nona, airlines, by = "carrier")
flights2$name <- gsub("Inc\\.|Co\\.", "", flights2$name)# remove "Inc." or "Co." from the names
flights2$name <- trimws(flights2$name)# remove the extra spaces

# find the 6 airlines with the most flights
top6 <- flights2 |>
  count(name) |>
  arrange(desc(n)) |>
  head(6)
top6
# A tibble: 6 × 2
  name                  n
  <chr>             <int>
1 Republic Airline  85431
2 United Air Lines  77438
3 JetBlue Airways   64280
4 Delta Air Lines   60364
5 Endeavor Air      52204
6 American Airlines 39750

Summary table for each destination

by_dest <- flights_nona |>
  group_by(dest) |>   # group all destinations
  summarise(count = n(),   # counts totals for each destination
            avg_dist = mean(distance),   # calculates the mean distance traveled
            avg_arr_delay = mean(arr_delay),   # calculates the mean arrival delay
            avg_dep_delay = mean(dep_delay),   # calculates the mean dep delay
            .groups = "drop") |>   # remove the grouping structure after summarizing
  arrange(avg_arr_delay) |>
  filter(avg_dist < 3000)
head(by_dest)
# A tibble: 6 × 5
  dest  count avg_dist avg_arr_delay avg_dep_delay
  <chr> <int>    <dbl>         <dbl>         <dbl>
1 PNS      71    1030         -10.6         -1.24 
2 HHH     461     695.         -9.95         1.38 
3 HDN      27    1728          -9.93         8.78 
4 VPS     107     988          -9.41         2.62 
5 AVP     140      93          -8.53        -0.957
6 GSO    2857     456.         -7.77         3.81 

Scatterplot

ggplot(by_dest, aes(avg_dist, avg_arr_delay)) +
  geom_point(aes(size = count), alpha = .3) +
  geom_smooth(se = FALSE) +   # remove the error band
  scale_size_area() +
  theme_bw() +
  labs(x = "Average Flight Distance (miles)",
       y = "Average Arrival Delay (minutes)",
       size = "Number of Flights \n Per Destination",
       caption = "Source: nycflights23 package",
       title = "Average Distance and Average Arrival Delays from Flights from NY")
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Heatmaps

Heatmap 1: busiest destinations

I only kept the 25 busiest destinations so the row names are easy to read.

by_dest_top <- by_dest |>
  arrange(desc(count)) |>
  head(25)

by_dest_matrix <- data.matrix(by_dest_top[, -1])   # drop dest from matrix so it won't show in heatmap
row.names(by_dest_matrix) <- by_dest_top$dest      # restore row names

library(viridis)
Loading required package: viridisLite
by_dest_heatmap <- heatmap(by_dest_matrix,
                           Rowv = NA,
                           Colv = NA,
                           col = viridis(250),
                           cexCol = .7,   # shrink x-axis label size
                           scale = "column",
                           xlab = "",
                           ylab = "",
                           main = "Heatmap of the 25 Busiest Destinations from NY")

Heatmap 2: airline and month

airline_month <- flights2 |>
  filter(name %in% top6$name) |>
  group_by(name, month) |>
  summarise(avg_arr_delay = mean(arr_delay), .groups = "drop")

ggplot(airline_month, aes(x = month, y = name, fill = avg_arr_delay)) +
  geom_tile(color = "white") +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = 1:12) +
  theme_bw() +
  labs(x = "Month (1 = January)",
       y = "Airline",
       fill = "Avg Arrival \n Delay (min)",
       caption = "Source: nycflights23 package",
       title = "Average Arrival Delay by Airline and Month")

Which 6 destination airports have the highest average arrival delay from NYC?

worst6 <- by_dest |>
  arrange(desc(avg_arr_delay)) |>
  head(6)

# change the airport codes to the airport names
worst6 <- left_join(worst6, airports, by = c("dest" = "faa"))
worst6 |>
  select(dest, name, count, avg_arr_delay)
# A tibble: 6 × 4
  dest  name                                   count avg_arr_delay
  <chr> <chr>                                  <int>         <dbl>
1 PSE   Mercedita Airport                        319          37.6
2 RNO   Reno Tahoe International Airport         129          34.4
3 ABQ   Albuquerque International Sunport        218          26.7
4 ONT   Ontario International Airport            353          26.1
5 BQN   Rafael Hernández Airport                 957          25.6
6 SJU   Luis Muñoz Marín International Airport  5312          21.0

Treemaps

Treemap 1: carriers

The index is the carrier, the size of the box is the average distance, and the color is the average arrival delay.

flights3 <- flights2 |>
  group_by(name) |>
  summarise(avg_dist = mean(distance),   # calculates the mean distance traveled
            avg_arr_delay = mean(arr_delay))   # calculates the mean arrival delay

library(RColorBrewer)
library(treemap)
treemap(flights3,
        index = "name",
        vSize = "avg_dist",
        vColor = "avg_arr_delay",
        type = "manual",
        palette = "RdYlBu",   # use RColorBrewer palette
        title = "Average Distance and Arrival Delay by Carrier",   # plot title
        title.legend = "Avg Arrival Delay (min)")   # legend label

Treemap 2: airport and airline

The big rectangles are the NYC airports (EWR, JFK, LGA) and the small ones inside are the airlines. The size is the number of flights.

flights4 <- flights2 |>
  filter(name %in% top6$name) |>
  group_by(origin, name) |>
  summarise(count = n(), .groups = "drop")

treemap(flights4,
        index = c("origin", "name"),   # big group first, small group second
        vSize = "count",
        title = "Number of Flights by Airport and Airline")

Alluvials

Alluvials need three variables: a time variable, a value, and a category.

Alluvial 1: refugees

library(alluvial)
library(ggalluvial)
data(Refugees)

ggalluv <- Refugees |>
  ggplot(aes(x = year, y = refugees, alluvium = country)) +
  theme_bw() +
  geom_alluvium(aes(fill = country),
                color = "white",
                width = .1,
                alpha = .8,
                decreasing = FALSE) +
  scale_fill_brewer(palette = "Spectral") +
  # Spectral has enough colors for all countries listed
  scale_x_continuous(lim = c(2002, 2013)) +
  labs(title = "UNHCR-Recognised Refugees Top 10 Countries\n (2003-2013)",
       y = "Number of Refugees",
       fill = "Country",
       caption = "Source: United Nations High Commissioner for Refugees (UNHCR)")

options(scipen = 999)   # no scientific notation on the y-axis
ggalluv

Alluvial 2: flights by month

flights_month <- flights2 |>
  filter(name %in% top6$name) |>
  group_by(month, name) |>
  summarise(flights = n(), .groups = "drop") |>
  complete(month, name, fill = list(flights = 0))   # puts a 0 if an airline has no flights in a month

ggalluv_flights <- flights_month |>
  ggplot(aes(x = month, y = flights, alluvium = name)) +
  theme_bw() +
  geom_alluvium(aes(fill = name),
                color = "white",
                width = .1,
                alpha = .8,
                decreasing = FALSE) +
  scale_fill_brewer(palette = "Set2") +
  scale_x_continuous(breaks = 1:12) +
  labs(title = "Monthly Flights from NYC by Airline (2023)",
       x = "Month (1 = January)",
       y = "Number of Flights",
       fill = "Airline",
       caption = "Source: nycflights23 package")

ggalluv_flights