The Data

The nycflights13 package contains all 336,776 flights that departed the three New York City airports (Newark, JFK, and LaGuardia) in 2013.

library(tidyverse)
library(nycflights13)

dim(flights)
## [1] 336776     19

Preparing the Data with dplyr

I used four dplyr commands to reshape the raw flight records into one average per airport per hour:

  • filter drops cancelled flights, which have no departure delay recorded
  • group_by groups the remaining flights by origin airport and scheduled departure hour
  • summarize computes the mean delay and counts the flights in each group
  • a second filter removes hour-airport combinations with fewer than 100 flights, so a handful of unusual late-night departures cannot swing an average
delay_by_hour <- flights |>
  filter(!is.na(dep_delay)) |>
  group_by(origin, hour) |>
  summarize(avg_delay = mean(dep_delay),
            flights = n(),
            .groups = "drop") |>
  filter(flights >= 100)

head(delay_by_hour)
## # A tibble: 6 × 4
##   origin  hour avg_delay flights
##   <chr>  <dbl>     <dbl>   <int>
## 1 EWR        5     0.649     892
## 2 EWR        6     3.50    10959
## 3 EWR        7     4.04     8540
## 4 EWR        8     5.50     9114
## 5 EWR        9     5.84     5993
## 6 EWR       10     7.79     6571

The Visualization

flight_plot <- delay_by_hour |>
  ggplot(aes(x = hour, y = avg_delay, color = origin)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "grey60") +
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  scale_color_manual(name = "Origin Airport",
                     values = c("EWR" = "#E69F00",
                                "JFK" = "#0072B2",
                                "LGA" = "#009E73"),
                     labels = c("EWR (Newark)",
                                "JFK (Kennedy)",
                                "LGA (LaGuardia)")) +
  scale_x_continuous(breaks = seq(5, 23, 2)) +
  labs(title = "Departure Delays Build Through the Day at All Three NYC Airports",
       subtitle = "Flights scheduled before 7am leave roughly on time; by 8pm the average flight is over 20 minutes late",
       x = "Scheduled Departure Hour (24-hour clock)",
       y = "Average Departure Delay (minutes)",
       caption = "Source: nycflights13 R package (all 336,776 flights departing EWR, JFK, and LGA in 2013),
       originally from the US Bureau of Transportation Statistics") +
  theme_minimal(base_size = 12) +
  theme(legend.position = "right",
        plot.title = element_text(face = "bold"))

flight_plot
Scatterplot with connecting lines showing average departure delay rising from about zero minutes for 5am departures to between 22 and 31 minutes for 8pm departures at Newark, JFK, and LaGuardia.

Average departure delay by scheduled hour for the three NYC airports

What It Shows

The visualization is a scatterplot with connecting lines, where each point is the average departure delay for one airport at one scheduled hour.

The pattern is strikingly consistent across all three airports. Flights scheduled between 5am and 7am leave essentially on time, with average delays near zero and LaGuardia’s 6am flights actually departing slightly early. From there the averages climb steadily all day. Newark peaks at 7pm around 31 minutes, while JFK and LaGuardia both sit above 20 minutes through the evening. The lines get jumpy after 8pm because far fewer flights are scheduled that late, so each remaining average rests on a smaller sample and is less stable than the daytime points.

The reason is that delay compounds. Early flights start the day with aircraft and crews already in position, so there is nothing upstream to inherit. As the day progresses, each late arrival pushes back the next departure for that plane, and congestion accumulates faster than the schedule can absorb it. The practical takeaway is straightforward: if you want to leave on time out of New York, book the earliest flight you can.

Newark sits above the other two airports for most of the day, which fits its reputation as the most delay-prone of the three. The gap widens in the afternoon, suggesting Newark absorbs accumulated delay less well than JFK or LaGuardia rather than simply starting out worse.

Requirements Checklist

  • Graph type covered in the course: scatterplot with connecting lines
  • At least one dplyr command: filter, group_by, summarize, and a second filter
  • Axis labels: x-axis “Scheduled Departure Hour (24-hour clock)”, y-axis “Average Departure Delay (minutes)”
  • Caption naming the data source: nycflights13 package, originally US Bureau of Transportation Statistics
  • Title: included, plus a subtitle stating the finding
  • At least two colors: three, one per airport, from a colorblind-friendly palette
  • Legend explaining the colors: titled “Origin Airport” with full airport names