4. Tibbles y tidyverse

4.1. De data frames a tibbles

Un tibble es una versión moderna del data.frame, es decir, es una tabla. Mantiene la organización en filas y columnas, pero ofrece una impresión más clara y un comportamiento más consistente al seleccionar y modificar columnas.

Un tibble sigue siendo un tipo de data.frame, por lo que puede utilizarse con la mayoría de las funciones que trabajan con tablas.

Algunas de las ventajas de tibble frente a data frames son:
- muestran solo las primeras filas y las columnas que caben en pantalla;
- indican el tipo de dato almacenado en cada columna;
- da las dimensiones de la tabla;
- se integran de forma coherente con los paquetes del tidyverse;
- evitan algunos comportamientos automáticos de los data.frame;
- suele generar mensajes de error más informativos.

Se puede crear mediante la función tibble() o convertirse desde otro objeto mediante as_tibble().

library(tidyverse)
datos_df <- data.frame(x = 1:3, y = c("a", "b", "c"))
datos_df
##   x y
## 1 1 a
## 2 2 b
## 3 3 c
datos_tb <- tibble(x = 1:3, y = c("a", "b", "c"))
datos_tb
## # A tibble: 3 × 2
##       x y    
##   <int> <chr>
## 1     1 a    
## 2     2 b    
## 3     3 c
datos_tb2 <- as_tibble(datos_df)
datos_tb2
## # A tibble: 3 × 2
##       x y    
##   <int> <chr>
## 1     1 a    
## 2     2 b    
## 3     3 c

4.2. ¿Qué es el tidyverse?

El tidyverse es un ecosistema de paquetes para ciencia de datos que comparten una filosofía de diseño, estructuras de datos y sintaxis coherentes, que permiten preparar, procesar y graficar bases de datos.

El paquete tidyverse funciona como un metapaquete está compuesto en la actualidad por 9 paquetes:

Este entorno de paquetes sustituye cómo se trabaja (crean variables nuevas, se hacen filtros, etc.) respecto a R base. Es importante pensar que aunque son formas distintas de escribir no son excluyentes, sino complementarias, y que a veces nos resultará mejor utilizar una u otra.

4.3. Datos ordenados o tidy data

Existen muchas maneras de organizar los datos en una tabla. Los datos ordenados o tidy data siguen una estructura regular y lógica para ordenar datos en una tabla. Un conjunto de datos es tidy cuando:
- cada variable ocupa una columna;
- cada observación ocupa una fila;
- cada valor ocupa una celda.

Ejemplo: Vamos al campo y medimos las condiciones fisiológicas de peso de los pollos de nuestra zona de estudio a lo largo de distintos meses en una temporada de muestreo. ¿Cómo podemos organizar los datos?.

  • El formato ancho (wide), los valores de una misma variable pueden estar distribuidos entre varias columnas. Esto es frecuente cuando se realizan varias mediciones sobre la misma unidad de observación.

    Individuo peso_abril peso_mayo peso_junio
    s2026_p1 15 23 21
    s2026_p2 13 21 25
    s2026_p3 18 20 23
    s2026_p4 16 25 26
  • El formato largo (long) suele facilitar que los datos cumplan los principios de tidy data, pero una tabla en formato long no es necesariamente tidy si no respeta las tres reglas anteriormente mencionadas.

    Individuo Mes Peso
    s2026_p1 Abril 15
    s2026_p1 Mayo 23
    s2026_p1 Junio 21
    s2026_p2 Abril 13
    s2026_p2 Mayo 21
    s2026_p2 Junio 25
    s2026_p3 Abril 18
    s2026_p3 Mayo 20
    s2026_p3 Junio 23
    s2026_p4 Abril 16
    s2026_p4 Mayo 25
    s2026_p4 Junio 26
TipVentajas del formato tidy

El formato tidy puede ser más largo de escribir manualmente y ocupar más filas, pero proporciona una estructura regular inequívoca: cada variable ocupa una columna, cada observación una fila y cada valor una celda. Esta estructura facilita la manipulación, la representación gráfica y el análisis de datos.

“Tidy datasets are all alike but every messy dataset is messy in its own way.” – Hadley Wickham

¿Qué pasaría si queremos añadir otra variable, por ejemplo longitud del ala cada mes? En el formato largo añadiríamos una sola columna, longitud_ala, mientras que en el formato ancho necesitaríamos tres columnas, una por mes (lon_abr, lon_may, lon_jun).

4.4. El operador pipe

Una pipe (tubería) es un separador especial que permite encadenar varias operaciones: toma el objeto a su izquierda como primer argumento de la función a su derecha. Esto permite escribir una secuencia de operaciones en el mismo orden en que se realizan y permite escribir el código de manera más limpia y natural.

El operador |> está disponible a partir de la versión R 4.1 y no requiere cargar ningún paquete.

El operador %>% pertenece al paquete magrittr y tiene la misma funcionalidad. Este operador se carga al cargar su paquete o library(tidyverse)

El operador |> es reciente. Lo más normal es que sigas encontrando el operador de magrittr %>% en la mayoría de tutoriales, libros y scripts.

Un truco para escribir rápidamente el operador es pulsar CTRL + SHIFT + M. En la actualidad este atajo devuelve |>, antes este atajo escribía %>%. Si prefieres que continue escribiendo la pipe de magrittr debes ir a Tools/Gobal options/Code y desmarcar la casilla *use native pipe operator, |>*.

Las pipes parecen raras al principio, pero permiten que nuestro código sea mucho más legible y entendible por un humano:

x <- c(3, 6, 1)

## Sin pipe
round(exp(diff(log(x))), 1)
## [1] 2.0 0.2

## Con pipe
x |> log() |> diff() |> exp() |> round(1)
## [1] 2.0 0.2

Coge el vector x, calcula su logaritmo, luego la diferencia de cada elemento con el anterior, halla su exponencial y redondea a una cifra decimal.

x %>% log() %>% diff() %>% exp() %>% round(1) # mismo resultado

4.5. Principales paquetes del tidyverse

  • readr proporciona funciones para importar tablas en diferentes formatos de texto. Una de sus funciones más utilizadas es read_csv(), que permite importar archivos CSV (Comma Separated Values).

Otro paquete similar, también desarrollado por Hadley Wickham pero no incluido en el tidyverse, es readxl(). Este paquete provee funciones para leer archivos en formato Excel (.xls y .xlsx)

  • tidyr proporciona funciones para reorganizar datos y facilitar que cumplan los principios de tidy data. Dos de sus funciones principales son:
    • pivot_wider(): agrupar datos de varias columnas en una sola. Para pasar de wide a long.
    • pivot_long(): “extender” los datos de una columna en varias. Para pasar de long a wide.

En ambos casos la nomenclatura es muy parecida:

pesos <- tibble(individuo = c("s2026_p1", "s2026_p2", "s2026_p3", "s2026_p4"),
                peso_abril = c(15, 13, 18, 16),
                peso_mayo = c(23, 21, 20, 25),
                peso_junio = c(21, 25, 23, 26))
pesos
## # A tibble: 4 × 4
##   individuo peso_abril peso_mayo peso_junio
##   <chr>          <dbl>     <dbl>      <dbl>
## 1 s2026_p1          15        23         21
## 2 s2026_p2          13        21         25
## 3 s2026_p3          18        20         23
## 4 s2026_p4          16        25         26

pesos_long <- pesos %>%
  pivot_longer(cols = starts_with("peso_"),
               names_to = "mes",
               values_to = "peso")
pesos_long
## # A tibble: 12 × 3
##    individuo mes         peso
##    <chr>     <chr>      <dbl>
##  1 s2026_p1  peso_abril    15
##  2 s2026_p1  peso_mayo     23
##  3 s2026_p1  peso_junio    21
##  4 s2026_p2  peso_abril    13
##  5 s2026_p2  peso_mayo     21
##  6 s2026_p2  peso_junio    25
##  7 s2026_p3  peso_abril    18
##  8 s2026_p3  peso_mayo     20
##  9 s2026_p3  peso_junio    23
## 10 s2026_p4  peso_abril    16
## 11 s2026_p4  peso_mayo     25
## 12 s2026_p4  peso_junio    26

pesos_wide <- pesos_long %>%
  pivot_wider(names_from = mes,
              values_from = "peso")
pesos_wide
## # A tibble: 4 × 4
##   individuo peso_abril peso_mayo peso_junio
##   <chr>          <dbl>     <dbl>      <dbl>
## 1 s2026_p1          15        23         21
## 2 s2026_p2          13        21         25
## 3 s2026_p3          18        20         23
## 4 s2026_p4          16        25         26
  • dplyr es la esencia del tidyverse. Proporciona un conjunto coherente de funciones, denominadas verbos, que permiten manipular fácilmente nuestra tabla. Algunas de las funciones más importantes son: select, rename, filter, mutate, group_by, summarise, arrange.

  • ggplot2 permite construir gráficos mediante una gramática de gráficos por capas a partir de una tabla. La base de cualquier gráfico escrito con ggplot2 está en definir:

    • ggplot() los datos que se van a utilizar,
    • aes() relaciona las variables de la tabla con propiedades visuales (ejes, color, tamaño, forma),
    • geom_*() cómo representar esos valores.
ggplot(data = pesos_long,         # qué datos vamos a usar
       aes(x = mes, y = peso))  + # qué vamos a pintar
  geom_point()                    # qué vamos a dibujar, en este caso puntos

Las correspondencias definidas en ggplot(aes(...)) se aplican por defecto a todas las capas. Pueden definirse dentro de una capa concreta cuando sólo deben afectar a esa geometría geom_point(aes(...)).

ggplot(data = pesos_long, aes(x = mes, y = peso)) +
  geom_line(aes(color = individuo, group = individuo), linewidth = 1.3) +
  geom_point(size = 3) +
  theme_bw() +
  theme(axis.title = element_text(size = 14, face = "bold"),
    axis.text = element_text(size = 12),
    legend.title = element_text(size = 16, face = "bold"),
    legend.text = element_text(size = 12),
    legend.position = "top"
  )

0A. Crea una carpeta nueva en el ordenador que se llame stats_intro_r

Dentro de esa carpeta, crea otra carpeta llamada datos. Descarga los siguientes archivos y colócalos en la carpeta que acabas de crear: pasajeros_aereos.csv , cinturones.csv , paris.zip , arboles_nyc.csv .

0B. Abre un nuevo script. Servirá para practicar la conversión entre datos en formato long y formato wide.

0C. En el preámbulo del script (al principio):

  1. Pon un comentario explicando qué va a hacer el script
  2. Fija la carpeta que has creado como directorio de trabajo
  3. Carga el tidyverse

Acostúmbrate a realizar estos pasos siempre que comiences a escribir en un script.

1. Tidy data

1A. Importa el fichero pasajeros_aereos.csv usando la función read_csv y guárdalo como un objeto.

Este fichero contiene números de pásajeros aéreos por mes y año en forma de tabla.

1B. ¿En qué formato están los datos? Conviértelos a formato tidy usando la función pivot_wider. Guarda el nuevo data frame con otro nombre.

Pista: tendrás que evitar que la columna year sea seleccionada.

1C. Vuelve a convertir la nueva tabla que has creado a formato wide usando la función pivot_longer. Guarda el resultado como otro objeto.

2. Principales paquetes del tidyverse: dplyr

La ley que obliga a los conductores a llevar cinturón en Reino Unido entró en vigor en enero de 1983. El archivo "cinturones.csv" contiene información sobre fallecimientos en carretera entre enero de 1969 y diciembre de 1984. Vamos a utilizar R para analizarlo y comprender si la entrada en vigor de la ley cambió algo.

2A. Importa el fichero cinturones.csv y guárdalo como un objeto.

2B. Análisis exploratorio: 1. ¿Qué columnas contiene el objeto? Puedes usar la función head o glimpse para saberlo. 2. Haz un histograma de las columnas que te parezcan interesantes. Usa el operador $ para acceder a las columnas del data frame. 3. Usa la función ggplot para obtener un gráfico del número de conductores fallecidos. ¿Ves alguna tendencia?

2C. Sólo nos interesan las columnas year, month, conductores_fallecidos, conductores y ley. Selecciónalas usando la función select.

2D. No podemos comparar el número de fallecimientos de un año a otro porque el número de conductores es distinto. Crea una nueva columna fallecidos_tasa que muestre la tasa de fallecidos en cada mes.

2E. Queremos comparar el porcentaje de fallecidos antes y después de la entrada en vigor de la ley. No obstante, hay muchos más datos de antes que de después. Filtra los datos para tener en cuenta solo aquellos ocurridos a partir de 1980.

2F. Por último, vamos a ver en qué años era mayor la tasa de fallecidos. Usa las funciones group_by y summarise para obtener la media de la tasa en cada grupo.

Pista: Tendrás que agrupar por la variable ley.

3. Automatización

Podemos usar R y el tidyverse para procesar simultáneamente multitud de ficheros. En el fichero paris.zip encontrarás 17 archivos con datos de reservas de AirBnB en París para distintos momentos de tiempo.

3A. Descarga el fichero y extráelo sus contenidos en una carpeta de tu proyecto llamado datos_paris.

3B. Abre un nuevo script y haz de esa carpeta tu directorio de trabajo. No te olvides de comentar al principio la función del script y de cargar las librerías.

3C. Vamos a explorar los contenidos del primero de estos ficheros:

  1. Impórtalo usando read_csv.

  2. Usa la función glimpse para ver qué columnas tiene.

  3. ¡Demasiadas! Usa la función select para quedarte solo con: neighborhood, reviews, overall_satisfaction, bedroom y price.

  4. Vamos a comparar los apartamentos disponibles en distintos barrios. Agrupa tus datos por barrio (“neighborhood”) y obtén la media del precio, las valoraciones (“reviews”) y satisfacción general (“overall_satisfaction”).

    Obtén también el número de apartamentos en cada barrio.

    Pista: tendrás que usar la función n() dentro de summarize.

  5. Crea una nueva carpeta en tu carpeta de proyecto que se llame paris_procesados.

    Guarda ahí tus datos en un nuevo CSV usando la función write_csv.

  6. Seguramente hayas hecho esto en varias líneas (y esto está genial para ir probando). Vamos a simplificar el script encadenando todas las funciones con pipes (%>%)

3D. Obtener estos datos para un fichero está bien pero podemos hacer más.

Usa un bucle y la función list.files para repetir el proceso anterior con todos los ficheros de datos.

¿Cómo puedes guardar estos ficheros automáticamente con distinto nombre? Una opción sería usar el nombre original (contenido en list.files) junto con la función paste y la opción sep = "" (para que no aparezcan las dos strings separadas por espacios) . ¿Se te ocurren otras?

Inténtalo un par de veces. Si esto no te sale, consulta la solución en solucion_paris_automatizado.R.

Para poner nombres distintos podrías usar la función gsub para cambiar la terminación de los ficheros. Por ejemplo:

Mostrar código
fichero <- "tomslee_airbnb_paris_0005_2013-11-27.csv"
nombre_guardar <- gsub(".csv", "_agregados.csv", fichero)

4. Visualización de datos: ggplot2

Vamos a visualizar algunas propiedades de los árboles plantados en Nueva York. >Utiliza el paquete ggplot y explora sus posibilidades.

4A. Importa el fichero arboles_nyc.csv

4B. Crea un gráfico de nube de puntos (scatter plot) poniendo longitude en el eje X y latitude en el eje Y.

4C. No se ve mucho en nuestro mapa. Haz que el color del punto sea el de la especie plantada y fija la transparencia a 0.5

Tendrás que usar la opción alpha dentro de geom_point para fijar la transparencia.

4D. ¡Mucho mejor! Saca ahora un histograma de los diámetros (tree_dbh)

4E. Está bien, pero no nos da información por especie. Crea un gráfico de cajas y bigotes (boxplot) para ver cómo varía el diámetro por especie.