For this assignment, I analyzed airline arrival data for Alaska Airlines and AM West across five destinations: Los Angeles, Phoenix, San Diego, San Francisco, and Seattle.
I recreated the provided data in a CSV file using the original wide format and imported it into R. I checked the dataset for missing values and used dplyr and tidyr to transform it from wide format to long format.
I then calculated the percentage of flights that were delayed and on time for each airline. I compared the airlines’ overall performance and examined their delay percentages separately across all five destinations using tables and visualizations.
Finally, I investigated the difference between the overall and city-by-city comparisons to understand how the distribution of flights across destinations affected the results.
I recreated the airline arrival data from the assignment and saved it as a CSV file. The original dataset contains four rows representing the two arrival statuses for each airline and five columns containing destination-specific flight counts.
Data source: The airline arrival-delay table provided in the DATA 607 Tidying and Transforming Data assignment, originally attributed to Numbersense by Kaiser Fung (McGraw-Hill, 2013).
The dataset includes 20 flight counts covering two airlines, two arrival statuses, and five destinations. All 20 reported counts were present in the recreated CSV, so no missing values needed to be filled.
airline_data <- data.frame(
Airline = c("Alaska", "Alaska", "AM West", "AM West"),
Arrival = c("On Time", "Delayed", "On Time", "Delayed"),
Los_Angeles = c(497, 62, 694, 117),
Phoenix = c(221, 12, 4840, 415),
San_Diego = c(212, 20, 383, 65),
San_Francisco = c(503, 102, 320, 129),
Seattle = c(1841, 305, 201, 61)
)
write.csv(
airline_data,
"airline_delays.csv",
row.names = FALSE
)
print(airline_data)## Airline Arrival Los_Angeles Phoenix San_Diego San_Francisco Seattle
## 1 Alaska On Time 497 221 212 503 1841
## 2 Alaska Delayed 62 12 20 102 305
## 3 AM West On Time 694 4840 383 320 201
## 4 AM West Delayed 117 415 65 129 61
## Airline Arrival Los_Angeles Phoenix San_Diego San_Francisco Seattle
## 1 Alaska On Time 497 221 212 503 1841
## 2 Alaska Delayed 62 12 20 102 305
## 3 AM West On Time 694 4840 383 320 201
## 4 AM West Delayed 117 415 65 129 61
## [1] 4 7
## Airline Arrival Los_Angeles Phoenix San_Diego
## 0 0 0 0 0
## San_Francisco Seattle
## 0 0
I checked the original dataset for missing values before transforming it. All reported flight counts were present, so no values needed to be replaced. If missing values are found in future data, I will investigate them before deciding how to handle them.
## Airline Arrival Los_Angeles Phoenix San_Diego
## 0 0 0 0 0
## San_Francisco Seattle
## 0 0
## [1] 0
Although the recreated dataset contains no missing flight counts, the following example demonstrates how missing values could be populated when the source confirms that a missing count represents zero flights.
example_data <- airline_data
example_data$Phoenix[1] <- NA
example_data$Phoenix_Filled <- replace_na(
example_data$Phoenix,
0
)
print(
example_data %>%
select(Airline, Arrival, Phoenix, Phoenix_Filled)
)## Airline Arrival Phoenix Phoenix_Filled
## 1 Alaska On Time NA 0
## 2 Alaska Delayed 12 12
## 3 AM West On Time 4840 4840
## 4 AM West Delayed 415 415
stopifnot(nrow(airline_data) == 4)
stopifnot(ncol(airline_data) == 7)
stopifnot(sum(is.na(airline_data)) == 0)
stopifnot(all(airline_data[, 3:7] >= 0))
cat("All data validation checks passed.")## All data validation checks passed.
I validated the dataset by checking that it contained four rows and seven columns, had no missing values, and included no negative flight counts. All validation checks passed.
I used pivot_longer() to combine the five destination
columns into a single Destination column. This produced a dataset with
20 rows and four columns, making it easier to compare flight counts
across airlines and destinations.
airline_long <- airline_data %>%
pivot_longer(
cols = -c(Airline, Arrival),
names_to = "Destination",
values_to = "Flights"
) %>%
mutate(
Destination = gsub("_", " ", Destination)
)
print(airline_long)## # A tibble: 20 × 4
## Airline Arrival Destination Flights
## <chr> <chr> <chr> <int>
## 1 Alaska On Time Los Angeles 497
## 2 Alaska On Time Phoenix 221
## 3 Alaska On Time San Diego 212
## 4 Alaska On Time San Francisco 503
## 5 Alaska On Time Seattle 1841
## 6 Alaska Delayed Los Angeles 62
## 7 Alaska Delayed Phoenix 12
## 8 Alaska Delayed San Diego 20
## 9 Alaska Delayed San Francisco 102
## 10 Alaska Delayed Seattle 305
## 11 AM West On Time Los Angeles 694
## 12 AM West On Time Phoenix 4840
## 13 AM West On Time San Diego 383
## 14 AM West On Time San Francisco 320
## 15 AM West On Time Seattle 201
## 16 AM West Delayed Los Angeles 117
## 17 AM West Delayed Phoenix 415
## 18 AM West Delayed San Diego 65
## 19 AM West Delayed San Francisco 129
## 20 AM West Delayed Seattle 61
overall_performance <- airline_long %>%
group_by(Airline, Arrival) %>%
summarise(
Flights = sum(Flights),
.groups = "drop"
) %>%
pivot_wider(
names_from = Arrival,
values_from = Flights
) %>%
mutate(
Total_Flights = `On Time` + Delayed,
Delay_Percentage = round(Delayed / Total_Flights * 100, 2),
On_Time_Percentage = round(`On Time` / Total_Flights * 100, 2)
)
print(overall_performance)## # A tibble: 2 × 6
## Airline Delayed `On Time` Total_Flights Delay_Percentage On_Time_Percentage
## <chr> <int> <int> <int> <dbl> <dbl>
## 1 AM West 787 6438 7225 10.9 89.1
## 2 Alaska 501 3274 3775 13.3 86.7
ggplot(
overall_performance,
aes(x = Airline, y = Delay_Percentage, fill = Airline)
) +
geom_col(width = 0.5) +
geom_text(
aes(label = paste0(Delay_Percentage, "%")),
vjust = -0.5
) +
labs(
title = "Overall Airline Delay Percentage",
x = "Airline",
y = "Delayed Flights (%)"
) +
ylim(0, 17) +
theme_minimal() +
theme(legend.position = "none")city_performance <- airline_long %>%
pivot_wider(
names_from = Arrival,
values_from = Flights
) %>%
mutate(
Total_Flights = `On Time` + Delayed,
Delay_Percentage = round(Delayed / Total_Flights * 100, 2),
On_Time_Percentage = round(`On Time` / Total_Flights * 100, 2)
) %>%
arrange(Destination, Airline)
print(city_performance)## # A tibble: 10 × 7
## Airline Destination `On Time` Delayed Total_Flights Delay_Percentage
## <chr> <chr> <int> <int> <int> <dbl>
## 1 AM West Los Angeles 694 117 811 14.4
## 2 Alaska Los Angeles 497 62 559 11.1
## 3 AM West Phoenix 4840 415 5255 7.9
## 4 Alaska Phoenix 221 12 233 5.15
## 5 AM West San Diego 383 65 448 14.5
## 6 Alaska San Diego 212 20 232 8.62
## 7 AM West San Francisco 320 129 449 28.7
## 8 Alaska San Francisco 503 102 605 16.9
## 9 AM West Seattle 201 61 262 23.3
## 10 Alaska Seattle 1841 305 2146 14.2
## # ℹ 1 more variable: On_Time_Percentage <dbl>
The number of flights operated by each airline differs significantly across destinations. This helps explain why the overall delay percentages differ from the city-by-city results.
ggplot(
city_performance,
aes(x = Destination, y = Total_Flights, fill = Airline)
) +
geom_col(position = "dodge") +
labs(
title = "Total Flights by Airline and Destination",
x = "Destination",
y = "Number of Flights",
fill = "Airline"
) +
theme_minimal()The following table shows the percentage of each airline’s total flights operated to each destination.
flight_distribution <- city_performance %>%
group_by(Airline) %>%
mutate(
Flight_Share = round(
Total_Flights / sum(Total_Flights) * 100, 2
)
) %>%
ungroup()
print(
flight_distribution %>%
select(Airline, Destination, Total_Flights, Flight_Share)
)## # A tibble: 10 × 4
## Airline Destination Total_Flights Flight_Share
## <chr> <chr> <int> <dbl>
## 1 AM West Los Angeles 811 11.2
## 2 Alaska Los Angeles 559 14.8
## 3 AM West Phoenix 5255 72.7
## 4 Alaska Phoenix 233 6.17
## 5 AM West San Diego 448 6.2
## 6 Alaska San Diego 232 6.15
## 7 AM West San Francisco 449 6.21
## 8 Alaska San Francisco 605 16.0
## 9 AM West Seattle 262 3.63
## 10 Alaska Seattle 2146 56.8
top_destinations <- flight_distribution %>%
group_by(Airline) %>%
slice_max(Flight_Share, n = 1, with_ties = FALSE) %>%
ungroup() %>%
mutate(
Label = paste(Airline, Destination, sep = " — ")
)
ggplot(
top_destinations,
aes(x = Flight_Share, y = Label, fill = Airline)
) +
geom_col(width = 0.5) +
geom_text(
aes(label = paste0(Flight_Share, "%")),
hjust = -0.2,
size = 5
) +
scale_x_continuous(limits = c(0, 100)) +
labs(
title = "Where Each Airline Operates Most of Its Flights",
x = "Percentage of Airline's Total Flights",
y = NULL
) +
theme_minimal() +
theme(
legend.position = "none",
plot.title = element_text(face = "bold", size = 14)
)delay_difference <- city_performance %>%
select(Airline, Destination, Delay_Percentage) %>%
pivot_wider(
names_from = Airline,
values_from = Delay_Percentage
) %>%
mutate(
Difference = `AM West` - Alaska
)
print(delay_difference)## # A tibble: 5 × 4
## Destination `AM West` Alaska Difference
## <chr> <dbl> <dbl> <dbl>
## 1 Los Angeles 14.4 11.1 3.34
## 2 Phoenix 7.9 5.15 2.75
## 3 San Diego 14.5 8.62 5.89
## 4 San Francisco 28.7 16.9 11.9
## 5 Seattle 23.3 14.2 9.07
ggplot(
delay_difference,
aes(x = Destination, y = Difference)
) +
geom_col(fill = "steelblue") +
labs(
title = "Alaska's Delay-Rate Advantage by Destination",
x = "Destination",
y = "Difference in Delay Rate (Percentage Points)"
) +
theme_minimal()AM West had an overall delay rate of 10.89%, compared with 13.27% for Alaska Airlines. Based on the overall percentages alone, AM West appeared to perform better.
When I compared the airlines separately across the five destinations, Alaska Airlines had a lower delay percentage in every city. The largest difference was in San Francisco, where Alaska had a delay rate of 16.86%, compared with 28.73% for AM West.
The difference between the overall and city-by-city comparisons is an example of Simpson’s paradox. Although Alaska Airlines had a lower delay percentage in every destination, AM West had a lower overall delay percentage.
The flight distribution analysis helps explain this reversal. Approximately 72.73% of AM West’s flights were to Phoenix, where both airlines had relatively low delay rates. In comparison, approximately 56.85% of Alaska’s flights were to Seattle, where delays were more common.
Because the two airlines operated different proportions of flights across destinations, the overall comparison was influenced by their destination distributions. This demonstrates why examining percentages within individual groups is important when interpreting aggregated data.
This analysis showed that AM West had a lower overall delay percentage, while Alaska Airlines had a lower delay percentage in every individual destination. This reversal illustrates Simpson’s paradox.
The results demonstrate that overall percentages can be misleading when groups have different distributions of observations. Comparing the airlines within each destination provided a more complete understanding of their performance.