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