1.0 Trabajar con tidyr: organizar y remodelar datos

El trabajo de la ciencia de datos requiere a veces proceso de organización para eficientizar el trabajo de manipulación de los mismos. Hacer este tipo de tareas en Excel requiere de mucho tiempo, pero con paquetes como tidyr (que pertenece a tidyverse) permite unir, ordenar y transformar datos.

1.1 Unir tablas

Es posible para Rstudio unir distintas tablas o bases de datos (en forma de dataframes) usando tidyr. Este tipo de funciones son similares a las que vemos en programas de manejo de bases de datos y tablas como SQL.

Primero instalemos la librería dplyr

install.packages("dplyr")

Luego carguemos la librería

library(dplyr)

Las combinaciones más comunes de utilizar para unión de conjuntos, fuentes de datos, etc son left join, right join, inner join y full join

Iremos paso a paso por cada uno de este tipo de uniones.

2.0 Uniones de tablas y bases de datos:

2.1 Unión por el comandoleft_join()

Este consiste en la unión de dos conjuntos de datos en las que se incluye los datos de la tabla de la izquierda (que es la principal) con los valores coincidentes de la tabla secundaria. Se necesita una variable de clave (variable común)

#Tabla 1
matematicas <- data.frame(
id = c(11,12,13,14,15,16),
NMatematicas = c(5,3,7,8,10,6)
)
#Tabla 2.
lengua <- data.frame(
id = c(11,12,13,14,16,17,18),
Nlengua = c(6,7,8,4,9,7,10)
)
#Tabla 3.
historia <- data.frame(
id_alum = c(11,12,13,14,15,17),
Nhistoria = c(8,5,4,6,9,10)
)

#Librería dplyrs: left_join()
left_join(matematicas, historia, by=c("id"= "id_alum"))
##   id NMatematicas Nhistoria
## 1 11            5         8
## 2 12            3         5
## 3 13            7         4
## 4 14            8         6
## 5 15           10         9
## 6 16            6        NA

2.2 right_join()

  • Mantenemos todas las observaciones de la tabla derecha con este comando pero en el caso de la tabla de matemáticas no se considerarán las que no tienen id con la tabla de Lengua (ej: alumno 15).

  • Los alumnos 17 y 18 poseen un NA al no tener correspondencia en la tabla de matemáticas.

    ##   id NMatematicas Nlengua
    ## 1 11            5       6
    ## 2 12            3       7
    ## 3 13            7       8
    ## 4 14            8       4
    ## 5 16            6       9
    ## 6 17           NA       7
    ## 7 18           NA      10

    2.3 inner_join()

La función inner_join() del paquete dplyr en R es una función para combinar dos marcos de datos (data frames) basándose en una o varias columnas comunes, algo parecido a lo que se hace con la operación JOIN en SQL. El resultado de un inner_join() solo contiene filas para las cuales hay valores coincidentes en ambas tablas.

La sintaxis básica de la función inner_join() es la siguiente:

inner_join(matematicas, lengua)
## Joining with `by = join_by(id)`
##   id NMatematicas Nlengua
## 1 11            5       6
## 2 12            3       7
## 3 13            7       8
## 4 14            8       4
## 5 16            6       9

Al final resultó que:

  • Se excluye al alumno de la tabla matemáticas: 15 (no existe en la tabla lengua).

  • Se excluye a los alumnos de la tabla lengua: 17 y 18 (no existen en la tabla matemáticas).

  • No se generan NA.

2.4 full_join()

La función full_join() del paquete dplyr en R se utiliza para unir dos data frames por una o varias columnas comunes, similar a cómo se realiza una operación FULL JOIN en SQL. A diferencia de inner_join(), full_join() conservará todas las filas de ambos data frames, independientemente de si hay una coincidencia en las columnas especificadas. Si no hay coincidencia, los valores de las columnas del otro data frame se establecerán como NA.

La sintaxis básica de full_join() es similar a inner_join():

full_join(matematicas, lengua)
## Joining with `by = join_by(id)`
##   id NMatematicas Nlengua
## 1 11            5       6
## 2 12            3       7
## 3 13            7       8
## 4 14            8       4
## 5 15           10      NA
## 6 16            6       9
## 7 17           NA       7
## 8 18           NA      10

Al final se incluyen todas las observaciones pero el id=15 existe en la tabla de matemáticas pero no la del lengua (NA). De manera similar a los id=17 y 18 existen en la tabla de lengua pero no en la de matemáticas NA.

3.0¿Para qué otras cosas sirve la librería tidyr?

La librería tidyr es una de las librerías más útiles en el lenguaje de programación R para la manipulación de datos. Es parte del conjunto de paquetes conocidos como tidyverse diseñados para la ciencia de datos. La filosofía central del tidyverse es tener datos ordenados o “tidy data”, donde cada variable es una columna y cada observación es una fila.

El paquete tidyr proporciona un conjunto de funciones que ayudan a manipular la estructura de los datos para que se ajusten a este formato “tidy”. Esto es especialmente útil cuando los datos con los que estás trabajando inicialmente están en un formato desordenado o “messy”.

Algunas de las funciones clave en tidyr incluyen:

  1. gather(): Esta función toma múltiples columnas y las combina en pares de clave-valor, lo que es útil cuando tienes columnas que son valores, no variables.

  2. spread(): Esta es la operación inversa de gather(). Toma pares de clave-valor y los propaga a través de múltiples columnas.

  3. separate(): Esta función divide una columna en múltiples columnas.

  4. unite(): Esta es la operación inversa de separate(). Toma múltiples columnas y las combina en una sola.

Estas funciones son esenciales para hacer que los datos sean más manejables y fáciles de analizar, lo que a su vez facilita el proceso de limpieza de datos y el análisis posterior.

4.1 gather()

Esta permite la transformación de un conjunto de datos de largo a ancho, el código es:

gather(data, key= "key", value= "value", ...,na.rm=FALSE, convert= FALSE, factor_key=FALSE

  • data es el conjunto de datos

  • key es la variable a agrupar (key)

  • value es el nombre de la variable cuyo valor esta distribuido dentro de la celda.

  • na.rm=TRUE; se eliminan los valores faltantes.

  • factor_key= FALSE; los valores se almacenan como vectores, si es TRUE se almacenan como factores.

3.1.1 Ejemplo práctico

3.2 spread()

El comando `spread()` en R, que forma parte de la librería `tidyr`, es utilizado para transformar datos que están en formato largo a formato ancho. Dicho de otro modo, es útil para cambiar la estructura de un conjunto de datos de manera que los valores de una columna variable se conviertan en múltiples columnas.

El funcionamiento básico de `spread()` es tomar dos columnas: una que indica las ‘llaves’ (las futuras columnas en el formato ancho) y otra que indica los ‘valores’ que irán en esas columnas.

Un ejemplo simple ayudará a entenderlo mejor. Imagina un conjunto de datos que registra, en formato largo, las calificaciones de estudiantes en diferentes materias:

library(tidyr)

data <- data.frame(
  estudiante = c('Juan', 'Juan', 'Ana', 'Ana'),
  materia = c('Matemáticas', 'Historia', 'Matemáticas', 'Historia'),
  calificacion = c(90, 85, 95, 88)
)

data_ancho <- spread(data, key = materia, value = calificacion)

Es importante mencionar que, aunque `spread()` ha sido ampliamente utilizado, ha sido sucedido por la función `pivot_wider()` en versiones más recientes de la librería `tidyr`, la cual es más flexible y tiene una sintaxis que se busca sea más intuitiva. Por lo tanto, si estás trabajando con versiones más recientes de `tidyr`, te recomendaría considerar el uso de `pivot_wider()` en lugar de `spread()`.

Otro dato importante es que spread() se uitiliza cuando tenemos una observación dispersa en múltiples filas. (ej. si tenemos datos de ciudades con una columna llamada “Tipo” de variable que indica población o área, realmente cada observación está esparcida en dos filas, una fila para la población y otra para área). Nos permite llevar un dataset a la forma de una observación por fila.

3.3 Pivotante()

En el contexto de R y, específicamente, con el paquete tidyverse, pivotar se refiere a cambiar la forma de los datos entre formatos “long” (largo) y “wide” (ancho). Esta funcionalidad se logra principalmente con las funciones pivot_longer() y pivot_wider() del paquete tidyr, que es parte del tidyverse.

  1. pivot_longer() convierte las columnas en filas, es para pasar de wide a long format. (Similar al reshape long en Stata).
  2. pivot_wider() convierte las filas en columnas, es para pasar de formato largo a ancho (long to wide formant).
library(tidyr)
dato_wide<- tibble(Curso_1=c(1,3,4, 3,4, 9, 7, 4, 8,8), Curso_2=c(1,10, 7, 7, 3, 4, 5, 6, 7, 6))

dato_wide %>% 
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")
## # A tibble: 20 × 2
##    Variable Valor
##    <chr>    <dbl>
##  1 Curso_1      1
##  2 Curso_2      1
##  3 Curso_1      3
##  4 Curso_2     10
##  5 Curso_1      4
##  6 Curso_2      7
##  7 Curso_1      3
##  8 Curso_2      7
##  9 Curso_1      4
## 10 Curso_2      3
## 11 Curso_1      9
## 12 Curso_2      4
## 13 Curso_1      7
## 14 Curso_2      5
## 15 Curso_1      4
## 16 Curso_2      6
## 17 Curso_1      8
## 18 Curso_2      7
## 19 Curso_1      8
## 20 Curso_2      6

Otro ejemplo pero con pivot_wider():

library(tidyr)
datos_largo <- tibble(variable = c("a", "b", "a", "b"), valor = c(1, 3, 2, 4))
datos_largo %>%
  pivot_wider(names_from = variable, values_from = valor)
## Warning: Values from `valor` are not uniquely identified; output will contain list-cols.
## • Use `values_fn = list` to suppress this warning.
## • Use `values_fn = {summary_fun}` to summarise duplicates.
## • Use the following dplyr code to identify duplicates.
##   {data} %>%
##   dplyr::group_by(variable) %>%
##   dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
##   dplyr::filter(n > 1L)
## # A tibble: 1 × 2
##   a         b        
##   <list>    <list>   
## 1 <dbl [2]> <dbl [2]>

La capacidad de pivotar datos es esencial para muchas tareas de preparación y análisis de datos. Permite al usuario reformatear fácilmente los datos según cómo se quieran analizar o visualizar. Por ejemplo, algunas funciones de gráficos podrían requerir un formato específico (largo o ancho) para funcionar correctamente, o ciertos modelos estadísticos podrían requerir que los datos estén en un formato específico. Por lo tanto, saber cómo pivotar los datos eficientemente es una habilidad valiosa al trabajar con R y el tidyverse.

3.4 unite()

La función unite() de la librería tidyr en R se utiliza para unir dos o más columnas en una única columna, generalmente concatenando sus valores en una cadena de texto. La lógica del comando es la siguiente:

dataframe%>% unite(x, y, z, sep= "arg")%>% head()

X, Y y Z son variables que quieres unir, sep= es el seperador que utilizarás para separar cada resultado de cada variable dentro del vector de dato "arg" es el separador, el cual puede ser " " (un separador) o un guión ("-")

3.3.1 Ejemplo con datos storms:

]

library(tidyr)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ readr     2.1.4
## ✔ ggplot2   3.4.2     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
data("storms")

storms %>% 
  unite(fecha, year, month, day, sep="-") %>% 
  head()
## # A tibble: 6 × 11
##   name  fecha      hour   lat  long status              category  wind pressure
##   <chr> <chr>     <dbl> <dbl> <dbl> <fct>                  <dbl> <int>    <int>
## 1 Amy   1975-6-27     0  27.5 -79   tropical depression       NA    25     1013
## 2 Amy   1975-6-27     6  28.5 -79   tropical depression       NA    25     1013
## 3 Amy   1975-6-27    12  29.5 -79   tropical depression       NA    25     1013
## 4 Amy   1975-6-27    18  30.5 -79   tropical depression       NA    25     1013
## 5 Amy   1975-6-28     0  31.5 -78.8 tropical depression       NA    25     1012
## 6 Amy   1975-6-28     6  32.4 -78.7 tropical depression       NA    25     1012
## # ℹ 2 more variables: tropicalstorm_force_diameter <int>,
## #   hurricane_force_diameter <int>
#Otro ejemplo pero con una fecha distinta:
storms_nueva<-storms %>% 
  unite(fecha, day, month, year, sep="-") # Esta es la fecha en formato usado en habla hispana. Contrario al primer ejemplo en el que el año está primero.
  head(storms_nueva)
## # A tibble: 6 × 11
##   name  fecha      hour   lat  long status              category  wind pressure
##   <chr> <chr>     <dbl> <dbl> <dbl> <fct>                  <dbl> <int>    <int>
## 1 Amy   27-6-1975     0  27.5 -79   tropical depression       NA    25     1013
## 2 Amy   27-6-1975     6  28.5 -79   tropical depression       NA    25     1013
## 3 Amy   27-6-1975    12  29.5 -79   tropical depression       NA    25     1013
## 4 Amy   27-6-1975    18  30.5 -79   tropical depression       NA    25     1013
## 5 Amy   28-6-1975     0  31.5 -78.8 tropical depression       NA    25     1012
## 6 Amy   28-6-1975     6  32.4 -78.7 tropical depression       NA    25     1012
## # ℹ 2 more variables: tropicalstorm_force_diameter <int>,
## #   hurricane_force_diameter <int>

3.5 separate()

La función separate() de la librería tidyr en R hace exactamente lo opuesto a unite(). Se utiliza para dividir una columna en varias columnas, basándose en un delimitador especificado.

3.5.1 Ejemplo práctico

df_unido <- data.frame(
  nombre = c("Juan", "Ana", "Carlos"),
  apellido_completo = c("Gómez Ramírez", "López Sánchez", "Martínez Pérez")
)

print(df_unido)
##   nombre apellido_completo
## 1   Juan     Gómez Ramírez
## 2    Ana     López Sánchez
## 3 Carlos    Martínez Pérez
df_separado <- df_unido %>%
  separate(apellido_completo, into = c("apellido1", "apellido2"), sep = " ")

print(df_separado)
##   nombre apellido1 apellido2
## 1   Juan     Gómez   Ramírez
## 2    Ana     López   Sánchez
## 3 Carlos  Martínez     Pérez

3.5.1 Otro ejemplo con la base de datos storms:

Recuerdan que habíamos creado una variable llamada fecha en la base de datos original? Lo que haremos es separarla.

storms_nueva %>% 
  separate(fecha, into= c("day", "month", "year"), sep="-") %>% 
  head()
## # A tibble: 6 × 13
##   name  day   month year   hour   lat  long status       category  wind pressure
##   <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <fct>           <dbl> <int>    <int>
## 1 Amy   27    6     1975      0  27.5 -79   tropical de…       NA    25     1013
## 2 Amy   27    6     1975      6  28.5 -79   tropical de…       NA    25     1013
## 3 Amy   27    6     1975     12  29.5 -79   tropical de…       NA    25     1013
## 4 Amy   27    6     1975     18  30.5 -79   tropical de…       NA    25     1013
## 5 Amy   28    6     1975      0  31.5 -78.8 tropical de…       NA    25     1012
## 6 Amy   28    6     1975      6  32.4 -78.7 tropical de…       NA    25     1012
## # ℹ 2 more variables: tropicalstorm_force_diameter <int>,
## #   hurricane_force_diameter <int>

Ahora bien, si queremos preservar la variable fecha agregamos el argumento remove= FALSE:

storms_nueva %>% 
separate(fecha, into= c("day", "month", "year"), sep= "-", remove = F) %>% 
  head()
## # A tibble: 6 × 14
##   name  fecha day   month year   hour   lat  long status category  wind pressure
##   <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <fct>     <dbl> <int>    <int>
## 1 Amy   27-6… 27    6     1975      0  27.5 -79   tropi…       NA    25     1013
## 2 Amy   27-6… 27    6     1975      6  28.5 -79   tropi…       NA    25     1013
## 3 Amy   27-6… 27    6     1975     12  29.5 -79   tropi…       NA    25     1013
## 4 Amy   27-6… 27    6     1975     18  30.5 -79   tropi…       NA    25     1013
## 5 Amy   28-6… 28    6     1975      0  31.5 -78.8 tropi…       NA    25     1012
## 6 Amy   28-6… 28    6     1975      6  32.4 -78.7 tropi…       NA    25     1012
## # ℹ 2 more variables: tropicalstorm_force_diameter <int>,
## #   hurricane_force_diameter <int>

4.0 Conclusiones sobre la librería tidyr

En esta sección, hemos adquirido habilidades sobre cómo fusionar dos conjuntos de datos, generando diferentes tablas basadas en los resultados que buscamos alcanzar. Como observamos, la sintaxis utilizada en todas estas funciones es consistente y comparte similitudes notables con el lenguaje SQL. Estas técnicas de unión son esenciales para manejar datos en R, especialmente cuando trabajamos con conjuntos de datos grandes o múltiples fuentes de datos.

Los diferentes tipos de uniones, como `inner_join`, `left_join`, `right_join` y `full_join`, nos permiten abordar distintos escenarios y cumplir con diferentes requerimientos de análisis de datos. Por ejemplo, `inner_join` nos permite encontrar y combinar las filas que comparten valores comunes en ambas tablas, mientras que `full_join` nos proporciona una unión completa, conservando todas las filas de ambos conjuntos de datos, incluso si no existe una coincidencia directa en las columnas clave.

Estas funciones de unión son herramientas poderosas que, cuando se usan correctamente, pueden simplificar y mejorar nuestros procesos de análisis y manipulación de datos. No solo permiten combinar datos de manera eficiente, sino que también ayudan a mantener la integridad de nuestros datos al preservar las relaciones entre las diferentes variables y observaciones.