Introducción

En el presente práctico se trabajará con librerías del conjunto tidyverse, las cuales permiten cargar, almacenar, filtrar, editar y visualizar datos de formato tabular. En particular, de las muchas librerías que engloba tidyverse nos focalizaremos en el uso de cuatro librerías clave: readr, dplyr, tidyr y ggplot2.

La modalidad será un práctico de tipo exposición, donde se irán discutiendo las operaciones realizadas. Se utilizarán dos sets de datos públicos obtenidos de (Data-Hub)[https://datahub.io/]. Los mismos son referentes a aeropuertos situados a lo largo del planeta.

Información rápida sobre las operaciones clásicas de estas librerías se encuentran en las llamadas cheatsheets. A continuación una lista con las mismas.

Varias cheatsheets se encuentran en esta página de RStudio

A su vez, se recomienda el libro R para Ciencia de Datos, escrito por el inventor de tidyverse (Hadley Wickham). En el mismo se expican de forma clara y concisa cómo utilizar la totalidad de estas herramientas.

Práctico

Leyendo datos con la librería readr

Es muy común que los archivos con los datos crudos que deseamos analizar se encuentren alojados en el disco duro en forma de tablas. Los mismos pueden estar en distintos formatos como CSV (comma-separated values), TSV (tab-separated values) u otros.

La librería readr posee funciones que permiten cargar estas tablas a R, produciendo automáticamente un objeto de clase tibble, específico de la librería tidyverse y diseñado para trabajar en ella. Las funciones de readr suelen llamarse read_*().

# cargo las librerias que utilizamos en el practico
library(magrittr)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.3     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.3     ✓ stringr 1.4.0
## ✓ readr   2.0.1     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x tidyr::extract()   masks magrittr::extract()
## x dplyr::filter()    masks stats::filter()
## x dplyr::lag()       masks stats::lag()
## x purrr::set_names() masks magrittr::set_names()
# cargo la tabla CSV
aeropuertos = readr::read_csv('airport-codes.csv')
## Rows: 55907 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (11): ident, type, name, continent, iso_country, iso_region, municipalit...
## dbl  (1): elevation_ft
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# la visualizo
aeropuertos
## # A tibble: 55,907 × 12
##    ident type          name  elevation_ft continent iso_country iso_region municipality
##    <chr> <chr>         <chr>        <dbl> <chr>     <chr>       <chr>      <chr>       
##  1 00A   heliport      Tota…           11 <NA>      US          US-PA      Bensalem    
##  2 00AA  small_airport Aero…         3435 <NA>      US          US-KS      Leoti       
##  3 00AK  small_airport Lowe…          450 <NA>      US          US-AK      Anchor Point
##  4 00AL  small_airport Epps…          820 <NA>      US          US-AL      Harvest     
##  5 00AR  closed        Newp…          237 <NA>      US          US-AR      Newport     
##  6 00AS  small_airport Fult…         1100 <NA>      US          US-OK      Alex        
##  7 00AZ  small_airport Cord…         3810 <NA>      US          US-AZ      Cordes      
##  8 00CA  small_airport Gold…         3038 <NA>      US          US-CA      Barstow     
##  9 00CL  small_airport Will…           87 <NA>      US          US-CA      Biggs       
## 10 00CN  heliport      Kitc…         3350 <NA>      US          US-CA      Pine Valley 
## # … with 55,897 more rows, and 4 more variables: gps_code <chr>,
## #   iata_code <chr>, local_code <chr>, coordinates <chr>

Realizando modificaciones con la librería tidyr

Es común que tengamos que realizar operaciones sobre las tablas que nos disponemos a analizar. Estas pueden poseer una disposición poco amigable, o poseer celdas donde exista más de un tipo de variable a la vez. Las funciones de la librería tidyr nos permiten llevar nuestros datos al formato deseado para análisis con tidyverse. En inglés se denomina a este tipo de tablas tidy data.

# realizo una modificacion con la libreria tidyr
aeropuertos %<>% tidyr::separate(data = ., col = 'coordinates', into = c('lon', 'lat'), sep = ', ')
aeropuertos$lat %<>% as.numeric()
aeropuertos$lon %<>% as.numeric()

# viasualizo el resultado
aeropuertos
## # A tibble: 55,907 × 13
##    ident type          name  elevation_ft continent iso_country iso_region municipality
##    <chr> <chr>         <chr>        <dbl> <chr>     <chr>       <chr>      <chr>       
##  1 00A   heliport      Tota…           11 <NA>      US          US-PA      Bensalem    
##  2 00AA  small_airport Aero…         3435 <NA>      US          US-KS      Leoti       
##  3 00AK  small_airport Lowe…          450 <NA>      US          US-AK      Anchor Point
##  4 00AL  small_airport Epps…          820 <NA>      US          US-AL      Harvest     
##  5 00AR  closed        Newp…          237 <NA>      US          US-AR      Newport     
##  6 00AS  small_airport Fult…         1100 <NA>      US          US-OK      Alex        
##  7 00AZ  small_airport Cord…         3810 <NA>      US          US-AZ      Cordes      
##  8 00CA  small_airport Gold…         3038 <NA>      US          US-CA      Barstow     
##  9 00CL  small_airport Will…           87 <NA>      US          US-CA      Biggs       
## 10 00CN  heliport      Kitc…         3350 <NA>      US          US-CA      Pine Valley 
## # … with 55,897 more rows, and 5 more variables: gps_code <chr>,
## #   iata_code <chr>, local_code <chr>, lon <dbl>, lat <dbl>

Aunque a simple vista no parezca, es posible que nuestra tabla posea datos faltantes en las columnas que posee. Podemos corroborar la existencia de datos faltantes utilizando la función filter(), de la librería dplyr. A su vez nos valdremos de la función is.na().

# visualizamos un caso en el cual existen NAs en nuestra tabla.
aeropuertos %>%
  filter(is.na(iso_country))
## # A tibble: 246 × 13
##    ident type   name  elevation_ft continent iso_country iso_region municipality
##    <chr> <chr>  <chr>        <dbl> <chr>     <chr>       <chr>      <chr>       
##  1 FYAA  small… Ai-A…         2000 AF        <NA>        NA-KU      Ai-Ais      
##  2 FYAB  small… Aroa…         3235 AF        <NA>        NA-KA      Aroab       
##  3 FYAK  small… Auss…          970 AF        <NA>        NA-KA      Aussenkehr  
##  4 FYAM  small… Amin…         4012 AF        <NA>        NA-OH      Aminuis     
##  5 FYAR  mediu… Aran…         1905 AF        <NA>        NA-ER      Arandis     
##  6 FYAS  small… Aus …         4856 AF        <NA>        NA-KA      Aus         
##  7 FYAV  small… Aria…         2549 AF        <NA>        NA-KA      Ariamsvley  
##  8 FYBC  small… Beth…         3260 AF        <NA>        NA-KA      Bethanien   
##  9 FYBJ  small… Bitt…         4167 AF        <NA>        NA-HA      Bitterwasser
## 10 FYEK  small… Epuk…         4892 AF        <NA>        NA-OH      Epukiro     
## # … with 236 more rows, and 5 more variables: gps_code <chr>, iata_code <chr>,
## #   local_code <chr>, lon <dbl>, lat <dbl>

Nuestro tibble posee algunos valores NAs en la columna continent. Nos deshacemos de ella utilizando la función select() de la librería dplyr.

Nos valdremos de otra tabla, country-and-continent-codes-list.csv, en la cual se correlacionan correctamente los aeropuertos y sus continentes. Esto nos servirá más adelante, ya que haremos algunos análisis categorizando en base a continentes.

Unimos estas tablas con la función left_join de la librería dplyr.

paises_vs_continentes = read_csv('country-and-continent-codes-list.csv') %>% dplyr::select(Continent_Name, Two_Letter_Country_Code)
## Rows: 262 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Continent_Name, Continent_Code, Country_Name, Two_Letter_Country_Co...
## dbl (1): Country_Number
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
aeropuertos %>%
 left_join(x = ., y = paises_vs_continentes, by = c('iso_country' = 'Two_Letter_Country_Code')) %>%
 dplyr::select(-continent) -> aeropuertos

aeropuertos
## # A tibble: 57,323 × 13
##    ident type   name   elevation_ft iso_country iso_region municipality gps_code
##    <chr> <chr>  <chr>         <dbl> <chr>       <chr>      <chr>        <chr>   
##  1 00A   helip… Total…           11 US          US-PA      Bensalem     00A     
##  2 00AA  small… Aero …         3435 US          US-KS      Leoti        00AA    
##  3 00AK  small… Lowel…          450 US          US-AK      Anchor Point 00AK    
##  4 00AL  small… Epps …          820 US          US-AL      Harvest      00AL    
##  5 00AR  closed Newpo…          237 US          US-AR      Newport      <NA>    
##  6 00AS  small… Fulto…         1100 US          US-OK      Alex         00AS    
##  7 00AZ  small… Corde…         3810 US          US-AZ      Cordes       00AZ    
##  8 00CA  small… Golds…         3038 US          US-CA      Barstow      00CA    
##  9 00CL  small… Willi…           87 US          US-CA      Biggs        00CL    
## 10 00CN  helip… Kitch…         3350 US          US-CA      Pine Valley  00CN    
## # … with 57,313 more rows, and 5 more variables: iata_code <chr>,
## #   local_code <chr>, lon <dbl>, lat <dbl>, Continent_Name <chr>

Obteniendo algunos valores descriptivos con funciones de la librería dplyr

Ahora que contamos con nuestra tabla completa, podemos obtener algunos valores descriptivos valiéndonos de las funciones group_by() y summarise()

Podríamos, por ejemplo, preguntarnos cuántos aeropuertos tenemos descritos por continente. Al realizar estas operaciones es necesario ser cuidadosos, ya que filas repetidas pueden distorsionar el estudio de las variables de interés.

# analizamos el numero de aeropuertos por continente
aeropuertos %>% 
  # seleccionamos columnas de interes
  dplyr::select(name, Continent_Name) %>% 
  # nos deshacemos de filas repetidas
  unique() %>% 
  # agrupamos segun valor en columna 'Continent_Name'
  group_by(Continent_Name) %>% 
  # obtenemos el resumen 
  summarise(numero_aeropuertos = n())
## # A tibble: 8 × 2
##   Continent_Name numero_aeropuertos
##   <chr>                       <int>
## 1 Africa                       3348
## 2 Antarctica                     31
## 3 Asia                         6046
## 4 Europe                       8582
## 5 North America               26344
## 6 Oceania                      3016
## 7 South America                7254
## 8 <NA>                            6
# analizaremos el tipo de aeropuertos que hay por continente
aeropuertos %>% 
  dplyr::select(name, Continent_Name, type) %>% 
  unique() %>% 
  # en este caso agrupamos segun dos columnas, 'Continent_Name' y 'type'
  group_by(Continent_Name, type) %>% 
  summarise(numero_aeropuertos = n())
## `summarise()` has grouped output by 'Continent_Name'. You can override using the `.groups` argument.
## # A tibble: 45 × 3
## # Groups:   Continent_Name [8]
##    Continent_Name type           numero_aeropuertos
##    <chr>          <chr>                       <int>
##  1 Africa         closed                         75
##  2 Africa         heliport                       10
##  3 Africa         large_airport                  48
##  4 Africa         medium_airport                447
##  5 Africa         seaplane_base                   1
##  6 Africa         small_airport                2773
##  7 Antarctica     closed                          2
##  8 Antarctica     medium_airport                  4
##  9 Antarctica     small_airport                  25
## 10 Asia           closed                        403
## # … with 35 more rows
# veremos ahora la altura promedio de los aeropuertos por continente
aeropuertos %>% 
  # nos deshacemos primero de aquellos aeropuertos para los que no tenemos datos
  filter(!is.na(elevation_ft)) %>%
  # seleccionamos columnas de interes
  dplyr::select(name, Continent_Name, iso_country, elevation_ft) %>% 
  unique() %>% 
  group_by(Continent_Name) %>% 
  summarise(numero_aeropuertos = mean(elevation_ft))
## # A tibble: 8 × 2
##   Continent_Name numero_aeropuertos
##   <chr>                       <dbl>
## 1 Africa                      2578.
## 2 Antarctica                  1069.
## 3 Asia                        1210.
## 4 Europe                       785.
## 5 North America               1209.
## 6 Oceania                      904.
## 7 South America               1317.
## 8 <NA>                        1841

Visualizando con la librería ggplot2

Visualizaremos ahora nuestro set de datos. Empezaremos graficando, tomando la variable lon como coordenada x de nuestro gráfico y la variable lat como coordenada y, especificando estos argumentos con la función aes() de la librería.

A su vez, debemos elegir una geometría que represente a nuestros puntos. En este caso, llamaremos a la función geom_point() para especificar que la misma será la geometría de puntos.

# tomamos los datos de aeropuerto
aeropuertos %>%
  # seteamos las variables a representarse en de los ejes x e y,
  ggplot(data = .,
         mapping = aes(x = lon, y = lat)) + 
  # seteamos el tipo de geometria  a ser usada ('point')
  geom_point()

# podemos a su vez colorear en funcion de la columna 'continente'
aeropuertos %>%
  ggplot(data = .,
         mapping = aes(x = lon, y = lat, color = Continent_Name)) + 
  geom_point()

# nos focalizaremos en America del Sur, coloreando segun pais
aeropuertos %>%
  dplyr::filter(., Continent_Name == 'South America') %>%
  ggplot(data = .,
         mapping = aes(x = lon, y = lat, color = iso_country)) + 
  geom_point()

# Filtraremos en base a un pais especifico, Australia (codigo AU). Coloreamos segun tipo de aeropuerto
aeropuertos %>% 
  filter(iso_country == 'AU') %>% 
  ggplot(data = ., mapping = aes(x = lon, y = lat, color = type)) + geom_point()

# Observamos como se distribuye el tipo de aeropuertos en America del Sur.
aeropuertos %>% 
  filter(Continent_Name == 'South America') %>% 
  ggplot(data = ., mapping = aes(x = lon, y = lat, color = type)) + geom_point()

A su vez podriamos realizar un boxplot para visualizar la dispersión que posee cada continente en torno a esta variable. Para ello podemos utilizar la función geom_boxplot() para visualizar nuestras variables con esa geometría.

aeropuertos %>%
  dplyr::filter(!is.na(elevation_ft)) %>%
  ggplot(data = .,
         mapping = aes(x = Continent_Name, y = elevation_ft, color = Continent_Name)) + 
  # en este caso utilizamos una geometria de tipo 'boxplot'
  geom_boxplot()

Podemos a su vez subdividir nuestro analisis en base a continentes (o cualquier factor). Lo haremos con la función facet_wrap().

aeropuertos %>%
  dplyr::filter(!is.na(elevation_ft)) %>%
  ggplot(data = .,
         mapping = aes(x = Continent_Name, y = elevation_ft, color = Continent_Name)) + 
  geom_boxplot() +
  # realizamos una subdivision 'en funcion' (~) de la columna 'type'
  facet_wrap(~type)

Un poco más de información

En la mayoría de los casos los datos con los que se trabaja suelen ser importados de archivos alojados en disco duro.

Para este propósito se utiliza la librería readr. Las funciones de la misma son capaces de leer archivos de varios formatos: CSV (comma-separated values), TSV (tab-separated > alues) y otros.

Las funciones alojadas en esta librería comienzan todas con el prefijo read_*, acompañadas del formato de texto que son capaces de leer. Así, por ejemplo, read_csv() es la función de esta librería diseñada para cargar archivos de texto con formato csv.

En general estas funciones también poseen una sintaxis similar, por lo que al aprenderse a utilizar una ya se posee el conocimiento para lograr cargar otros formatos.

La librería tidyverse fue construída para trabajar sobre conjuntos de datos que poseen un formato en particular: estos datos son llamados tidy data.

Para que un set sea caracterizado como tidy data debe poseer tres características que están interrelacionadas:

• Cada variable debe estar representada en una columna

• Cada observación debe estar representada en una fila

• Cada valor debe estar alojado en una celda

Esto no siempre se cumple en la vida cotidiana. Los datos crudos con los que solemos presentarnos suelen tener uno (o los dos) siguientes defectos que nos > impiden definirlos como tidy datasets:

• Una variable puede estar siendo representada en más de una columna

• Una observación puede estar representada en más de una fila

Estos problemas pueden ser resueltos con las funciones de la librería tidyr. Como su nombre indica, esta librería tiene como objetivo generar tidy datasets > en R.

Sus funciones más importantes son

pivot_longer(): hace a un dataset más largo, alojando valores que antes se encontraban en columnas en filas.

pivot_wider(): lo opuesto de pibot_longer(). Un dataset se hace más ancho al aumentar el número de columnas y disminuir el número de filas.

separate(): separa observaciones de una columna en varias columnas, al separar los valores de sus celdas según un separador.

unite(): lo opuesto de separate(). Se unen observaciones de diferentes columnas, utilizando para ello un caracter especificado.

Cinco de las funciones de la librería dplyr permiten hacer un conjunto de operaciones que, en su conjunto, logran resolver la mayoría de los problemas relacionados con la manipulación de datos en un tidy dataset.

Estas funciones son

filter(): filtra un tibble teniendo en cuenta condiciones lógicas que operan sobre sus columnas.

arrange(): ordena un tibble en base a los valores de una o más columnas

select(): selecciona/descarta columnas de un tibble.

mutate(): genera nuevas columnas en un tibble en base a operaciones ejecutadas sobre columnas preexistentes

summarise(): resume datos de un tibble realizando operaciones sobre el mismo (e.g. contar un número de columnas -función n()-, o sacar un promedio -función mean()-). Suele utilizarse junto con la función group_by, la cual permite subdividir a un tibble en función de los valores de una o más columnas.

La librería ggplot2 nos permite realizar