Primera Parte

Analizamos un poco los datos del dataset de ANAC

Los datos originales utilizados se encuentran en el siguiente Link, los cuales corresponden a los datos de Aterrizajes y despegues en el año 2021 procesados por ANAC.

Leamos y analisemos que tiene este csv.

# datos.ANAC := datos del csv original de aterrizajes y vuelos
datos.ANAC <- read.csv('./csv/202109-informe-ministerio.csv', sep=';', encoding = 'UTF-8')
datos.ANAC
colnames(datos.ANAC)
##  [1] "Fecha"                             "Hora.UTC"                         
##  [3] "Clase.de.Vuelo..todos.los.vuelos." "Clasificación.Vuelo"              
##  [5] "Tipo.de.Movimiento"                "Aeropuerto"                       
##  [7] "Origen...Destino"                  "Aerolinea.Nombre"                 
##  [9] "Aeronave"                          "Pasajeros"                        
## [11] "Calidad.dato"

En este data.set tenemos \(11\) columnas Referidas a distintos vuelos, tanto internacionales como nacionales.

Quedemonos con los datos de los vuelos nacionales, esto es en los cuales la clasificacion de vuelos es Dom. Para esto nos apollaremos en la libreria Tidyverse

require(tidyverse)
## Loading required package: tidyverse
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5     v purrr   0.3.4
## v tibble  3.1.3     v dplyr   1.0.7
## v tidyr   1.1.3     v stringr 1.4.0
## v readr   2.0.0     v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
# Modificamos un poco los nombres por comodidad

datos.ANAC <- datos.ANAC %>% 
  rename(
    Clase_Vuelo = Clase.de.Vuelo..todos.los.vuelos., 
  )
colnames(datos.ANAC)
##  [1] "Fecha"               "Hora.UTC"            "Clase_Vuelo"        
##  [4] "Clasificación.Vuelo" "Tipo.de.Movimiento"  "Aeropuerto"         
##  [7] "Origen...Destino"    "Aerolinea.Nombre"    "Aeronave"           
## [10] "Pasajeros"           "Calidad.dato"
datos.ANAC

Ahora si, busquemos todos los vuelos dentro de argentina.

# datos.vuelos.nacionales := datos del csv de ANAC correspondiente a los vuelos nacionales


datos.vuelos.nacionales <- datos.ANAC %>% 
  filter( 
    Clasificación.Vuelo == "Dom", 
    Clase_Vuelo == "Regular"
    )

datos.vuelos.nacionales

Una de las cosas que podemos notar, es que El Tipo de Movimiento se vincula con un Aeropuerto (dato en IATA) al mismo tiempo este se vincula con un dato de un Aeropuerto tambien en IATA en la Columna de Origen...Destino, esto debe interpretarse como:

El avión despega de el Aeropuerto \(x\) con Destino \(y\)

o bien

El avión aterriza Al Aeropuerto \(x\) con Origen \(y\)

Esto es algo que debemos tener en cuenta más adelante para poder organizar bien los datos.

Entonces, teniendo esto en cuenta, creemos dentro de este data.set dos columnas nuevas una señalando cual es el origen y el destino del vuelo, y asi ya tener bien identificado cual sera el viaje recorrido por el avion.

datos.vuelos.nacionales <-datos.vuelos.nacionales %>% 
  mutate(
    Origen  = if_else(Tipo.de.Movimiento=='Despegue',Aeropuerto,Origen...Destino),
    Destino = if_else(Tipo.de.Movimiento=='Despegue',Origen...Destino,Aeropuerto)
    )


datos.vuelos.nacionales

Scrapeo de datos de coordenadas de wikipedia

En la consigna del tp, nos dejan el siguiente Link. Este tiene una entrada de wikipedia el cual posee una tabla con datos de aeropuertos nacionales.

library(httr)
library(rvest)
## 
## Attaching package: 'rvest'
## The following object is masked from 'package:readr':
## 
##     guess_encoding

pagina.wiki <- read_html('https://en.wikipedia.org/wiki/List_of_airports_in_Argentina')

elemento_tabla <- html_element(pagina.wiki,'.wikitable')
datos.wiki     <- html_table(elemento_tabla)

# datos.wiki := datos correspondientes a la tabla de wikipedia (sin ningun filtro)
datos.wiki

Nosotros necesitamos los datos de IATA en lugar de los de ICAO, pues, en el dataset en ANAC los datos de los Aeropuertos utilizan los codigos en IATA.

Ahora, podemos ver directamente en la tabla de wikipedia, que hay muchos aeropuertos que no estan identificados mediante este codigo por lo que necesitamos filtrarlos

codigos <- unique(datos.wiki$IATA)
codigos <- codigos[codigos!=""] #elimino el string vacio

length(codigos)
## [1] 105
codigos
##   [1] "ARR" "BHI" "BRC" "AEP" "EPA" "EZE" "CPG" "CTC" "CCT" "CVH" "CRR" "CNT"
##  [13] "HOS" "CLX" "CRD" "COC" "COR" "CSZ" "CNQ" "CUT" "UZU" "EHL" "FTE" "ING"
##  [25] "EMX" "ELO" "EQS" "FMA" "GPO" "GNR" "VGS" "GGS" "OYA" "GHU" "IGB" "JSM"
##  [37] "JUJ" "JNI" "LCM" "LPG" "IRJ" "LHS" "LLS" "LCP" "LMD" "LGS" "MQD" "MDQ"
##  [49] "MDZ" "MDX" "RLO" "MJR" "MCS" "NEC" "NQN" "OVR" "ORA" "PRA" "AOL" "PEH"
##  [61] "PMQ" "PSS" "PRQ" "PUD" "IGR" "PMY" "ULA" "RZA" "RAF" "RCQ" "RES" "RDS"
##  [73] "RCU" "RGL" "RGA" "ROY" "RYO" "ROS" "SLA" "OES" "UAQ" "LUQ" "CPC" "AFA"
##  [85] "SFN" "RSA" "SST" "SDE" "OLN" "SGV" "NCJ" "TDL" "TTG" "RHD" "REL" "OYO"
##  [97] "TUC" "USH" "VCF" "VDM" "VDR" "VLG" "VMR" "VME" "APZ"

Otro dato que necesitaremos limpiar es el dato de las coordenadas. Cada coordenada en la tabla de datos.wiki esta como un objeto string, el problema es que renemos los mismos datos en formatos diferentes y solo nos interesa quedarnos con los datos que son puramente numericos, pues, luego queremos utilizarlos para calcular la distancia entre Aeropuertos.

Tratemos de filtrar los datos de coordenadas y separar latitudes y longitudes.

# Inicializamos algunas variables
latitud <- c()
longitud <- c()
primer.filtro  <- c()
segundo.filtro <- c()
coordenadas.filtradas <- c()


# Tomamos la columna de coordenadas desde la tabla de wikipedia
coordenadas <- datos.wiki$Coordinates


# Separamos las coordenadas mediante el simbolo '/'
# Esto nos vividira el string en una lista 
primer.filtro <- strsplit(coordenadas, split='/')


# Recorremos cada coordenada, nos quedamos con los datos en formato numerico
# Creando un vector que tendra en primer lugar las latitudes y en el segundo 
# Las longitudes
for( i in (1:length(coordenadas)) ) {
  
  segundo.filtro[i]        <- gsub( '[^0-9,;,.,-]', '', primer.filtro[[i]][3] )
  coordenadas.filtradas[i] <- strsplit( segundo.filtro[i], ';' )

  }


# Del vector de coordenadas, creamos vectores de latitud y longitud por separado
for( i in (1:length(coordenadas.filtradas))) {
  
  latitud  <- c( latitud, coordenadas.filtradas[[i]][1])
  longitud <- c(longitud, coordenadas.filtradas[[i]][2])

  }


# Agregamos cols con latitud y longitud a los datos de wikipedia
datos.wiki <- datos.wiki %>% 
  mutate(
    latitud=latitud, 
    longitud=longitud
  ) %>%
  filter(
    IATA != ''
    ) %>%
  select( 
    `City served`, 
    Province, 
    IATA, 
    `Airport name`, 
    latitud, longitud
    ) %>%
  rename(
    ciudad = `City served`,
    provincia = Province,
    Aeropuerto = `Airport name`
  )


datos.wiki

Bueno, ahora tenemos los datos de la entrada de wikipedia, ya filtrados y listos para vincularlos con los datos emitidos por ANAC.

Vinculación de datasets

Lo primero que vamos a hacer acá es terminar de filtrar bien. Esto es, tomando los datos de ANAC y filtrando segun los codigos IATA que si tenemos de los datos de wikipedia.

En particlar, tambien nos quedaremos con los datos de calidad DEFINITIVO, teniendo en cuenta que los que estan como PROVISORIO quizas esten sometidos a actualizaciones y por tanto no son del todo confiables.

datos.vuelos.nacionales <- datos.vuelos.nacionales %>% 
  filter( 
    Clasificación.Vuelo %in% "Dom" & 
    Calidad.dato        %in% "DEFINITIVO"    &
    Origen              %in% codigos &
    Destino             %in% codigos
  )

datos.vuelos.nacionales

Acá se nos plantea un problema… ¿Como es que inicialmente teniamos \(63597\) vuelo y ahora solo \(2712\)?

Bueno, aca tuvimos un pequeño problema con los codigos… .

Analisemos que codigos tiene cada uno de los datasets, esto es, los codigos en la pagina de wikipedia y en el csv de ANAC

codigos.ANAC <- datos.ANAC %>% select(Aeropuerto)
codigos.wiki <- datos.wiki %>% select(IATA)

table(codigos.ANAC)
## codigos.ANAC
##   AER   BAR   BCA   CAT   CBA   CHP   CRR   CRV   DIA   DIL   DOZ   DRY   ECA 
## 23083  7161  2122   982  6524  1903  3191  4633   566    91  7982  1676  1913 
##   ESQ   EZE   FDO   FSA   GAL   GOY   GPI   GRA   IGU   JUA   JUJ   LAR   MDP 
##   791 22604 35395   511  2546    73     2   529  2212  1509  1720    15  2774 
##   MLG   MOR   NEU   OSA   PAL   PAR   POS   ROS   RTA   RYD   SAL   SDE   SIS 
##   791 37031  8018  1062  2331  2409  3973  3567  2168  1880  4823  1944  1722 
##   SRA   SVO   TRC   TRE   TUC   UIS   USU   VIE 
##  3734  1542  1031  2015  3256   486  2675   837

codigos.ANAC <- names(table(codigos.ANAC))
cat("\n ------------ \n\n", codigos.ANAC ,"\n", length(codigos.ANAC),"\n ------------ \n\n")
## 
##  ------------ 
## 
##  AER BAR BCA CAT CBA CHP CRR CRV DIA DIL DOZ DRY ECA ESQ EZE FDO FSA GAL GOY GPI GRA IGU JUA JUJ LAR MDP MLG MOR NEU OSA PAL PAR POS ROS RTA RYD SAL SDE SIS SRA SVO TRC TRE TUC UIS USU VIE 
##  47 
##  ------------




codigos.wiki <- names(table(codigos.wiki))
cat("\n ------------ \n\n", codigos.wiki, "\n ------------ \n\n")
## 
##  ------------ 
## 
##  AEP AFA AOL APZ ARR BHI BRC CCT CLX CNQ CNT COC COR CPC CPG CRD CRR CSZ CTC CUT CVH EHL ELO EMX EPA EQS EZE FMA FTE GGS GHU GNR GPO HOS IGB IGR ING IRJ JNI JSM JUJ LCM LCP LGS LHS LLS LMD LPG LUQ MCS MDQ MDX MDZ MJR MQD NCJ NEC NQN OES OLN ORA OVR OYA OYO PEH PMQ PMY PRA PRQ PSS PUD RAF RCQ RCU RDS REL RES RGA RGL RHD RLO ROS ROY RSA RYO RZA SDE SFN SGV SLA SST TDL TTG TUC UAQ ULA USH UZU VCF VDM VDR VGS VLG VME VMR 
##  ------------
cat("\n ------------ \n\n", length(codigos.wiki), "\n ------------ \n\n")
## 
##  ------------ 
## 
##  105 
##  ------------

Veamos cuales coinciden.


# Funcion burbuja := realiza un "burbujeo" entre vectores.
#   Esta, me permitira comparar los elementos coincidentes entre estos dos vectores.
burbuja <- function(vec.prim, vec.sec){
    coinciden <- c()
    
    for (i in (1:length(vec.prim))){
      for (j in (1:length(vec.sec))){
        if (vec.sec[j] == vec.prim[i]) {
          coinciden <- c(coinciden, vec.prim[i])
        }
      }
    }
    return(coinciden) 
  } 

burbuja(codigos.wiki, codigos.ANAC)
## [1] "CRR" "EZE" "JUJ" "ROS" "SDE" "TUC"

Vemos que solo coinciden 6 codigos, es decir, a lo que necesitamos no sirve… ¿Porque razón sera?

La razón es clara, y es el hecho de que los codigos en el dataset del ANAC no estan en IATA sino, que estan justamente segun el ANAC, una lista de estos se pueden ver Acá

Por suerte, nos dieron otro dataset, los cuales tienen varios formatos de codigos y una de las columnas justamente son codigos ANAC para algunos de los aeropuertos.

Nota: Sabemos que el algoritmo del ordenamiento burbuja no es lo más efectivo, pues tiene un crecimiento logaritmico, no uno lineal, pero en este caso puntual sabiamos que iba a funcionar bien.

Carga del nuevo dataset

Ahora, lo que quedaria es unir los datasets de wikipedia y de ANAC y reinterpretar un poco los datos para así luego poder calcular las distancias y las velocidades medias de cada vuelo.

data.nueva <- read.csv("./csv/sna_abril_2021_fixed_encoding.csv", encoding = 'UTF-8')
data.nueva

Usando un bucle como el anterior, vamos a comparar que codigo de este nuevo dataset es el que coincide con los datos de ANAC

Solo compararemos los de la columna ita y ana, pues los otros codigos a simple vista son muy distintos.

codigos.ita <- names(table(data.nueva %>% select(ita)))
codigos.ana <- names(table(data.nueva %>% select(ana)))

cat("------------------------------", 
    "\n Comparación ita - ANAC \n", 
    "------------------------------\n", 
    burbuja(codigos.ANAC, codigos.ita ))
## ------------------------------ 
##  Comparación ita - ANAC 
##  ------------------------------
##  EZE FDO JUJ ROS SDE TUC

cat("\n------------------------------", 
    "\n Comparación ana - ANAC \n", 
    "------------------------------\n", 
    burbuja(codigos.ana, codigos.ANAC))
## 
## ------------------------------ 
##  Comparación ana - ANAC 
##  ------------------------------
##  AER BAR BCA CAT CBA CHP CRR CRV DIA DIL DOZ DRY ECA ESQ EZE FDO FSA GAL GPI GRA IGU JUA JUJ LAR MDP MLG NEU OSA PAL PAR POS ROS RTA RYD SAL SDE SIS SRA SVO TRC TRE TUC UIS USU VIE

Listo, sabemos que la lista de codigos que debemos utilizar para comparar es la columna ana.

Tomemos de este dataset los datos que nos sean de utilidad, para luego mergear ambos datasets.

datos.aeropuertos.nacionales <- data.nueva %>%
  select(
    cpr, nam, fna, ana, x, y
  ) %>%
  rename(
    provincia = cpr,
    ciudad = nam,
    aeropuerto = fna,
    codigo.ANAC = ana,
    longitud = x,
    latitud = y
  )


datos.aeropuertos.nacionales

Ahora si, hagamos una union de datos entre los dataset de ANAC y los datos de aeropuertos.

new.df.vuelos.nacionales <- datos.ANAC %>% 
  filter(
    Calidad.dato == "DEFINITIVO",
    Aerolinea.Nombre != "0",
    Clasificación.Vuelo == "Dom", 
    Clase_Vuelo == "Regular"
    ) %>% 
  mutate(
    Origen = if_else(Tipo.de.Movimiento=='Despegue',Aeropuerto,Origen...Destino),
    Destino = if_else(Tipo.de.Movimiento=='Despegue',Origen...Destino,Aeropuerto)
    ) %>%
  filter(  
          Origen  %in% codigos.ana &
          Destino %in% codigos.ana
          ) 

new.df.vuelos.nacionales

Esto tiene otra cara, ahora tenemos \(44.985\) datos para analizar.

# Unimos los datasets utilizando la funcion merge 
# Utililizando los datos del Origen
df_origen <- merge( x=new.df.vuelos.nacionales,         
                    y=datos.aeropuertos.nacionales, 
                    by.x='Origen', by.y='codigo.ANAC')

# Utilizando los datos del Destino
df_destino <- merge(x=new.df.vuelos.nacionales, 
                    y=datos.aeropuertos.nacionales,
                    by.x='Destino', by.y='codigo.ANAC')

#ordeno a partir de fecha y hora (y si coinciden a partir del modelo de avion)
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
  arrange( Fecha,
           Hora.UTC,
           Aeronave
           )

# Realizamos el mismo ordenamiento con los datos mergeados
df_destino <- df_destino %>% arrange( Fecha, Hora.UTC, Aeronave )
df_origen  <- df_origen  %>% arrange( Fecha, Hora.UTC, Aeronave )

#agrego las columnas a los datos filtrados
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>% 
  mutate(
    ciudad.origen = df_origen$ciudad,
    ciudad.destino = df_destino$ciudad,
    provincia.origen = df_origen$provincia,
    provincia.destino = df_destino$provincia,
    lat.origen = df_origen$latitud,
    lat.destino = df_destino$latitud,
    long.origen = df_origen$longitud,
    long.destino = df_destino$longitud
  )


new.df.vuelos.nacionales

Obtuvimos asi, un dataset que tiene las las coordenadas de origenes y destinos, ciudades de origen y destino y provincias.

Podemos notar tambien, que hay columnas que ya no son de utilidad. Por lo que crearemos teniendo en cuenta que:

  • Calidad de datos : ya no es nesesaria
  • Aeropuerto : sera reemplazado por el dato de Origen
  • Origen...Destino : Se reemplazara por el Destino
  • Latitudes/origenes/pasajeros : Haremos la conversión de string a double
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
  select( 
    Fecha, Hora.UTC, Pasajeros, Clase_Vuelo, 
    Tipo.de.Movimiento, Aerolinea.Nombre, 
    Aeronave, Origen, Destino, 
    ciudad.origen, ciudad.destino, 
    provincia.origen, provincia.destino, 
    lat.origen, long.origen, lat.destino, long.destino, 
    ) %>%
  mutate(
    Pasajeros = as.double(Pasajeros),
    lat.origen = as.double(lat.origen), 
    long.origen = as.double(long.origen), 
    lat.destino = as.double(lat.destino), 
    long.destino = as.double(long.destino),
    ) %>%
  rename(
    Aerolinea = Aerolinea.Nombre
  )
  

new.df.vuelos.nacionales

Calculo de distancias

Para el calcular las distancias utilizaremos la libreria geosphere, la cual tiene una seriae de herramientas muy utiles para hacer calculos con trigonometria esferica.

Esta tiene una funcion llamada disHavestine (Cuyos detalles se pueden ver Acá), la cual nos permitira calcular las distancias a partir de los datos de las latitudes y las longitudes.

Esta función toma dos parametros inicialmente, los cuales son dos vectores cada uno tiene las longitudes y latitudes, “desde-hasta” para asi calcular la distancia.

require(geosphere)
## Loading required package: geosphere
# Iniciamos algunas variables
distancias <- c()
latitudes.origenes  <- new.df.vuelos.nacionales$lat.origen
longitudes.origenes <- new.df.vuelos.nacionales$long.origen
latitudes.destino   <- new.df.vuelos.nacionales$lat.destino
longitudes.destino  <- new.df.vuelos.nacionales$long.destino

# Calculamos las distancias utilizando disHaversine
# Pasandolas a Kilometros
for (i in 1:44985){
  distancias <- c( distancias, 
                   (distHaversine(
                     c(latitudes.origenes[i],longitudes.origenes[i]),
                     c(latitudes.destino[i],longitudes.destino[i]))
                   )/1000)
}

distancias[1:3]
## [1] 1388.9893 1655.5274  929.2051
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
  mutate(distancia.km = distancias)

new.df.vuelos.nacionales

Ya tenemos las distancias en Kilometros, calculadas en base a las coordenadas.

Calculo de las Velocidades promedio

Para poder calcular las velocidades es necesario tener los vuelos completos, es decir, la hora de despegue para elvuelo y su hora de aterrizaje. Claramente en el dataset no hay un identificativo de cada vuelo, lo cual seria de mucha utilidad y el calculo seria una aproximación un tanto mas precisa, pero no es el caso.

Para poder calcular las velocidades, necesitamos tener el tiempo en el cual sale o llega el avion en un formato que nos permita calcular una diferecia temporal y que sea luego utilizable a la hora del calculo. Para esto, vamos a usar la función strptime, esta funcion crea un horario segun un formato determinado que nosotros le digamos.

#Agrego nueva columna a los datos con informacion temporal completa

# Primero "pegamos" la Fecha del vuelo con su horario
horario <- paste( new.df.vuelos.nacionales$Fecha, new.df.vuelos.nacionales$Hora.UTC, sep=' ')
# Utilizamos la funcion strptime para formatear este dato
horario <- strptime(horario, format="%d/%m/%Y %H:%M")

# Agregamos lo horarios a la columna Horario del dataframe.
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>% 
  mutate(
    Horario = horario
    )

# Tomo Solo Aterrizajes
aterrizajes <- new.df.vuelos.nacionales %>% 
  filter(
    distancia.km > 0 & Tipo.de.Movimiento=='Aterrizaje'
    ) %>% 
  select(
    Horario, 
    Origen, 
    Destino, 
    Tipo.de.Movimiento, 
    Aeronave,
    Pasajeros, 
    distancia.km, 
    lat.destino,
    long.destino
    )

# Tomo Solo Despegues
despegues <- new.df.vuelos.nacionales %>% 
  filter(
    distancia.km > 0 & Tipo.de.Movimiento=='Despegue'
    ) %>% 
  select(
    Horario, 
    Origen, 
    Destino, 
    Tipo.de.Movimiento, 
    Aeronave,
    Pasajeros, 
    distancia.km,
    lat.origen,
    long.origen
    )

#Busco interseccion entre despegues y aterrizajes a partir de las ciudades origen/destino y el modelo de avion
interseccion <- merge( x=despegues, y=aterrizajes, 
                   by=c('Origen','Destino','Aeronave')
                   )
dim(interseccion)
## [1] 1211046      15

#Me quedo con los vuelos que hayan volado un rango de tiempo razonable
interseccion <- interseccion %>%
  mutate(
    tiempo.vuelo = as.double(difftime( Horario.y, Horario.x, units="hours"))
    )  %>% 
  filter(
    tiempo.vuelo > 0 & tiempo.vuelo < 5 
    ) 


dim(interseccion)
## [1] 21427    16

Ya teniendo las distancias y los tiempos de vuelo, podemos pasar a calcular las velocidades promedio, en este caso de cada vuelo.

interseccion <- interseccion %>% mutate(Velocidad.kmhs = distancia.km.x / tiempo.vuelo) 
interseccion

Y exportaremos este ultimo como csv para poder trabajar a futuro con este dataset.

write.csv(interseccion, file = "vuelos_2021.csv", row.names = FALSE)

Glosario de Variables