Introduction

As usual, first, the libraries and the data are downloaded.

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggplot2)
url<-"https://raw.githubusercontent.com/Enrique01234/607-Fall-2026/refs/heads/main/Delays2.csv"

The raw data from GitHub are converted into a data frame.

Flights <- read_csv(file = url, show_col_types = FALSE, progress = FALSE)
Flights
## # A tibble: 4 × 7
##   Airlines Time    `Los Angeles` Phoenix `San Diego` `San Francisco` Seattle
##   <chr>    <chr>           <dbl>   <dbl>       <dbl>           <dbl>   <dbl>
## 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

This data frame is not tidy as the variable, which is the number of flights, is distributed in rows (for the airline and whether the flight is classified as on time or delayed) and columns (cities).

The pivot_longer function is used to tidy the data frame. A new data frame (called Pivoted) is created. Although it may not be needed to see these two data frames, it helped me to double-check the operations worked.

Pivoted <- Flights|>
  pivot_longer (!c(Airlines, Time), names_to = "City", values_to = "nF")
Pivoted
## # A tibble: 20 × 4
##    Airlines Time    City             nF
##    <chr>    <chr>   <chr>         <dbl>
##  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

Calculations

Initially, I estimated the different proportions in Excel. Although there is no need to create a new data frame, I wanted to make sure I kept my newly pivoted data frame intact (again, this step is not really needed, but as I was exploring different ways to group and select the observations, I wanted to be extra-cautious).

Pivoted2<- Pivoted

I started the calculations in R with the total flights by Airline.

Pivoted3<- Pivoted2|>
 group_by(Airlines)|>
 mutate(Total = sum(nF))

Then, I calculated the total flights by Airline and city. These sums are needed to calculate the percentages.

Pivoted4 <- Pivoted3|>
 group_by(Airlines, Time)|>
 mutate(Total_by_subgroup = sum(nF))

Once I had these new variables, I calculated the percentages for four subgroups of the data frame: Alaska flights On Time, Alaska flights Delayed, AM West flights On Time, and AM West flights Delayed.

These percentages are included in a new variable called “PropDelay_by_subgroup”. As both on time and delayed flights are included, it could have been called “PropDelay_or_ontime_by_subgroup” but it would have been too long. In order to be clear, this variable includes the proportion of delayed (and on time) flights for each airline. Thus, it has only two values per airline, which for each airline add up to 1.

 Pivoted4 <- Pivoted4 |>
 mutate(PropDelay_by_subgroup = Total_by_subgroup /Total)

Afterwards, I calculated, for each airline the total flights per city.

Pivoted4 <- Pivoted4 |>
 group_by(Airlines, City)|>
   mutate(Flights_by_city = sum(nF))

Then, in in one variable (“PropDelays_by_city_by_airline”), I calculated the percentage of on time and delayed flights per city for each airline

Pivoted4 <- Pivoted4 |>
 mutate(PropDelays_by_city_by_airline = nF/Flights_by_city)

This file, with all the calculated columns has been converted to a csv file. It has also been uploaded in GitHub

write.csv (Pivoted4,"Pivoted_4.csv")
read.csv ("Pivoted_4.csv")
##     X Airlines    Time          City   nF Total Total_by_subgroup
## 1   1   Alaska On Time   Los Angeles  497  3775              3274
## 2   2   Alaska On Time       Phoenix  221  3775              3274
## 3   3   Alaska On Time     San Diego  212  3775              3274
## 4   4   Alaska On Time San Francisco  503  3775              3274
## 5   5   Alaska On Time       Seattle 1841  3775              3274
## 6   6   Alaska Delayed   Los Angeles   62  3775               501
## 7   7   Alaska Delayed       Phoenix   12  3775               501
## 8   8   Alaska Delayed     San Diego   20  3775               501
## 9   9   Alaska Delayed San Francisco  102  3775               501
## 10 10   Alaska Delayed       Seattle  305  3775               501
## 11 11  AM West On Time   Los Angeles  694  7225              6438
## 12 12  AM West On Time       Phoenix 4840  7225              6438
## 13 13  AM West On Time     San Diego  383  7225              6438
## 14 14  AM West On Time San Francisco  320  7225              6438
## 15 15  AM West On Time       Seattle  201  7225              6438
## 16 16  AM West Delayed   Los Angeles  117  7225               787
## 17 17  AM West Delayed       Phoenix  415  7225               787
## 18 18  AM West Delayed     San Diego   65  7225               787
## 19 19  AM West Delayed San Francisco  129  7225               787
## 20 20  AM West Delayed       Seattle   61  7225               787
##    PropDelay_by_subgroup Flights_by_city PropDelays_by_city_by_airline
## 1              0.8672848             559                    0.88908766
## 2              0.8672848             233                    0.94849785
## 3              0.8672848             232                    0.91379310
## 4              0.8672848             605                    0.83140496
## 5              0.8672848            2146                    0.85787512
## 6              0.1327152             559                    0.11091234
## 7              0.1327152             233                    0.05150215
## 8              0.1327152             232                    0.08620690
## 9              0.1327152             605                    0.16859504
## 10             0.1327152            2146                    0.14212488
## 11             0.8910727             811                    0.85573366
## 12             0.8910727            5255                    0.92102759
## 13             0.8910727             448                    0.85491071
## 14             0.8910727             449                    0.71269488
## 15             0.8910727             262                    0.76717557
## 16             0.1089273             811                    0.14426634
## 17             0.1089273            5255                    0.07897241
## 18             0.1089273             448                    0.14508929
## 19             0.1089273             449                    0.28730512
## 20             0.1089273             262                    0.23282443

Results

The results are shown in a table. The packcge to create tables needs to be downloaded.

 install.packages("gt")
## Error in `contrib.url()`:
## ! trying to use CRAN without setting a mirror
library(gt)

As I had difficulties properly selecting the columns for the tables, I created a new data frame (Pivoted 5). It includes only the columns neeed for the analysis.

Pivoted5<- Pivoted4|>
  select(Airlines, Time, City, PropDelay_by_subgroup, PropDelays_by_city_by_airline)
Pivoted5|>
 gt()
Time PropDelay_by_subgroup PropDelays_by_city_by_airline
Alaska - Los Angeles
On Time 0.8672848 0.88908766
Delayed 0.1327152 0.11091234
Alaska - Phoenix
On Time 0.8672848 0.94849785
Delayed 0.1327152 0.05150215
Alaska - San Diego
On Time 0.8672848 0.91379310
Delayed 0.1327152 0.08620690
Alaska - San Francisco
On Time 0.8672848 0.83140496
Delayed 0.1327152 0.16859504
Alaska - Seattle
On Time 0.8672848 0.85787512
Delayed 0.1327152 0.14212488
AM West - Los Angeles
On Time 0.8910727 0.85573366
Delayed 0.1089273 0.14426634
AM West - Phoenix
On Time 0.8910727 0.92102759
Delayed 0.1089273 0.07897241
AM West - San Diego
On Time 0.8910727 0.85491071
Delayed 0.1089273 0.14508929
AM West - San Francisco
On Time 0.8910727 0.71269488
Delayed 0.1089273 0.28730512
AM West - Seattle
On Time 0.8910727 0.76717557
Delayed 0.1089273 0.23282443

In the first column of the table (PropDelay_by_subgroup), it can be seen that the percentage of delayed flights for Alaska (13.3%) is higher than for AM West (10.1%). This percentage applies to all the flights for each airline.

However, in the second column (PropDelays_by_city_by_airline), the percentage of delayed flights is taken out of the flights per city. In this case, for all five cities, the percentage of delayed flights is lower for Alaska than for AM West. For instance, for San Francisco, Alaska flights were delayed 16.7% of the time while AM West flights were delayed 28.7% of the time.

This discrepancy is an example of Simpson’s paradox. In the work I do, for example measuring poverty across countries but also for sub-national units like provinces/states within countries, we use an expression which captures a similar effect: “Averages hide disparities”.