# Clear Global Environment
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE,
fig.height = 4,
fig.width = 6,
fig.align = "center"
)
rm(list = ls())
options(
scipen = 999,
digits = 3
)
# Setting the Working Directory
setwd("~/Desktop/Fall Quarter/BUSN-32100/final-exam")
# Load packages
library(readr) # read csv
library(dplyr) # data manipulation
library(tidyverse)
library(janitor)
library(statar)
library(airportr) # for merging airport information
library(lubridate) # date time manipulation
library(hms)
library(stargazer) # Regression table output
library(kableExtra) # print formatted kable
library(ggplot2) # Plot
library(plotly)
library(reshape2) # melt data for correlation map plotting
library(ggthemes) # Set Theme
library(ggthemr)
library(ggpubr) # Arrange multiple plots
library(patchwork)
library(maps) # Spatial plot
library(modelr) # Modeling
library(caTools)
library(InformationValue)
Research Question Using the dataset from US Department of Transportation in 2015, I want to explore the patterns of flight departure delay. Specifically, answering the following questions:
First, overall, whether the flight delay time or percentage related to the cities that the flight departure from or arriving, whether the percentage of delay is related to time, including departure month, day of the week and scheduled hour.
Second, whether the flight departure delay related to air time delay or distance. Does a departure delay also likely to cause air time delay.
Third, does the flight delay related to the airline. And if so in which month or hour each airline performs better
Lastly, if we are departing from Chicago, which month, hour and airline will be the best choice
# Load data
files <- list.files(pattern = "*.csv")
list2env(
lapply(
setNames(files, make.names(gsub("_.*|*.csv", "", files))),
read.csv
),
envir = .GlobalEnv
)
## <environment: R_GlobalEnv>
# Check dataset
glimpse(flights)
## Rows: 5,819,079
## Columns: 31
## $ YEAR <int> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 20…
## $ MONTH <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ DAY <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ DAY_OF_WEEK <int> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,…
## $ AIRLINE <chr> "AS", "AA", "US", "AA", "AS", "DL", "NK", "US", "A…
## $ FLIGHT_NUMBER <int> 98, 2336, 840, 258, 135, 806, 612, 2013, 1112, 117…
## $ TAIL_NUMBER <chr> "N407AS", "N3KUAA", "N171US", "N3HYAA", "N527AS", …
## $ ORIGIN_AIRPORT <chr> "ANC", "LAX", "SFO", "LAX", "SEA", "SFO", "LAS", "…
## $ DESTINATION_AIRPORT <chr> "SEA", "PBI", "CLT", "MIA", "ANC", "MSP", "MSP", "…
## $ SCHEDULED_DEPARTURE <int> 5, 10, 20, 20, 25, 25, 25, 30, 30, 30, 30, 35, 35,…
## $ DEPARTURE_TIME <int> 2354, 2, 18, 15, 24, 20, 19, 44, 19, 33, 24, 27, 3…
## $ DEPARTURE_DELAY <int> -11, -8, -2, -5, -1, -5, -6, 14, -11, 3, -6, -8, 0…
## $ TAXI_OUT <int> 21, 12, 16, 15, 11, 18, 11, 13, 17, 12, 12, 21, 18…
## $ WHEELS_OFF <int> 15, 14, 34, 30, 35, 38, 30, 57, 36, 45, 36, 48, 53…
## $ SCHEDULED_TIME <int> 205, 280, 286, 285, 235, 217, 181, 273, 195, 221, …
## $ ELAPSED_TIME <int> 194, 279, 293, 281, 215, 230, 170, 249, 193, 203, …
## $ AIR_TIME <int> 169, 263, 266, 258, 199, 206, 154, 228, 173, 186, …
## $ DISTANCE <int> 1448, 2330, 2296, 2342, 1448, 1589, 1299, 2125, 14…
## $ WHEELS_ON <int> 404, 737, 800, 748, 254, 604, 504, 745, 529, 651, …
## $ TAXI_IN <int> 4, 4, 11, 8, 5, 6, 5, 8, 3, 5, 4, 7, 4, 5, 4, 4, 4…
## $ SCHEDULED_ARRIVAL <int> 430, 750, 806, 805, 320, 602, 526, 803, 545, 711, …
## $ ARRIVAL_TIME <int> 408, 741, 811, 756, 259, 610, 509, 753, 532, 656, …
## $ ARRIVAL_DELAY <int> -22, -9, 5, -9, -21, 8, -17, -10, -13, -15, -30, -…
## $ DIVERTED <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ CANCELLED <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ CANCELLATION_REASON <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""…
## $ AIR_SYSTEM_DELAY <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ SECURITY_DELAY <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ AIRLINE_DELAY <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ LATE_AIRCRAFT_DELAY <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ WEATHER_DELAY <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
# Check supporting dataset
# Airport information
glimpse(airports)
## Rows: 322
## Columns: 7
## $ IATA_CODE <chr> "ABE", "ABI", "ABQ", "ABR", "ABY", "ACK", "ACT", "ACV", "ACY…
## $ AIRPORT <chr> "Lehigh Valley International Airport", "Abilene Regional Air…
## $ CITY <chr> "Allentown", "Abilene", "Albuquerque", "Aberdeen", "Albany",…
## $ STATE <chr> "PA", "TX", "NM", "SD", "GA", "MA", "TX", "CA", "NJ", "AK", …
## $ COUNTRY <chr> "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA…
## $ LATITUDE <dbl> 40.7, 32.4, 35.0, 45.4, 31.5, 41.3, 31.6, 41.0, 39.5, 51.9, …
## $ LONGITUDE <dbl> -75.4, -99.7, -106.6, -98.4, -84.2, -70.1, -97.2, -124.1, -7…
# Missing values
colSums(is.na(airports))
## IATA_CODE AIRPORT CITY STATE COUNTRY LATITUDE LONGITUDE
## 0 0 0 0 0 3 3
# Fill out missing values
na_airport <- airports[is.na(airports$LATITUDE), "IATA_CODE"] # find missing airport IATA code
# Create empty tibble with column names to store the info
airport_info <- tibble(
"IATA_CODE" = character(),
"LATITUDE" = numeric(),
"LONGITUDE" = numeric()
)
# Function to find the location info for missing values
i <- 1
for (i in seq_along(na_airport)) {
airport_info[i, 1] <- na_airport[i] # IATA_CODE
airport_info[i, 2:3] <- airport_location(na_airport[i]) # geo location info
i <- i + 1
}
# Combine to the original Airport data frame
airports[is.na(airports$LATITUDE), "LATITUDE"] <- airport_info$LATITUDE
airports[is.na(airports$LONGITUDE), "LONGITUDE"] <- airport_info$LONGITUDE
# Airline information
glimpse(airlines)
## Rows: 14
## Columns: 2
## $ IATA_CODE <chr> "UA", "AA", "US", "F9", "B6", "OO", "AS", "NK", "WN", "DL", …
## $ AIRLINE <chr> "United Air Lines Inc.", "American Airlines Inc.", "US Airwa…
colSums(is.na(airlines))
## IATA_CODE AIRLINE
## 0 0
Explanation
flight_merged <- flights %>%
left_join(airports, by = c("ORIGIN_AIRPORT" = "IATA_CODE")) %>%
left_join(airports, by = c("DESTINATION_AIRPORT" = "IATA_CODE")) %>%
select(-contains("COUNTRY")) %>% # all flights were departing in the US
clean_names(
replace = c(`.x` = "_ORIGIN", `.y` = "_DESTINATION")
) %>%
left_join(airlines, by = c("airline" = "IATA_CODE")) %>%
rename(
"airline_name" = AIRLINE,
"scheduled_airtime" = scheduled_time
) %>%
filter(cancelled == 0) %>% # filter out cancelled flights
select(
-contains(c("taxi", "wheels", "elapsed", "arrival", "cancell")),
-diverted, -year, -c(air_system_delay:weather_delay)
) # remove non-informative column
# Check missing values
colSums(is.na(flight_merged))
## month day day_of_week
## 0 0 0
## airline flight_number tail_number
## 0 0 0
## origin_airport destination_airport scheduled_departure
## 0 0 0
## departure_time departure_delay scheduled_airtime
## 0 0 1
## air_time distance airport_origin
## 15187 0 483711
## city_origin state_origin latitude_origin
## 483711 483711 483711
## longitude_origin airport_destination city_destination
## 483711 483711 483711
## state_destination latitude_destination longitude_destination
## 483711 483711 483711
## airline_name
## 0
# Remove rows with empty air time information
flight_merged <- flight_merged %>%
filter(!is.na(air_time))
# Check rows with empty airport info
summary(flight_merged[is.na(flight_merged$airport_origin), "month"])
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 10 10 10 10 10 10
#double check to make sure no missing value is included: colSums(is.na(flight_merged))
# Check duplicated values
flight_dup <- flight_merged %>% get_dupes(
month, day, scheduled_departure, # same time
origin_airport, destination_airport, # same location schedule
airline, tail_number # same flight
)
flight_dup <- flight_dup %>%
filter(flight_number != 1865) # only keep the last one which is correct
flight_merged <- anti_join(flight_merged, flight_dup, # remove wrong duplicated data
by = c(
"month", "day", "scheduled_departure",
"origin_airport", "destination_airport",
"airline", "tail_number", "flight_number"
)
) %>%
select(-flight_number)
Interpretation
flight_merged <- flight_merged %>%
mutate(air_time_delay = air_time - scheduled_airtime)
# Function to combine date time info
clean_dttm <- function(year, month, day, time) {
make_datetime(year, month, day, time %/% 100, time %% 100)
}
# Apply the function to the date set
flight_merged <- flight_merged %>%
mutate(
date = format(clean_dttm(2015, month, day, departure_time), "%b-%d"), # create date
day_of_week = factor(
as.factor(
day_of_week # convert to factor column, for plotting
),
labels = c(
"Mon",
"Tues",
"Wed",
"Thurs",
"Fri",
"Sat",
"Sun"
)
),
scheduled_departure = hms((scheduled_departure %/% 100 * 3600 # Hour
+ scheduled_departure %% 100 * 60)), # Mins
scheduled_dep_hour = hour(scheduled_departure) # Create ]sche_dep_hour column
)
flight_merged <- flight_merged %>%
mutate(departure_delayed_dummy = ifelse(
departure_delay >= 15,
1, # if delay exceeds 15 mins, equals 1
0 # if delay is shorter than 15 mins, equals 0
)) %>%
select(
date, month, day, day_of_week, # select dataset column order
scheduled_departure, scheduled_dep_hour,
departure_delay, departure_delayed_dummy,
scheduled_airtime, air_time_delay, distance,
airline, airline_name, everything()
)
# Write to csv
# write_csv(flight_merged, "flight_merged_clean.csv")
as.data.frame(summary(select_if(flight_merged, is.numeric))) %>%
select(-Var1) %>%
separate(Freq, into = c("attribute", "value"), sep = ":", extra = "merge") %>%
mutate(attribute = str_remove(attribute, "\\.")) %>%
rename(Variable = "Var2") %>%
pivot_wider(
names_from = attribute,
values_from = value
) %>%
select(-contains("NA")) %>%
filter(!grepl("longitude|latitude", Variable)) %>% #remove geo location columns
kable(
caption = "Summary Statistics",
format.args = list(scientific = FALSE)
) %>%
kable_classic() %>%
kable_styling(
position = "center", font_size = 8,
full_width = T, html_font = "Cambria",
c("stripend", "bordered")
)
| Variable | Min | 1st Qu | Median | Mean | 3rd Qu | Max |
|---|---|---|---|---|---|---|
| month | 1.00 | 4.00 | 7.00 | 6.55 | 9.00 | 12.00 |
| day | 1.0 | 8.0 | 16.0 | 15.7 | 23.0 | 31.0 |
| scheduled_dep_hour | 0 | 9 | 13 | 13 | 17 | 23 |
| departure_delay | -82 | -5 | -2 | 9 | 7 | 1988 |
| departure_delayed_dummy | 0.000 | 0.000 | 0.000 | 0.184 | 0.000 | 1.000 |
| scheduled_airtime | 18 | 85 | 123 | 142 | 174 | 718 |
| air_time_delay | -216.0 | -34.0 | -28.0 | -28.4 | -22.0 | 189.0 |
| distance | 31 | 373 | 650 | 824 | 1065 | 4983 |
| departure_time | 1 | 921 | 1330 | 1335 | 1740 | 2400 |
| air_time | 7 | 60 | 94 | 114 | 144 | 690 |
# Find distribution of month
count(flight_merged, month) %>% arrange(desc(n))
# Find the overall percentage of delayed flights
count(flight_merged, departure_delayed_dummy)
# Find distribution of origin and destination city
count(filter(flight_merged, !is.na(city_destination)), city_origin) %>%
arrange(desc(n)) %>%
head(10) %>%
cbind(count(filter(flight_merged, !is.na(city_destination)), city_destination) %>%
arrange(desc(n)) %>%
head(10))
Interpretation
Based on the analysis above, we can see that most flights are not delayed, while the maximum delay minutes were 1988. The dataset contains flight info from the start of the year to the end of 2015. After cleaning the dataset, there are 5819079 observations left and those data are relatively evenly distributed by Month.
Chicago has the highest number of flights either by departure or arrival number. The top 10 airports with the highest flight number does not change either by departure or arrival.
# Set theme
ggthemr("fresh")
theme <-
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 10, face = "italic", hjust = 0.5),
axis.title = element_text(size = 9, face = "bold"),
plot.caption = element_text(size = 6, face = "italic", hjust = 0),
axis.text = element_text(size = 9),
legend.text = element_text(size = 8),
legend.title = element_text(size = 8)
)
# Calculate correlation
corr_mat <- round(cor(select_if(select(flight_merged, -contains(c("latitude", "longitude"))), is.numeric)), 2)
# Reshape correlation data
melted_corr_mat <- melt(corr_mat)
# Plot
plt_corr <- ggplot(melted_corr_mat) +
geom_tile(aes(Var1, Var2, fill = value)) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 5)) +
scale_fill_continuous_tableau(
name = "Value",
breaks = seq(-0.5, 1, 0.2)
) +
labs(title = "Correlation of Flight Delay Index in 2015") +
theme +
theme(axis.text = element_text(size = 6)) +
legend_top()
ggplotly(plt_corr)
Explanation
flight_merged %>%
ggplot() +
geom_freqpoly(aes(departure_delay)) +
labs(x = "Departure Delay(mins)")
Explanation
# Write function to calculate the percentage by selected group
# function
#one disaggregation variable
get_delay_pct <- function(col, df) {
# get data frame
data <- df %>%
group_by(df[[col]]) %>%
summarise(delay_pct = mean(departure_delayed_dummy))
colnames(data) <- c(col, "delay_pct")
data
}
#multiple disaggregation variables
get_delay_pct_db <- function(col1, col2, df) {
# get data frame
data <- df %>%
group_by(df[[col1]], df[[col2]]) %>%
summarise(delay_pct = mean(departure_delayed_dummy))
colnames(data) <- c(col1, col2, "delay_pct")
data
}
# Generate Plot
get_plt <- function(var, df) {
plt <- df %>%
ggplot() +
geom_line(aes(factor(df[[var]]), delay_pct,
group = 1)) +
labs(
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
theme
plt
}
# By month
plt_month <- get_plt("month", get_delay_pct("month", flight_merged)) +
labs(
subtitle = "Summer and Winter has Highest Delayed Flights Rate",
x = "Month"
)
# By day of the week
plt_week <- get_plt("day_of_week", get_delay_pct("day_of_week", flight_merged)) +
labs(
subtitle = "Monday and Thursday has Highest Delayed Flights Rate",
x = "Day of the Week"
)
# By hour
plt_hour <- get_plt("scheduled_dep_hour", get_delay_pct("scheduled_dep_hour", flight_merged)) +
labs(
subtitle = "Early Evening has Highest Delayed Flights Rate",
x = "Departure Hour"
) +
scale_x_discrete(breaks = seq(0, 23, 2))
#Check number of flights
flight_merged %>%
group_by(scheduled_dep_hour) %>%
summarise(count = n()) %>%
arrange(count)
(plt_month | plt_hour) /
plt_week
Explanation
June has the highest delay rate, followed by other months in summer and February. Delay rate in Autumn is pretty low.
During the week, midweek has higher delay rate. The rate then goes down and increases sharply on Sunday and Monday. The trend is potentially related to work schedule. Near the end of the week people need to come back to home city or plans to go out during the weekend, then before workday starts they need to take flights back. As the demand increases, delay could be more likely to happen.
During the day, 4am-6am has the lowest delay rate, but by checking the data we know that the number of flights are also significantly less compared to any other time period. Starting early morning, the delay rate continues to increase, peaking at early night around 8pm. Besides, 2am has the second highest delay rate.
# By month and day
get_delay_pct_db("month", "day_of_week", flight_merged) %>%
ggplot() +
geom_line(aes(day_of_week, delay_pct,
group = 1)) +
labs(
title = "Delayed Flights Rate by Month and Day",
x = "Day",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
facet_wrap(~month) +
theme +
theme(axis.text.x = element_text(angle = 90))
# By month and hour
get_delay_pct_db("scheduled_dep_hour", "day_of_week", flight_merged) %>%
ggplot() +
geom_line(aes(scheduled_dep_hour, delay_pct)) +
labs(
title = "Delayed Flights Rate by Day and Hour",
x = "Hour",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
facet_wrap(~day_of_week) +
theme
Explanation
Distribution of delay rate during the week varies across month. In general, summer months have high delay rate throughout the week, with a slightly higher rate during the beginning of a week. In December, early days during the week are more likely to have delay. All other months follow the overall trend.
The delay pattern by hour is quite similar during throughout the week.
# By origin
plt_delay_ori_h <- get_delay_pct("origin_airport", filter(flight_merged, month!= 10)) %>%
slice_max(order_by = delay_pct, n = 10) %>%
ggplot() +
geom_col(aes(fct_reorder(origin_airport, desc(delay_pct)),
delay_pct),
fill = "#65ADC2") +
labs(
title = "Airports witht the Highest Delayed Rate",
x = "Origin Airport",
y = "Delay Percentage") +
theme
plt_delay_des_h <- get_delay_pct("destination_airport", filter(flight_merged, month!= 10)) %>%
slice_max(order_by = delay_pct, n = 10) %>%
ggplot() +
geom_col(aes(fct_reorder(destination_airport, desc(delay_pct)),
delay_pct),
fill = "#65ADC2") +
labs(
x = "Destination Airport",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015") +
theme
ggarrange(plt_delay_ori_h, plt_delay_des_h,
nrow = 2)
# ILG By month
get_delay_pct("month", filter(flight_merged, origin_airport == "ILG")) %>%
arrange(desc(delay_pct))
# By hour
get_delay_pct("scheduled_dep_hour", filter(flight_merged, origin_airport == "ILG")) %>%
ggplot() +
geom_col(aes(scheduled_dep_hour, delay_pct)) +
scale_x_continuous(breaks = seq(1, 24, 1)) +
labs(
title = "Delayed Flights Rate by Hour in Wilmington Airport",
x = "Hour",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
theme
# Destination
get_delay_pct("destination_airport", filter(flight_merged, origin_airport == "ILG")) %>%
arrange(desc(delay_pct))
Explanation
# Carriers with the highest delay rate in 2015
get_delay_pct("airline_name", flight_merged) %>%
arrange(desc(delay_pct)) %>%
ggplot() +
geom_col(aes(x = fct_reorder(airline_name, delay_pct),
y = delay_pct)
) +
scale_y_continuous(breaks = seq(0, 0.25, 0.05)) +
labs(title = "Carrier with Highest Flight Delay Rate",
caption = "Data Source: US Department of Transportation, Year 2015",
x = "Carrier",
y = "Percentage of Delayed Flights") +
coord_flip() +
theme
# Analysis:
#flight_merged %>% filter(departure_delay > 15) %>%
# mutate(departure_delay = winsorise(departure_delay, probs = c(0, 0.9))) %>%
# ggplot(aes(cut_interval(departure_delay, 6), air_time_delay)) +
# geom_boxplot()
Explanation
chicago <- flight_merged %>%
filter(city_origin == "Chicago")
# By month
get_plt("month", get_delay_pct("month", chicago)) +
labs(
title = "Delayed Flights Rate by Month in Chicago",
subtitle = "June and Feb has the Highest Delayed Flights Rate",
x = "Month"
) +
theme
# Which Flight to choose in Feb or June
chicago %>%
filter(month %in% c(2, 6)) %>%
group_by(day_of_week, airline) %>%
summarise(delay_pct = mean(departure_delayed_dummy)) %>%
slice_min(order_by = delay_pct)
chicago %>%
filter(month %in% c(2, 6)) %>%
group_by(scheduled_dep_hour, airline) %>%
summarise(delay_pct = mean(departure_delayed_dummy)) %>%
slice_min(order_by = delay_pct)
# By carrier
get_delay_pct_db("scheduled_dep_hour", "airline_name", chicago) %>%
ggplot() +
geom_line(aes(scheduled_dep_hour, delay_pct)) +
scale_x_continuous(breaks = seq(0, 23, 2)) +
labs(
title = "Delayed Flights Rate by Carrier in Chicago",
x = "Departure Hour",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
facet_wrap(~airline_name) +
theme
# By destination: if we're going to New York
get_delay_pct_db("destination_airport", "airline_name", filter(chicago, city_destination == "New York")) %>%
arrange(delay_pct, destination_airport)
get_delay_pct_db("destination_airport", "scheduled_dep_hour",
filter(chicago, city_destination == "New York")) %>%
arrange(delay_pct) %>%
ggplot() +
geom_line(
aes(scheduled_dep_hour, delay_pct,
color = destination_airport
),
size = 1
) +
scale_x_continuous(breaks = seq(0, 23, 2)) +
labs(
title = "Delayed Flights Rate by Carrier in Chicago",
x = "Departure Hour",
y = "Delay Percentage",
caption = "Data Source: US DOT, Year 2015"
) +
theme
Explanation
In general, flights in June and February has the highest delay rate, which is likely caused by the weather condition and number of people on travel during the summer. The delay rate is also higher than the average across the nation.
All flights have a higher delay rate at night and all carriers perform similarly on flight delay, while VX has an abnormally high delay rate compare to others around noon. Also, Frontier Airline is not a good choice in particular if we want to leave Chicago in early morning like 4am. Among all others, JetBlue, Delta and Spirit are not good choices as they have higher delay rate overall.
If we want to go New York, Skywest Airlines Inc. to LFA and Delta Air Lines Inc. to JFK has a lower delay rate on average. And the delay rate at JFK is higher in most cases during the day except between 10am-12am
flight_merged <- flight_merged %>%
mutate(day_of_week = as.numeric(day_of_week)) # convert to numeric for model
#a.Basic model
#Does departure delay related to departure time
reg <- lm(departure_delay ~ month + day_of_week + scheduled_dep_hour, data = flight_merged)
stargazer(reg, type = "text")
##
## ===================================================
## Dependent variable:
## -------------------------------
## departure_delay
## ---------------------------------------------------
## month -0.238***
## (0.005)
##
## day_of_week -0.232***
## (0.008)
##
## scheduled_dep_hour 0.844***
## (0.003)
##
## Constant 0.781***
## (0.061)
##
## ---------------------------------------------------
## Observations 5,713,997
## R2 0.013
## Adjusted R2 0.013
## Residual Std. Error 36.700 (df = 5713993)
## F Statistic 24,645.000*** (df = 3; 5713993)
## ===================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
#b.Improve model
reg_full <- lm(departure_delay ~ month + day_of_week + scheduled_dep_hour
+ airline + air_time*distance, data = flight_merged)
stargazer(reg_full, type = "latex")
##
## % Table created by stargazer v.5.2.3 by Marek Hlavac, Social Policy Institute. E-mail: marek.hlavac at gmail.com
## % Date and time: Sat, Dec 10, 2022 - 09:45:02
## \begin{table}[!htbp] \centering
## \caption{}
## \label{}
## \begin{tabular}{@{\extracolsep{5pt}}lc}
## \\[-1.8ex]\hline
## \hline \\[-1.8ex]
## & \multicolumn{1}{c}{\textit{Dependent variable:}} \\
## \cline{2-2}
## \\[-1.8ex] & departure\_delay \\
## \hline \\[-1.8ex]
## month & $-$0.274$^{***}$ \\
## & (0.005) \\
## & \\
## day\_of\_week & $-$0.234$^{***}$ \\
## & (0.008) \\
## & \\
## scheduled\_dep\_hour & 0.849$^{***}$ \\
## & (0.003) \\
## & \\
## airlineAS & $-$7.570$^{***}$ \\
## & (0.098) \\
## & \\
## airlineB6 & 2.030$^{***}$ \\
## & (0.084) \\
## & \\
## airlineDL & $-$1.550$^{***}$ \\
## & (0.059) \\
## & \\
## airlineEV & 0.423$^{***}$ \\
## & (0.068) \\
## & \\
## airlineF9 & 3.590$^{***}$ \\
## & (0.129) \\
## & \\
## airlineHA & $-$6.940$^{***}$ \\
## & (0.143) \\
## & \\
## airlineMQ & 1.830$^{***}$ \\
## & (0.084) \\
## & \\
## airlineNK & 6.440$^{***}$ \\
## & (0.116) \\
## & \\
## airlineOO & $-$0.553$^{***}$ \\
## & (0.067) \\
## & \\
## airlineUA & 5.090$^{***}$ \\
## & (0.068) \\
## & \\
## airlineUS & $-$3.680$^{***}$ \\
## & (0.095) \\
## & \\
## airlineVX & $-$0.385$^{**}$ \\
## & (0.155) \\
## & \\
## airlineWN & 1.720$^{***}$ \\
## & (0.055) \\
## & \\
## air\_time & 0.015$^{***}$ \\
## & (0.001) \\
## & \\
## distance & 0.002$^{***}$ \\
## & (0.0002) \\
## & \\
## air\_time:distance & $-$0.00001$^{***}$ \\
## & (0.00000) \\
## & \\
## Constant & $-$1.830$^{***}$ \\
## & (0.094) \\
## & \\
## \hline \\[-1.8ex]
## Observations & 5,713,997 \\
## R$^{2}$ & 0.019 \\
## Adjusted R$^{2}$ & 0.019 \\
## Residual Std. Error & 36.500 (df = 5713977) \\
## F Statistic & 5,894.000$^{***}$ (df = 19; 5713977) \\
## \hline
## \hline \\[-1.8ex]
## \textit{Note:} & \multicolumn{1}{r}{$^{*}$p$<$0.1; $^{**}$p$<$0.05; $^{***}$p$<$0.01} \\
## \end{tabular}
## \end{table}
# Output residual and RMSE
summary(reg_full)$r.squared
## [1] 0.0192
rmse(reg_full, flight_merged)
## [1] 36.5
# Plot
ggplot(reg_full, aes(x = scheduled_dep_hour, y = departure_delay )) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)
#c.Train dataset
sample <- sample.split(flight_merged$month, SplitRatio = 0.6)
train <- subset(flight_merged, sample == TRUE)
test <- subset(flight_merged, sample == FALSE)
# Run regression again
reg_train <- lm(departure_delay ~ month + day_of_week + scheduled_dep_hour
+ airline + air_time*distance, data = train)
summary(reg_train)
##
## Call:
## lm(formula = departure_delay ~ month + day_of_week + scheduled_dep_hour +
## airline + air_time * distance, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -85.3 -14.2 -9.0 -1.5 1983.1
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.867425435 0.120798505 -15.46 < 0.0000000000000002 ***
## month -0.276485050 0.005916699 -46.73 < 0.0000000000000002 ***
## day_of_week -0.230856710 0.009941839 -23.22 < 0.0000000000000002 ***
## scheduled_dep_hour 0.852658457 0.004105224 207.70 < 0.0000000000000002 ***
## airlineAS -7.637303865 0.127178892 -60.05 < 0.0000000000000002 ***
## airlineB6 2.059091204 0.107970748 19.07 < 0.0000000000000002 ***
## airlineDL -1.505846977 0.075922604 -19.83 < 0.0000000000000002 ***
## airlineEV 0.451233798 0.087778143 5.14 0.000000273852951 ***
## airlineF9 3.666706124 0.167167515 21.93 < 0.0000000000000002 ***
## airlineHA -6.871150081 0.185364926 -37.07 < 0.0000000000000002 ***
## airlineMQ 1.794219182 0.108603110 16.52 < 0.0000000000000002 ***
## airlineNK 6.490703946 0.150037641 43.26 < 0.0000000000000002 ***
## airlineOO -0.667283231 0.086507884 -7.71 0.000000000000012 ***
## airlineUA 4.974539890 0.087242617 57.02 < 0.0000000000000002 ***
## airlineUS -3.635382362 0.122883065 -29.58 < 0.0000000000000002 ***
## airlineVX -0.276869679 0.199262737 -1.39 0.16
## airlineWN 1.727393010 0.071136643 24.28 < 0.0000000000000002 ***
## air_time 0.014790035 0.001661424 8.90 < 0.0000000000000002 ***
## distance 0.002152075 0.000204512 10.52 < 0.0000000000000002 ***
## air_time:distance -0.000008442 0.000000322 -26.25 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 36.5 on 3428380 degrees of freedom
## Multiple R-squared: 0.0193, Adjusted R-squared: 0.0193
## F-statistic: 3.55e+03 on 19 and 3428380 DF, p-value: <0.0000000000000002
#Plot
prediction <- matrix(nrow = 2285597, ncol = 2)
prediction[, 1] <- predict(reg_train, test)
prediction[, 2] <- test$departure_delayed
as.data.frame(prediction) %>%
ggplot(aes(V1, V2)) +
geom_point(alpha = 0.5) +
geom_smooth(color = "black") +
labs(x = "Predicted Delay",
y = "Real Delay")
#d.Logistic Regression
reg_lo <- glm(departure_delayed_dummy ~ month + day_of_week + scheduled_dep_hour
+ airline + air_time * distance, data = train, family = "binomial")
summary(reg_lo)
##
## Call:
## glm(formula = departure_delayed_dummy ~ month + day_of_week +
## scheduled_dep_hour + airline + air_time * distance, family = "binomial",
## data = train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.253 -0.681 -0.551 -0.426 2.940
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -2.7520740734 0.0090688354 -303.46 < 0.0000000000000002 ***
## month -0.0319558967 0.0004260183 -75.01 < 0.0000000000000002 ***
## day_of_week -0.0145705671 0.0007163818 -20.34 < 0.0000000000000002 ***
## scheduled_dep_hour 0.0927291895 0.0003037650 305.27 < 0.0000000000000002 ***
## airlineAS -0.6020825284 0.0109606457 -54.93 < 0.0000000000000002 ***
## airlineB6 0.2438483976 0.0074674398 32.65 < 0.0000000000000002 ***
## airlineDL -0.2556416451 0.0057902175 -44.15 < 0.0000000000000002 ***
## airlineEV 0.0527245490 0.0064416358 8.18 0.00000000000000027 ***
## airlineF9 0.2601986900 0.0112554051 23.12 < 0.0000000000000002 ***
## airlineHA -0.8601796860 0.0188310412 -45.68 < 0.0000000000000002 ***
## airlineMQ 0.2257811616 0.0076976024 29.33 < 0.0000000000000002 ***
## airlineNK 0.5307367036 0.0096843871 54.80 < 0.0000000000000002 ***
## airlineOO -0.0188673862 0.0064124011 -2.94 0.0033 **
## airlineUA 0.3697183164 0.0060360907 61.25 < 0.0000000000000002 ***
## airlineUS -0.2973073632 0.0094405887 -31.49 < 0.0000000000000002 ***
## airlineVX 0.0283335322 0.0144089491 1.97 0.0493 *
## airlineWN 0.2549596889 0.0050997355 49.99 < 0.0000000000000002 ***
## air_time 0.0007803207 0.0001177751 6.63 0.00000000003460352 ***
## distance 0.0002633596 0.0000146305 18.00 < 0.0000000000000002 ***
## air_time:distance -0.0000008946 0.0000000249 -35.94 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 3272697 on 3428399 degrees of freedom
## Residual deviance: 3135336 on 3428380 degrees of freedom
## AIC: 3135376
##
## Number of Fisher Scoring iterations: 5
fitted.results <- predict(reg_lo, test, type = "response")
fitted.results <- ifelse(fitted.results > 0.5, 1, 0)
confusionMatrix(test$departure_delayed_dummy, fitted.results)
sensitivity(test$departure_delayed_dummy, fitted.results) # true positive rate
## [1] 0.000171
specificity(test$departure_delayed_dummy, fitted.results) #rue negative rate
## [1] 1
# Plot
prediction <- matrix(nrow = 2285597, ncol = 2)
prediction[, 1] <- fitted.results
prediction[, 2] <- test$departure_delayed_dummy
as.data.frame(prediction) %>%
group_by(V1, V2) %>%
summarise(count = n()) %>%
ggplot(aes(factor(V1), count)) +
geom_col(aes(fill = factor(V2)),
position = "dodge") +
scale_fill_brewer("Real Delay") +
labs(x = "Predicted Delay",
y = "Count") +
facet_wrap(~V1,
scales = "free")
Explanation
The coefficient of scheduled departure hour on departure delay is 0.841 and is significant given alpha equals 0.01, which means given all other conditions the same, a flight 1 hour late will have an expected increase at 0.841 min for departure delay.
The coefficient of month and day of the week are both negative, which means the later in a month or in a day of the week, the more likely we need to expect a higher delay for departure. The result aligns with the exploratory analysis in part 3.
For airline, given all others equal, we will expect 6.520 mins increase on departure delay on average if taking a flight with Spirit Air Lines. Other airlines which has an obvious negative effect on departure delay includes Frontier Airlines Inc., JetBlue Airways, United Air Lines Inc.. Most of the airlines are cheap airlines, so it makes sense that flight delay is more likely to happen. In contrast, a flight with Hawaiian Airlines Inc. will have an expected decrease at 8.5 mins for departure delay.
However, the R square is 0.019, which performs very poor. It means the departure delay time for flights in US in 2015 does not significantly related to departure hour, although it’s significant at 1% level. As shown in the plot, the prediction model cannot predict the real departure delay time well.
With the logistic regression however, the overall accuracy turns out to be good, which is 81.6%. However, sensitivity is low and if we look at the false negative rate(77.4%), the prediction is not good at predicting delay as well.