DATA 101 — Homework 4: Dates, Factors & Missing Data

Author

Dawit Merdassa

Instructions

This homework assignment evaluates your ability to work with Dates (lubridate), Factors (forcats), and Missing Data (NA) in R.

You will analyze the flights dataset from the nycflights13 package, which contains records for all 336,776 commercial flights that departed from New York City (JFK, LGA, EWR) in 2013.

Setup and Libraries

Run the chunk below to load the required tidyverse packages and the flights dataset. If you have not yet installed nycflights13, run install.packages("nycflights13") in your R console first.

library(tidyverse)
library(lubridate)
library(forcats)
library(nycflights13)

# Load flights into your environment
data(flights)

Inspect the Dataset

# Inspect the dataset structure
glimpse(flights)
Rows: 336,776
Columns: 19
$ year           <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2…
$ month          <int> 1, 1, 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, 1, 1…
$ dep_time       <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558, …
$ sched_dep_time <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600, …
$ dep_delay      <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -1…
$ arr_time       <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849,…
$ sched_arr_time <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851,…
$ arr_delay      <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -1…
$ carrier        <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", "…
$ flight         <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, 4…
$ tailnum        <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N394…
$ origin         <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA",…
$ dest           <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD",…
$ air_time       <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, 1…
$ distance       <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733, …
$ hour           <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6…
$ minute         <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0…
$ time_hour      <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 0…

Notice that year, month, day, and dep_time are stored in separate numeric columns, carrier is a plain character string (<chr>), and several columns contain missing values (NA).


Question 1: Creating Datetimes with make_datetime()

Create a new column in flights called dep_datetime that combines year, month, day, and the departure time into a proper POSIXct datetime object using make_datetime().

Hint on dep_time: Times are recorded as integers like 517 (meaning 05:17) or 1830 (meaning 18:30). You can extract the hour using integer division %/% 100 and the minute using the modulus operator %% 100.

Display the first 5 rows showing year, month, day, dep_time, and your new dep_datetime column.

# Your code here:
flights <- flights %>%
  mutate(dep_datetime = make_datetime(
    year, month, day,
    hour = dep_time %/% 100,
    min = dep_time %% 100
  ))

flights %>%
  select(year, month, day, dep_time, dep_datetime) %>%
  head(5)
# A tibble: 5 × 5
   year month   day dep_time dep_datetime       
  <int> <int> <int>    <int> <dttm>             
1  2013     1     1      517 2013-01-01 05:17:00
2  2013     1     1      533 2013-01-01 05:33:00
3  2013     1     1      542 2013-01-01 05:42:00
4  2013     1     1      544 2013-01-01 05:44:00
5  2013     1     1      554 2013-01-01 05:54:00

Question 2: Extracting Components & Filtering with month()

Using your new dep_datetime column and lubridate’s month() function, filter the dataset for all flights that departed in June 2013.

How many total flights departed New York City in June?

# Your code here:
june_flights <- flights %>%
  filter(month(dep_datetime) == 6)

nrow(june_flights)
[1] 27234

Answer: Enter the number of June flights here. 27,234 flights. —

Question 3: Day of the Week Analysis with wday()

Add a new column called dep_wday to the dataset that extracts the day of the week from dep_datetime as an ordered label (e.g., Sunday, Monday, Tuesday…).

Count the number of departures for each day of the week, sorted from most flights to fewest flights. Which day of the week has the fewest scheduled flights leaving NYC?

# Your code here:
flights <- flights %>%
  mutate(
    dep_wday = wday(dep_datetime, label = TRUE, abbr = FALSE)
  )

flights %>%
  count(dep_wday, sort = TRUE)
# A tibble: 8 × 2
  dep_wday      n
  <ord>     <int>
1 Monday    49468
2 Tuesday   49273
3 Wednesday 48858
4 Friday    48703
5 Thursday  48654
6 Sunday    45643
7 Saturday  37922
8 <NA>       8255

Answer: Enter the day of the week with the fewest departures and its flight count here. Saturday has the fewest departure 37,922 —

Question 4: Converting Categorical Variables to Factors

The carrier column records the two-letter airline code (e.g., "UA", "AA", "DL"), but it is currently stored as a character vector.

Convert carrier into a formal factor and display all of its unique levels. How many distinct airlines operated flights out of NYC in 2013?

# Your code here:
flights <- flights %>%
  mutate(carrier = factor(carrier))

levels(flights$carrier)
 [1] "9E" "AA" "AS" "B6" "DL" "EV" "F9" "FL" "HA" "MQ" "OO" "UA" "US" "VX" "WN"
[16] "YV"
length(levels(flights$carrier))
[1] 16

Answer: Enter the number of unique airline carriers here. 16 —

Question 5: Collapsing Factor Levels with fct_collapse()

Use fct_collapse() to consolidate airline carriers into four clean categories: * Keep "UA" (United), "AA" (American), and "DL" (Delta) as their own separate levels. * Group all other airlines into a single category labeled "Other".

Generate a frequency count table displaying how many flights belong to each of these 4 carrier categories.

# Your code here:
flights <- flights %>%
  mutate(
    carrier_group = fct_collapse(
      carrier,
      United = "UA",
      American = "AA",
      Delta = "DL",
      Other = setdiff(levels(carrier), c("UA", "AA", "DL"))
    )
  )

flights %>%
  count(carrier_group, sort = TRUE)
# A tibble: 4 × 2
  carrier_group      n
  <fct>          <int>
1 Other         197272
2 United         58665
3 Delta          48110
4 American       32729

Question 6: Auditing Missing Data (NA)

How many flights in the dataset are missing a departure delay (dep_delay == NA)?

In 1–2 sentences, what does a missing departure time or departure delay typically signify for a scheduled commercial flight in the real world?

# Your code here:
sum(is.na(flights$dep_delay))
[1] 8255

Answer: Explain what missing departure delay data represents. 8,255 flights are missing departure delay data, likely because they were cancelled or didn’t have a recorded departure time. —

Question 7: Filtering Missing Data & Calculating Averages

  1. Filter the dataset to include ONLY completed flights (rows where dep_delay is not missing). Save this cleaned dataset as flights_clean. How many flights remain?
  2. Using flights_clean, calculate the average departure delay (in minutes) across all completed flights. Round your answer to 2 decimal places.
# Your code here:
flights_clean <- flights %>%
  filter(!is.na(dep_delay))

nrow(flights_clean)
[1] 328521
flights_clean %>%
  summarise(avg_dep_delay = round(mean(dep_delay), 2))
# A tibble: 1 × 1
  avg_dep_delay
          <dbl>
1          12.6

Answer: State the number of completed flights and the average departure delay in minutes. 328,521 completed flights, with an average departure delay of 12.64 minutes.