This homework assignment uses the flights dataset from
the nycflights13 package, which contains real-world data on
over 336,000 flights departing from New York City airports (JFK, LGA,
EWR) in 2013. The dataset includes variables such as departure and
arrival times (with date components), airline carrier (categorical),
origin and destination airports (categorical), delays (with missing
values for cancelled flights), distance, and more. It is sourced from
the US Bureau of Transportation Statistics.
This assignment reinforces the Week 4 topics:
lubridate.zoo.All questions (except the final reflection) require you to write and run R code to solve them. Submit your URL for your RPubs. Make sure to comment your code, along with key outputs (e.g., summaries, plots, or tables). Use the provided setup code to load the data.
Install and load the necessary packages if not already done:
#install.packages(c("nycflights13", "dplyr", "lubridate", "zoo", "forcats")) # If needed
library(nycflights13)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(zoo)
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(forcats) # For factor recoding; base R alternatives are acceptable
data(flights) # Load the dataset
Explore the data briefly with str(flights) and
head(flights) to understand the structure. Note: Dates are
in separate year, month, day
columns; times are in dep_time and arr_time
(as integers like 517 for 5:17 AM).
#Explore your data here
str(flights)
## tibble [336,776 × 19] (S3: tbl_df/tbl/data.frame)
## $ year : int [1:336776] 2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
## $ month : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
## $ day : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
## $ dep_time : int [1:336776] 517 533 542 544 554 554 555 557 557 558 ...
## $ sched_dep_time: int [1:336776] 515 529 540 545 600 558 600 600 600 600 ...
## $ dep_delay : num [1:336776] 2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
## $ arr_time : int [1:336776] 830 850 923 1004 812 740 913 709 838 753 ...
## $ sched_arr_time: int [1:336776] 819 830 850 1022 837 728 854 723 846 745 ...
## $ arr_delay : num [1:336776] 11 20 33 -18 -25 12 19 -14 -8 8 ...
## $ carrier : chr [1:336776] "UA" "UA" "AA" "B6" ...
## $ flight : int [1:336776] 1545 1714 1141 725 461 1696 507 5708 79 301 ...
## $ tailnum : chr [1:336776] "N14228" "N24211" "N619AA" "N804JB" ...
## $ origin : chr [1:336776] "EWR" "LGA" "JFK" "JFK" ...
## $ dest : chr [1:336776] "IAH" "IAH" "MIA" "BQN" ...
## $ air_time : num [1:336776] 227 227 160 183 116 150 158 53 140 138 ...
## $ distance : num [1:336776] 1400 1416 1089 1576 762 ...
## $ hour : num [1:336776] 5 5 5 5 6 5 6 6 6 6 ...
## $ minute : num [1:336776] 15 29 40 45 0 58 0 0 0 0 ...
## $ time_hour : POSIXct[1:336776], format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...
head(flights)
## # A tibble: 6 × 19
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## 6 2013 1 1 554 558 -4 740 728
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## # hour <dbl>, minute <dbl>, time_hour <dttm>
lubridateCreate a column dep_datetime by combining year, month, day, and
dep_time into a POSIXct datetime using lubridate. (Hint: Use
make_datetime function to combine: year, month, day, for
hour and min use division, e.g., hour = dep_time %/% 100, min = dep_time
%% 100.)
Show the first 5 rows of flights with dep_datetime.
Output: First 5 rows showing year, month, day, dep_time, and dep_datetime.
flights <- flights |>
mutate(dep_datetime = make_datetime(year, month, day, hour = dep_time %/% 100,min = dep_time %% 100))
head(flights,5)
## # A tibble: 5 × 20
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## # ℹ 12 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## # hour <dbl>, minute <dbl>, time_hour <dttm>, dep_datetime <dttm>
lubridateUsing dep_datetime from Question 1, create a column weekday with the day of the week (e.g., “Mon”) using wday(dep_datetime, label = TRUE). Use table() to show how many flights occur on each weekday.
Output: The table of flight counts by weekday.
flights <- flights |>
mutate(weekday = wday(dep_time, label = TRUE))
table(flights$weekday)
##
## Sun Mon Tue Wed Thu Fri Sat
## 25 35 26 26 21 22 22
Filter for flights from JFK (origin == “JFK”) and create a zoo time series of departure delays (dep_delay) by dep_datetime. Plot the time series (use plot()). (Hint: Use a subset to avoid memory issues, e.g., first 1000 JFK flights using `slice_head().)
Output: The time series plot.
JFK <- flights |>
filter(origin == "JFK") |>
slice_head(n=1000)
jfk_flights <- zoo(JFK$dep_delay, order.by = JFK$dep_datetime)
## Warning in zoo(JFK$dep_delay, order.by = JFK$dep_datetime): some methods for
## "zoo" objects do not work if the index entries in 'order.by' are not unique
plot(jfk_flights, main = " JFK Departure Delays Over Time",
xlab = "Departure Time", ylab = "Departure Delay (min)",
col = "pink", type = "l")
#### Question 4: Working with Factors Convert the origin column
(airports: “JFK”, “LGA”, “EWR”) to a factor called origin_factor. Show
the factor levels with levels() and create a frequency table with
table(). Make a bar plot of flights by airport using barplot().
Output: The levels, frequency table, and bar plot.
flights$origin_factor <- factor(flights$origin)
levels(flights$origin_factor)
## [1] "EWR" "JFK" "LGA"
origin_table <- table(flights$origin_factor)
print(origin_table)
##
## EWR JFK LGA
## 120835 111279 104662
barplot(origin_table,
main = "Number of Flights by NYC Airport",
xlab = "Airport",
ylab = "Number of Flights",
col = "purple")
Recode origin_factor from Question 4 into a new column origin_recoded with full names: “JFK” to “Kennedy”, “LGA” to “LaGuardia”, “EWR” to “Newark” using fct_recode() or base R. Create a bar plot of the recoded factor.
Output: The new levels and bar plot.
flights$origin_recoded <- fct_recode(flights$origin_factor,
"Kennedy" = "JFK",
"LaGuardia" = "LGA",
"Newark" = "EWR")
levels(flights$origin_recoded)
## [1] "Newark" "Kennedy" "LaGuardia"
recoded_table <- table(flights$origin_recoded)
barplot(recoded_table,
main = "Number of Flights by Airport (Full Names)",
xlab = "Airport",
ylab = "Number of Flights",
col = "blue")
Count missing values in dep_delay and arr_delay using colSums(is.na(flights)). Impute missing dep_delay values with 0 (assuming no delay for cancelled flights) in a new column dep_delay_imputed. Create a frequency table of dep_delay_imputed for delays between -20 and 20 minutes (use filter() to subset).
Output: NA counts, and the frequency table for imputed delays.
colSums(is.na(flights[, c("dep_delay", "arr_delay")]))
## dep_delay arr_delay
## 8255 9430
flights <- flights |>
mutate(dep_delay_imputed = ifelse(is.na(dep_delay), 0, dep_delay))
flights |>
filter(dep_delay_imputed >= -20, dep_delay_imputed <= 20) |>
count(dep_delay_imputed) |>
print()
## # A tibble: 41 × 2
## dep_delay_imputed n
## <dbl> <int>
## 1 -20 37
## 2 -19 19
## 3 -18 81
## 4 -17 110
## 5 -16 162
## 6 -15 408
## 7 -14 498
## 8 -13 901
## 9 -12 1594
## 10 -11 2727
## # ℹ 31 more rows
Reflect on the assignment: What was easy or hard about working with flight dates or missing data? How might assuming zero delay for missing values (Question 6) affect conclusions about flight punctuality? What did you learn about NYC flights in 2013? (150-200 words)
Working with flight data in this assignment highlighted both the power and the complexity of real-world datasets. The first few question were fairly easy to reproduce with the flights dataset since most of them were showed in Activity 4. However, dealing with missing data was more challenging. Understanding why values like dep_delay were missing (often due to cancelled flights) required careful consideration, and choosing how to handle those gaps had significant implications for the analysis.
In Question 6, we imputed missing dep_delay values with zero, assuming cancelled flights had no delay. While this approach kept all rows in the dataset, it could lead to misleading conclusions about flight punctuality by underestimating the true extent of delays. Cancelled flights are not necessarily on time—they simply didn’t depart, and treating them as zero-delay can artificially inflate on-time performance statistics.
From analyzing the NYC flights data from 2013, I learned that each airport (JFK, LGA, and EWR) had thousands of departures with varying delay patterns depending on time and day. I also saw how data quality—especially around missing values—can significantly affect what we conclude. This assignment emphasized the importance of thoughtful data cleaning and transparent reporting when working with incomplete data.