Air travel out of New York City is often disrupted, but not evenly. Some days and some times of day are much riskier than others. This report uses the flights data set from the nycflights13 package, which records 336,776 flights that departed from airports all ofver the US in 2013 across 19 variables. The data originally comes from the U.S. Bureau of Transportation Statistics. The goal is to find the days with an unusually large share of cancellations and long delays, work out what caused them, and find out which time of day is riskiest. Throughout, a flight counts as a problem flight if it was cancelled or left more than one hour late.
To find the days when things went wrong, I found each flight as a problem flight, then calculated the percentage of problem flights per day. Cancelled flights have NA for dep_time, so is.na(dep_time) | dep_delay > 60 catches both cancellations and long delays. The table below lists the days where more than 35% of flights had a problem, sorted from worst to least bad.
daily_problem_pct <- flights %>%
mutate(problem_flight = is.na(dep_time) | dep_delay > 60) %>%
group_by(year, month, day) %>%
summarise(
n_flights = n(),
n_problem = sum(problem_flight, na.rm = TRUE),
pct_problem = 100 * n_problem / n_flights,
.groups = "drop"
) %>%
mutate(date = make_date(year, month, day))
bad_days <- daily_problem_pct %>%
filter(pct_problem > 35) %>%
arrange(desc(pct_problem))
datatable(bad_days %>% select(date, n_flights, n_problem, pct_problem))
A rate above 35% is far from normal, so the next step is to check the weather. I searched for New York City weather on each date in the table above (for example, “New York weather February 8 2013”). I expect most of these dates to match major weather events, such as winter storms and blizzards, severe summer thunderstorms, and heavy rain.
Based on my google search of these dates, it is apparent that just about all of them experienced some sort of weather problem. In the winter month dates, there were heavy blizzard conditions, snowstorms, and frigid winter weather that would explain the delay/cancellation issues. Furthermore, the summer dates experienced weather events like severe thunderstorms, very humid/hot temps, and even an instant of a flash flood watch. The only date that didn’t seem to have anything out of the ordinary as far as weather was on 6/28/13, so maybe another issue was the cause of that particular delay/cancellation.
With all that being said, it is quite evident that these large number of delays/cancellations stemmed from bad weather conditions unsafe for flying.
Setting individual dates aside, I now ask whether the scheduled hour of departure affects the chance of a problem. The table below shows, for each scheduled departure hour across the whole year, the number of flights and the percentage that were cancelled or delayed by more than an hour.
hourly_problem_pct <- flights %>%
mutate(problem_flight = is.na(dep_time) | dep_delay > 60) %>%
group_by(hour) %>%
summarise(
n_flights = n(),
n_problem = sum(problem_flight, na.rm = TRUE),
pct_problem = 100 * n_problem / n_flights,
.groups = "drop"
) %>%
arrange(hour)
datatable(hourly_problem_pct)
A plot makes the pattern easier to see. The bars show the percentage for each hour, and the orange line is a smoothed trend.
ggplot(hourly_problem_pct, aes(x = hour, y = pct_problem)) +
geom_col(fill = "steelblue") +
geom_smooth(se = FALSE, color = "darkorange") +
scale_x_continuous(breaks = 0:24) +
labs(
title = "Departure Delays and Cancellations by Scheduled Hour",
subtitle = "NYC-origin flights, 2013",
x = "Scheduled Departure Hour",
y = "% of Flights Cancelled or Delayed > 1 Hour"
) +
theme_minimal(base_size = 13)
The plot takes a left-skewed shape because the percentage should rise through the day. Early flights start with fresh aircraft, crews, and gates. As the day goes on, a late inbound plane or a crew running out of hours pushes delays onto later flights, so problems compound.
5b. The first hour appears to be a substantial outlier, being that 100% of flights at that hour had an issue with their timing. However, hour 1 shows a 100% cancelled/delayed flights during that time because there was only one flight during that hour and it happened to be delayed, which explains the massive bar off to the left. Thus, I would not base my conclusion off of one flight, so I will say that there are simply just not enough flights in the early morning hours for it to be seriously considered as an outlier.
The riskiest time to fly would most likely be in the evening (roughly 7–9 p.m.). Delays have had all day to build up, and thunderstorms tend to hit in late afternoon and evening. Early morning is the safest time if you want to avoid a delay/cancellation.
Even on the worst days, some flights left on time or early. The table below lists those flights. Cancelled flights have NA for dep_delay, so the filter dep_delay <= 0 leaves them out.
flights_ontime_on_bad_days <- flights %>%
mutate(date = make_date(year, month, day)) %>%
filter(date %in% bad_days$date, dep_delay <= 0)
datatable(flights_ontime_on_bad_days %>%
select(date, carrier, origin, dest, sched_dep_time, dep_delay))
A natural guess is that these were the morning flights that got out before the weather hit. To test this guess, the table below gives the average scheduled departure hour of the on-time flights for each bad date (written as hours plus minutes/60, so 7:30 a.m. is 7.5).
avg_sched_hour_by_date <- flights_ontime_on_bad_days %>%
mutate(sched_hour_decimal = hour + minute / 60) %>%
group_by(date) %>%
summarise(
n_ontime_flights = n(),
avg_sched_hour = mean(sched_hour_decimal, na.rm = TRUE),
.groups = "drop"
) %>%
arrange(date)
datatable(avg_sched_hour_by_date)
Since most of the dates appear to be before noon, I would say that this guess is pretty accurate. The flights that got out were the ones that left before the weather arrived.
Some dates may have an afternoon average instead. The table below picks out any date where the average scheduled hour is noon or later.
afternoon_bad_dates <- avg_sched_hour_by_date %>%
filter(avg_sched_hour >= 12)
datatable(afternoon_bad_dates)
There is one date that had an average departure hour in the afternoon, which could be due to the fact that the storm arrived late in the day, so some afternoon flights left before conditions worsened. Or on the other hand, the conditions were present in the morning, but mellowed out by the time it was time to take off.
The worst days in 2013 were mostly driven by significant weather in the New York area. Separately, delay risk climbs through the day as delays cascade, so early morning is the safest time to fly and evening the riskiest. On bad-weather days, the flights that left on time were generally the early ones that departed before conditions deteriorated.