Week 5 Assignment 5A - Airline Delays

Author

Supriya P.

Introduction

For this assignment, I recreated an airline arrival table as a CSV in its original wide layout, with the five cities as columns and on-time and delayed counts listed under each airline. The original table leaves the airline name blank on each “delayed” row and includes an empty row between the two airlines, so those gaps are kept in the CSV. After loading the data into R, I filled in the missing values, reshaped the data into long format, and compared the delay rates of Alaska and AM West, both overall and city by city.

Load the Data

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
raw <- read_csv(
  "https://raw.githubusercontent.com/0pree/symmetrical-robot/refs/heads/main/airline_delays.csv",
  show_col_types = FALSE
)
New names:
• `` -> `...1`
• `` -> `...2`
knitr::kable(raw)
…1 …2 Los Angeles Phoenix San Diego San Francisco Seattle
ALASKA on time 497 221 212 503 1841
NA delayed 62 12 20 102 305
NA NA NA NA NA NA NA
AM WEST on time 694 4840 383 320 201
NA delayed 117 415 65 129 61

The first two columns have no headers, the airline name is missing on the “delayed” rows, and there is a fully empty row separating the two airlines.

Clean and Fill Missing Data

flights <- raw %>%
  rename(airline = 1, status = 2) %>%
  filter(!is.na(status)) %>%
  fill(airline)

knitr::kable(flights)
airline status Los Angeles Phoenix San Diego San Francisco Seattle
ALASKA on time 497 221 212 503 1841
ALASKA delayed 62 12 20 102 305
AM WEST on time 694 4840 383 320 201
AM WEST delayed 117 415 65 129 61

Reshape from Wide to Long

flights_long <- flights %>%
  pivot_longer(
    cols = -c(airline, status),
    names_to = "city",
    values_to = "count"
  )

knitr::kable(head(flights_long, 10))
airline status city count
ALASKA on time Los Angeles 497
ALASKA on time Phoenix 221
ALASKA on time San Diego 212
ALASKA on time San Francisco 503
ALASKA on time Seattle 1841
ALASKA delayed Los Angeles 62
ALASKA delayed Phoenix 12
ALASKA delayed San Diego 20
ALASKA delayed San Francisco 102
ALASKA delayed Seattle 305

Each row now represents one airline, one city, and one flight status, which makes it straightforward to group and summarize.

Overall Delay Rates

overall <- flights_long %>%
  group_by(airline, status) %>%
  summarize(count = sum(count), .groups = "drop") %>%
  pivot_wider(names_from = status, values_from = count) %>%
  mutate(
    total = `on time` + delayed,
    pct_delayed = round(delayed / total * 100, 1)
  )

knitr::kable(overall)
airline delayed on time total pct_delayed
ALASKA 501 3274 3775 13.3
AM WEST 787 6438 7225 10.9
ggplot(overall, aes(x = airline, y = pct_delayed, fill = airline)) +
  geom_col() +
  geom_text(aes(label = paste0(pct_delayed, "%")), vjust = -0.5) +
  labs(title = "Overall Percentage of Delayed Flights", x = NULL, y = "% Delayed") +
  theme_minimal() +
  theme(legend.position = "none")

Across all five cities combined, AM West has the better record, with 10.9% of flights delayed compared to 13.3% for Alaska.

Delay Rates by City

by_city <- flights_long %>%
  pivot_wider(names_from = status, values_from = count) %>%
  mutate(
    total = `on time` + delayed,
    pct_delayed = round(delayed / total * 100, 1)
  )

by_city %>%
  select(city, airline, pct_delayed) %>%
  pivot_wider(names_from = airline, values_from = pct_delayed) %>%
  knitr::kable()
city ALASKA AM WEST
Los Angeles 11.1 14.4
Phoenix 5.2 7.9
San Diego 8.6 14.5
San Francisco 16.9 28.7
Seattle 14.2 23.3
ggplot(by_city, aes(x = city, y = pct_delayed, fill = airline)) +
  geom_col(position = "dodge") +
  labs(title = "Percentage of Delayed Flights by City", x = NULL, y = "% Delayed", fill = "Airline") +
  theme_minimal()

City by city, the result reverses. Alaska has a lower delay rate than AM West in all five cities. The largest gap is in San Francisco, where 28.7% of AM West flights were delayed compared to 16.9% for Alaska.

Explaining the Discrepancy

by_city %>%
  group_by(airline) %>%
  mutate(pct_of_flights = round(total / sum(total) * 100, 1)) %>%
  select(airline, city, total, pct_of_flights, pct_delayed) %>%
  knitr::kable()
airline city total pct_of_flights pct_delayed
ALASKA Los Angeles 559 14.8 11.1
ALASKA Phoenix 233 6.2 5.2
ALASKA San Diego 232 6.1 8.6
ALASKA San Francisco 605 16.0 16.9
ALASKA Seattle 2146 56.8 14.2
AM WEST Los Angeles 811 11.2 14.4
AM WEST Phoenix 5255 72.7 7.9
AM WEST San Diego 448 6.2 14.5
AM WEST San Francisco 449 6.2 28.7
AM WEST Seattle 262 3.6 23.3

AM West looks better overall even though Alaska is better in every city. This is an example of Simpson’s paradox, and it comes from where each airline’s flights are concentrated. About 73% of AM West’s flights go to Phoenix, which has the lowest delay rate of any city for both airlines. About 57% of Alaska’s flights go to Seattle, where delays are much more common. AM West’s overall rate is pulled down by its large volume of Phoenix flights, while Alaska’s overall rate is pulled up by its large volume of Seattle flights. The overall numbers mostly reflect which cities each airline flies to, not how well each airline performs.

Conclusions

Comparing the airlines only on overall delay rates would lead to the wrong conclusion. Alaska performs better in every city, and AM West’s overall advantage comes from the mix of cities it serves. Looking at the data by group gives a more accurate comparison. A next step would be to add more cities or more time periods to see whether this pattern holds, or to look at other factors, like time of day or weather, that might explain why some cities have higher delay rates than others.

AI Citation

Anthropic. (2026). Claude Sonnet 5 [Large language model]. https://claude.ai. Accessed October 2026.