Data Manipulation

Proceso del Tidyverse

Proceso del Tidyverse

La manipulación de datos es parte del proceso de Data Wrangling. En esta sección veremos algunos paquetes que nos servirán para manipular los datos antes del análisis exploratorio de datos, entre ellos, dplyr (una de las herramientas más importantes en el data wrangling), magrittr, lubridate, stringr y forcats.

Operador Pipe %>%

El paquete magrittr ofrece un conjunto de operadores que hacen que su código sea más legible al:

  • Estructurar secuencias de operaciones de datos de izquierda a derecha (a diferencia de desde adentro hacia afuera).

  • Minimizando la necesidad de variables locales y definiciones de funciones, y

  • Facilitando la adición de pasos en cualquier lugar de la secuencia de operaciones.

Puedes encontrar más información del paquete magrittr en magrittr.tidyverse.org

Puede utilizar el atajo de teclado Ctrl+Shift+M para generar el operador %>%. Ejemplo: Genere una vista en el editor de scripts de las primeras 5 observaciones de las columnas de Transacción y Tiempo_Servicio_seg de la Data banco.

library(magrittr)
## 
## Attaching package: 'magrittr'
## The following object is masked from 'package:purrr':
## 
##     set_names
## The following object is masked from 'package:tidyr':
## 
##     extract
banco %>% select(Transaccion, Tiempo_Servicio_seg) %>% head(5)
## # A tibble: 5 × 2
##   Transaccion              Tiempo_Servicio_seg
##   <chr>                                  <dbl>
## 1 Cobro/Pago (Cta externa)                 311
## 2 Cobro/Pago (Cta externa)                 156
## 3 Cobro/Pago (Cta externa)                 248
## 4 Cobro/Pago (Cta externa)                  99
## 5 Cobro/Pago (Cta externa)                 123

str(banco)

Transformación de datos con dplyr

El paquete dplyr proporciona un vocabulario intuitivo para ejecutar tareas de gestión y análisis de datos.

La visualización de datos es una herramienta importante para la generación de conocimiento; sin embargo, es raro que obtengas los datos exactamente en la forma en que los necesitas, por lo que previamente tendrás que crear algunas variables nuevas o resúmenes, o tal vez solo quieras cambiar el nombre de las variables o reordenar las observaciones para facilitar el trabajo con los datos, o agrupar las observaciones y todo esto es lo que te ayuda a hacer dplyr.

Puedes encontrar más información del paquete dplyr en dplyr.tidyverse.org

El paquete proporciona un conjunto de verbos (funciones) para describir y realizar tareas comunes de preparación de datos. Específicamente, la gramática dplyr le permite hablar y realizar fácilmente tareas como las siguientes:

  • select() -> Seleccionar características específicas (columnas) de interés de un conjunto de datos.
  • filter() -> Filtrar los datos irrelevantes y mantenga solo las observaciones (filas) de interés.
  • mutate() -> Mutar un conjunto de datos agregando más características (columnas).
  • arrange() -> Organizar las observaciones (filas) en un orden particular
  • summarise() -> Resumir los datos en términos de agregados como la media, la mediana o el máximo.
  • group_by() -> Todos esta funciones se pueden usar junto con group_by(), que cambia el alcance de cada función de operar en todo el conjunto de datos a operar en él grupo por grupo.

Estas seis funciones proporcionan los verbos para un lenguaje de manipulación de datos.

Recuerda que para visualizar los datos en el editor de scripts en RStudio es mejor utilizar view().

Seleccionar columnas con select()

select() permite seleccionar rápidamente un subconjunto útil utilizando operaciones basadas en los nombres de las variables.

Para seleccionar las columnas por nombre:

banco %>% select(Transaccion, Tiempo_Servicio_seg)
## # A tibble: 24,299 × 2
##    Transaccion              Tiempo_Servicio_seg
##    <chr>                                  <dbl>
##  1 Cobro/Pago (Cta externa)                 311
##  2 Cobro/Pago (Cta externa)                 156
##  3 Cobro/Pago (Cta externa)                 248
##  4 Cobro/Pago (Cta externa)                  99
##  5 Cobro/Pago (Cta externa)                 123
##  6 Cobro/Pago (Cta externa)                 172
##  7 Cobro/Pago (Cta externa)                 140
##  8 Cobro/Pago (Cta externa)                 247
##  9 Cobro/Pago (Cta externa)                 183
## 10 Cobro/Pago (Cta externa)                  91
## # … with 24,289 more rows

Para seleccionar todas las columnas entre Transacción y Monto:

banco %>% select(Transaccion:Monto)
## # A tibble: 24,299 × 4
##    Transaccion              Tiempo_Servicio_seg Satisfaccion Monto
##    <chr>                                  <dbl> <fct>        <dbl>
##  1 Cobro/Pago (Cta externa)                 311 Muy Bueno    2889.
##  2 Cobro/Pago (Cta externa)                 156 Malo         1671.
##  3 Cobro/Pago (Cta externa)                 248 Regular        NA 
##  4 Cobro/Pago (Cta externa)                  99 Regular      1765.
##  5 Cobro/Pago (Cta externa)                 123 Muy Bueno    1836.
##  6 Cobro/Pago (Cta externa)                 172 Bueno        2165.
##  7 Cobro/Pago (Cta externa)                 140 Regular      1305.
##  8 Cobro/Pago (Cta externa)                 247 Bueno        4080.
##  9 Cobro/Pago (Cta externa)                 183 Muy Bueno    2541.
## 10 Cobro/Pago (Cta externa)                  91 Muy Bueno    2219.
## # … with 24,289 more rows

Para seleccionar todas las columnas excepto aquellas entre Transacción y Monto:

banco %>% select(-(Transaccion:Monto))
## # A tibble: 24,299 × 3
##    Sucursal Cajero ID_Transaccion
##       <dbl>  <dbl> <chr>         
##  1       62   4820 2             
##  2       62   4820 2             
##  3       62   4820 2             
##  4       62   4820 2             
##  5       62   4820 2             
##  6       62   4820 2             
##  7       62   4820 2             
##  8       62   4820 2             
##  9       62   4820 2             
## 10       62   4820 2             
## # … with 24,289 more rows

Existen argumentos auxiliares para seleccionar bajo otros criterios, por ejemplo, para seleccionar columnas cuyos nombres inician con “s”:

banco %>% select(starts_with("s"))
## # A tibble: 24,299 × 2
##    Sucursal Satisfaccion
##       <dbl> <fct>       
##  1       62 Muy Bueno   
##  2       62 Malo        
##  3       62 Regular     
##  4       62 Regular     
##  5       62 Muy Bueno   
##  6       62 Bueno       
##  7       62 Regular     
##  8       62 Bueno       
##  9       62 Muy Bueno   
## 10       62 Muy Bueno   
## # … with 24,289 more rows

Para seleccionar columnas cuyos nombres contienen “sa”

banco %>% select(contains("sa"))
## # A tibble: 24,299 × 4
##    Sucursal ID_Transaccion Transaccion              Satisfaccion
##       <dbl> <chr>          <chr>                    <fct>       
##  1       62 2              Cobro/Pago (Cta externa) Muy Bueno   
##  2       62 2              Cobro/Pago (Cta externa) Malo        
##  3       62 2              Cobro/Pago (Cta externa) Regular     
##  4       62 2              Cobro/Pago (Cta externa) Regular     
##  5       62 2              Cobro/Pago (Cta externa) Muy Bueno   
##  6       62 2              Cobro/Pago (Cta externa) Bueno       
##  7       62 2              Cobro/Pago (Cta externa) Regular     
##  8       62 2              Cobro/Pago (Cta externa) Bueno       
##  9       62 2              Cobro/Pago (Cta externa) Muy Bueno   
## 10       62 2              Cobro/Pago (Cta externa) Muy Bueno   
## # … with 24,289 more rows

Operadores lógicos

Set completo de operadores booleanos

Set completo de operadores booleanos

Ahora puedo combinar algunos argumentos, por ejemplo, para seleccionar columnas cuyos nombres inician con “s” o terminan con “n”.

banco %>% select(starts_with("s")|ends_with("n"))
## # A tibble: 24,299 × 4
##    Sucursal Satisfaccion ID_Transaccion Transaccion             
##       <dbl> <fct>        <chr>          <chr>                   
##  1       62 Muy Bueno    2              Cobro/Pago (Cta externa)
##  2       62 Malo         2              Cobro/Pago (Cta externa)
##  3       62 Regular      2              Cobro/Pago (Cta externa)
##  4       62 Regular      2              Cobro/Pago (Cta externa)
##  5       62 Muy Bueno    2              Cobro/Pago (Cta externa)
##  6       62 Bueno        2              Cobro/Pago (Cta externa)
##  7       62 Regular      2              Cobro/Pago (Cta externa)
##  8       62 Bueno        2              Cobro/Pago (Cta externa)
##  9       62 Muy Bueno    2              Cobro/Pago (Cta externa)
## 10       62 Muy Bueno    2              Cobro/Pago (Cta externa)
## # … with 24,289 more rows

Para seleccionar columnas cuyos nombres inician con “s” y terminan con “n”.

banco %>% select(starts_with("s")&ends_with("n"))
## # A tibble: 24,299 × 1
##    Satisfaccion
##    <fct>       
##  1 Muy Bueno   
##  2 Malo        
##  3 Regular     
##  4 Regular     
##  5 Muy Bueno   
##  6 Bueno       
##  7 Regular     
##  8 Bueno       
##  9 Muy Bueno   
## 10 Muy Bueno   
## # … with 24,289 more rows

Comparaciones

Para usar el filtrado de manera efectiva, debes saber cómo seleccionar las observaciones que deseas utilizando los operadores de comparación. R proporciona el conjunto estándar: >, >=, <, <=, != (no igual) y == (igual).

Filtrar filas con filter()

filter() permite filtrar un subconjunto de observaciones según sus valores para lo cual son necesarios los operadores de comparación.

Para filtrar todas las transacciones de la sucursal 267:

banco %>% filter(Sucursal==267)
## # A tibble: 3,329 × 7
##    Sucursal Cajero ID_Transaccion Transaccion      Tiempo_Servicio… Satisfaccion
##       <dbl>  <dbl> <chr>          <chr>                       <dbl> <fct>       
##  1      267   2503 2              Cobro/Pago (Cta…             450. Muy Bueno   
##  2      267   2503 2              Cobro/Pago (Cta…             244. Malo        
##  3      267   2503 2              Cobro/Pago (Cta…             101. Malo        
##  4      267   2503 2              Cobro/Pago (Cta…             511. Bueno       
##  5      267   2503 2              Cobro/Pago (Cta…             405. Malo        
##  6      267   2503 2              Cobro/Pago (Cta…             667. Muy Bueno   
##  7      267   2503 2              Cobro/Pago (Cta…             274. Malo        
##  8      267   2503 2              Cobro/Pago (Cta…             154. Muy Malo    
##  9      267   2503 2              Cobro/Pago (Cta…             424. Muy Bueno   
## 10      267   2503 2              Cobro/Pago (Cta…             474. Muy Malo    
## # … with 3,319 more rows, and 1 more variable: Monto <dbl>

Para filtrar todas las transacciones de la sucursal 267 y hayan durado más de 120 segundos:

banco %>% filter(Sucursal==267 & Tiempo_Servicio_seg>120)
## # A tibble: 2,067 × 7
##    Sucursal Cajero ID_Transaccion Transaccion      Tiempo_Servicio… Satisfaccion
##       <dbl>  <dbl> <chr>          <chr>                       <dbl> <fct>       
##  1      267   2503 2              Cobro/Pago (Cta…             450. Muy Bueno   
##  2      267   2503 2              Cobro/Pago (Cta…             244. Malo        
##  3      267   2503 2              Cobro/Pago (Cta…             511. Bueno       
##  4      267   2503 2              Cobro/Pago (Cta…             405. Malo        
##  5      267   2503 2              Cobro/Pago (Cta…             667. Muy Bueno   
##  6      267   2503 2              Cobro/Pago (Cta…             274. Malo        
##  7      267   2503 2              Cobro/Pago (Cta…             154. Muy Malo    
##  8      267   2503 2              Cobro/Pago (Cta…             424. Muy Bueno   
##  9      267   2503 2              Cobro/Pago (Cta…             474. Muy Malo    
## 10      267   2503 2              Cobro/Pago (Cta…             350. Muy Bueno   
## # … with 2,057 more rows, and 1 more variable: Monto <dbl>

Para filtrar todas las transacciones de la sucursal 267 y hayan durado más de 120 segundos y la evaluación a la satisfacción sea “Muy Malo”:

banco %>% filter(Sucursal==267 & Tiempo_Servicio_seg>120 & Satisfaccion=="Muy Malo")
## # A tibble: 334 × 7
##    Sucursal Cajero ID_Transaccion Transaccion      Tiempo_Servicio… Satisfaccion
##       <dbl>  <dbl> <chr>          <chr>                       <dbl> <fct>       
##  1      267   2503 2              Cobro/Pago (Cta…             154. Muy Malo    
##  2      267   2503 2              Cobro/Pago (Cta…             474. Muy Malo    
##  3      267   2503 2              Cobro/Pago (Cta…             270. Muy Malo    
##  4      267   2503 2              Cobro/Pago (Cta…             653. Muy Malo    
##  5      267   2503 2              Cobro/Pago (Cta…             121. Muy Malo    
##  6      267   2503 2              Cobro/Pago (Cta…             151. Muy Malo    
##  7      267   2503 2              Cobro/Pago (Cta…             215. Muy Malo    
##  8      267   2503 2              Cobro/Pago (Cta…             154. Muy Malo    
##  9      267   2503 2              Cobro/Pago (Cta…             278. Muy Malo    
## 10      267   2503 2              Cobro/Pago (Cta…             340. Muy Malo    
## # … with 324 more rows, and 1 more variable: Monto <dbl>

Para filtrar las transacciones que sean mayor a 3000 dólares y menor a 5000 dólares (incluidos)

banco %>% 
    filter(Monto>=3000 & Monto<=5000) %>% 
    select(-c(ID_Transaccion, Sucursal, Cajero))
## # A tibble: 2,130 × 4
##    Transaccion              Tiempo_Servicio_seg Satisfaccion Monto
##    <chr>                                  <dbl> <fct>        <dbl>
##  1 Cobro/Pago (Cta externa)                 247 Bueno        4080.
##  2 Cobro/Pago (Cta externa)                 209 Bueno        3803.
##  3 Cobro/Pago (Cta externa)                 329 Bueno        3423.
##  4 Cobro/Pago (Cta externa)                 319 Regular      3253.
##  5 Cobro/Pago (Cta externa)                 188 Muy Bueno    3083.
##  6 Cobro/Pago (Cta externa)                 192 Regular      3819.
##  7 Cobro/Pago (Cta externa)                 190 Malo         3522.
##  8 Cobro/Pago (Cta externa)                 222 Regular      3073 
##  9 Cobro/Pago (Cta externa)                 375 Bueno        3541.
## 10 Cobro/Pago (Cta externa)                 315 Bueno        3594.
## # … with 2,120 more rows

Otra forma de hacerlo es usando between().

Ejercicios propuestos:

  1. ¿Qué sucede si incluyes el nombre de una variable varias veces en una llamada a select()?

  2. ¿Cómo tratan por defecto las funciones auxiliares de select() a las palabras en mayúsculas o en minúsculas? ¿Existe distinción?

  3. Muestre el cajero, ID_Transaccion y monto de las transacciones que son depósitos que su tiempo de servicio haya durado menos de 3 minutos y que se hayan realizado en la Sucursal 443.

Reordenar filas con arrange()

arrange() trabaja de manera similar a filter(), es decir con filas, a diferencia que lo que hace es cambiar su orden.

Para ordenar el conjunto de datos por Satisfacción de forma alfabética o ascendente:

banco %>% 
    arrange(Satisfaccion)

Para ordenar el conjunto de datos por Satisfacción de forma descendente:

banco %>% 
    arrange(desc(Satisfaccion))

Añadir variables con mutate()

mutate() (del inglés mutar o transformar) crea nuevas columnas en función de columnas existentes o modifica columnas ya existentes.

Las columnas existentes que son modificadas, siempre se devolverán a su ubicación original. Y las nuevas columnas creadas a través de mutate() se colocarán de acuerdo con los argumentos .before y .after.

Para crear nueva columna con el tiempo de servicio en minutos:

banco %>% 
    mutate(Tiempo_Serv_Min=Tiempo_Servicio_seg/60)

Para conservar sólo las nuevas variables use transmute():

banco %>% 
    transmute(Tiempo_minutos=Tiempo_Servicio_seg/60)
## # A tibble: 24,299 × 1
##    Tiempo_minutos
##             <dbl>
##  1           5.18
##  2           2.6 
##  3           4.13
##  4           1.65
##  5           2.05
##  6           2.87
##  7           2.33
##  8           4.12
##  9           3.05
## 10           1.52
## # … with 24,289 more rows

Para crear nuevas variables de otras nuevas:

banco %>% 
    mutate(Tiempo_minutos=Tiempo_Servicio_seg/60,
           Tiempo_horas=Tiempo_minutos/60)

Para modificar las variables ya existentes:

banco <- banco %>%
    mutate(Sucursal= as.character(Sucursal),
           Cajero = as.character(Cajero),
           Satisfaccion = parse_factor(Satisfaccion,
                                       levels= c('Muy Malo', 'Malo', 'Regular',
                                                 'Bueno', 'Muy Bueno')),
           Monto= parse_number(Monto, locale = locale(decimal_mark = ".")))

Funciones parse_*()

Estas funciones toman un vector de caracteres y devuelven un vector más especializado, como un vector lógico, numérico o una fecha. Hay ocho segmentadores particularmente importantes:

  • parse_logical() y parse_integer() analizan valores lógicos y números enteros respectivamente.

  • parse_double() es un segmentador numérico estricto, y parse_number() es un segmentador numérico flexible.

  • parse_character() parece tan simple que no debiera ser necesario. Pero una complicación lo hace bastante importante: la codificación de caracteres (el encoding).

  • parse_factor() crea factores, la estructura de datos que R usa para representar variables categóricas con valores fijos y conocidos.

  • parse_datetime(), parse_date() y parse_time() te permiten analizar diversas especificaciones de fechas y horas.

Números

Por ejemplo, podemos visualizar las diferencias entre usar as.numeric() o parse_number().

parse_number("$1,543.4")
## [1] 1543.4
as.numeric("$1,543.4")
## Warning: NAs introducidos por coerción
## [1] NA

Observamos que parse_number() convierte el dato en tipo numérico:

class(parse_number("$1,543.4"))
## [1] "numeric"

Podemos definir los decimales con decimal_mark:

parse_number("$1.543,4", locale = locale(decimal_mark = ","))
## [1] 1543.4

Fechas, fechas-horas y horas

El paquete lubridate ofrece un conjunto de funciones para trabajar con datos de fecha y hora en R.

Los comandos R para fecha y hora generalmente no son intuitivos y cambian según el tipo de objeto de fecha y hora que se utilice.

lubridate hace que sea más fácil hacer las cosas que hace R con fecha y hora y hace posible hacer las cosas que R no hace, ya que carece de capacidades como resistencia a las zonas horarias, los días bisiestos y los horarios de verano.

Puedes encontrar más información del paquete lubridate en lubridate.tidyverse.org

Por ejemplo, si deseamos convertir datos numéricos como los años a períodos, usamos la función years():

t1 <- 2022
lubridate::years(t1)
## [1] "2022y 0m 0d 0H 0M 0S"
class(lubridate::years(t1))
## [1] "Period"
## attr(,"package")
## [1] "lubridate"

También podemos usar las funciones parse_*, as.Date() y as.POSIXct().

parse_datetime() asume una fecha-hora ISO8601. ISO8601 es un estándar internacional en el que los componentes de una fecha están organizados de mayor a menor: año, mes, día, hora, minuto, segundo.

parse_datetime("2010-10-01T2010")
## [1] "2010-10-01 20:10:00 UTC"
as.Date("2010-10-01T2010")
## [1] "2010-10-01"
parse_datetime("20101010")
## [1] "2010-10-10 UTC"

parse_date() asume un año de cuatro dígitos, un guión - o /, el mes, un guión - o / y luego el día.

parse_date("2010-10-01")
## [1] "2010-10-01"

parse_time() espera la hora, :, minutos, opcionalmente : y segundos, y un especificador opcional am/pm:

parse_time("01:10 am")
## 01:10:00
parse_time("20:10:01")
## 20:10:01

Si esos valores por defecto no funcionan con tus datos, puedes proporcionar tu propio formato fecha-hora construido con las siguientes piezas:

Año

  • %Y (4 dígitos)
  • %y (2 dígitos)

Mes

  • %m (2 dígitos)
  • %b (nombre abreviado, como “ene”)
  • %B (nombre completo, “enero”)

Día

  • %d (2 dígitos)
  • %e (espacio opcional destacado)

Hora

  • %H 0-23 horas
  • %I 0-12, debe utilizarse con %p
  • %p indicador AM/PM
  • %M minutos
  • %S segundos enteros
  • %OS segundos reales

Por ejemplo:

parse_date("01/02/15", "%m/%d/%y")
## [1] "2015-01-02"
parse_date("01/02/15", "%d/%m/%y")
## [1] "2015-02-01"
parse_date("01/02/15", "%y/%m/%d")
## [1] "2001-02-15"

Si estás utilizando %b o %B con nombres de meses no ingleses, necesitarás ajustar el argumento lang para locale(). Mira la lista de lenguas incorporados en date_names_langs(). Por ejemplo una fecha en italiano:

parse_date("14 febbraio 2016", "%d %B %Y", locale = locale("it"))
## [1] "2016-02-14"

Ejercicio propuesto: Genera el formato correcto de texto para segmentar cada una de las siguientes fechas y horas:

d3 <- "06-Jun-2017"
d5 <- "12/30/14" # Dec 30, 2014
t1 <- "1705"
t2 <- "11:15:10.12 PM"

Resúmenes con summarise()

summarise() (resumir en inglés) colapsa toda la información en una sola fila, y es mucho más útil si la usamos con group_by()

Para sumar todos los montos de las transacciones:

banco %>%
    summarise(monto_total=sum(Monto, na.rm=TRUE))
## # A tibble: 1 × 1
##   monto_total
##         <dbl>
## 1   48501426.

Podemos agregar más información, por ejemplo el promedio con mean(), máximo max(), mínimo min() y conteo de filas con n().

banco %>%
    summarise(monto_total=sum(Monto, na.rm=TRUE),
              promedio=mean(Monto, na.rm=TRUE),
              minimo=min(Monto, na.rm=TRUE), 
              maximo=max(Monto, na.rm=TRUE),
              num_transacciones=n())
## # A tibble: 1 × 5
##   monto_total promedio minimo maximo num_transacciones
##         <dbl>    <dbl>  <dbl>  <dbl>             <int>
## 1   48501426.    1996.   53.8  6278.             24299

Agrupaciones con group_by

Además de los 5 verbos básicos revisados, muchas veces podríamos aplicar las mismas operaciones por grupos. Para ello existe la función group_by(), que recibe un tibble y regresa un tibble agrupado al que se le aplicarán funciones por grupos, ya sea via summarise() o mutate().

Podemos agrupar las mismas condiciones del apartado anterior, agruparemos por Sucursal.

banco %>%
    group_by(Sucursal) %>% 
    summarise(monto_total=sum(Monto, na.rm=TRUE), 
              promedio=mean(Monto, na.rm=TRUE), 
              minimo=min(Monto, na.rm=TRUE), 
              maximo=max(Monto, na.rm=TRUE),
              num_transacciones=n())
## # A tibble: 5 × 6
##   Sucursal monto_total promedio minimo maximo num_transacciones
##      <dbl>       <dbl>    <dbl>  <dbl>  <dbl>             <int>
## 1       62    4986854.    1758.  114.   4576.              2838
## 2       85   24670195.    2048.   53.8  6230.             12044
## 3      267    6869578.    2064.   62.6  5701.              3329
## 4      443    8710627.    2079.  102.   6278.              4190
## 5      586    3264173.    1720.  149.   4649.              1898

Ejercicio propuesto: Muestre un tibble que resuma la media y la desviación estándar del tiempo de atención en segundos y el número de transacciones, por Nivel de “Satisfaccion” y Tipo de “Transaccion” de la Sucursal del Mall del Sol (No. 443) y ordénelas por el número de transacciones de forma descendente.

Ejercicio propuesto: Cargue las 2 hojas del archivo “banco”, con los nombres “sucursales” y “cajeros”. Corrija los tipos de datos con mutate().

Datos relacionales con dplyr

La mayor parte del tiempo, nos vienen muchas tablas de información que debemos combinar para responder a nuestras preguntas de interés, a esto se le llama datos relacionales.

Para trabajar con datos relacionales necesitas verbos que funcionen con pares de tablas y para esto existen tres familias de verbos diseñadas para trabajar con datos relacionales que son las 1) uniones de transformación, 2) uniones de filtro y 3) operaciones de conjuntos:

El lugar más común para encontrar datos relacionales es en un sistema relacional de administración de bases de datos (Relational Data Base Management System en inglés), un concepto que abarca casi todas las bases de datos modernas.

Una de las formas de mostrar las relaciones entre las diferentes tablas es mediante un diagrama:

Mutating joins

Las uniones de transformación (del inglés mutating joins) agregan nuevas variables a un data frame a partir de las observaciones coincidentes en otra tabla.

En las uniones de transformación tenemos algunos tipos de uniones:

Para entenderlas, vamos a crear los siguientes data frames:

df_1 <- data.frame(
    Nombre= c('Belén', 'Noé', 'Salvador', 'Anne', 'Pablo', 'Rafaela'),
    Edad = c(22,18,21,26,25,23),
    Ciudad= factor(c('Gye', 'Uio', 'Cue', 'Gye', 'Cue', 'Uio')) )
df_1
##     Nombre Edad Ciudad
## 1    Belén   22    Gye
## 2      Noé   18    Uio
## 3 Salvador   21    Cue
## 4     Anne   26    Gye
## 5    Pablo   25    Cue
## 6  Rafaela   23    Uio
df_2 <- data.frame(
    A= c('Ana', 'Belén','Jose', 'Anne'), 
    B= c(100,200,300,200),
    C= c('Soltera','Soltera','Casado','Divorciada'))
df_2
##       A   B          C
## 1   Ana 100    Soltera
## 2 Belén 200    Soltera
## 3  Jose 300     Casado
## 4  Anne 200 Divorciada

inner_join() muestra las observaciones cuyas claves coinciden en ambos conjuntos de datos:

df_1 %>% inner_join(df_2, by = c("Nombre"="A"))
##   Nombre Edad Ciudad   B          C
## 1  Belén   22    Gye 200    Soltera
## 2   Anne   26    Gye 200 Divorciada

left_join() muestra las observaciones del conjunto de la izquierda pero añade los datos del conjunto de la derecha, coincidan o no coincidan. Si no coinciden, se agregan con NA.

df_1 %>% left_join(df_2, by = c("Nombre"="A"))
##     Nombre Edad Ciudad   B          C
## 1    Belén   22    Gye 200    Soltera
## 2      Noé   18    Uio  NA       <NA>
## 3 Salvador   21    Cue  NA       <NA>
## 4     Anne   26    Gye 200 Divorciada
## 5    Pablo   25    Cue  NA       <NA>
## 6  Rafaela   23    Uio  NA       <NA>

right_join() muestra las observaciones del conjunto de la derecha pero añade los datos del conjunto de la izquierda, coincidan o no coincidan. Si no coinciden, se agregan con NA.

df_1 %>% right_join(df_2, by = c("Nombre"="A"))
##   Nombre Edad Ciudad   B          C
## 1  Belén   22    Gye 200    Soltera
## 2   Anne   26    Gye 200 Divorciada
## 3    Ana   NA   <NA> 100    Soltera
## 4   Jose   NA   <NA> 300     Casado

full_join() muestra las observaciones de ambos conjuntos de datos, cuando los datos no coinciden, se agregan con NA.

df_1 %>% full_join(df_2, by = c("Nombre"="A"))
##     Nombre Edad Ciudad   B          C
## 1    Belén   22    Gye 200    Soltera
## 2      Noé   18    Uio  NA       <NA>
## 3 Salvador   21    Cue  NA       <NA>
## 4     Anne   26    Gye 200 Divorciada
## 5    Pablo   25    Cue  NA       <NA>
## 6  Rafaela   23    Uio  NA       <NA>
## 7      Ana   NA   <NA> 100    Soltera
## 8     Jose   NA   <NA> 300     Casado

Filtering joins

Las uniones de filtro (del inglés filtering joins) filtran observaciones en un data frame con base en si coinciden o no con una observación de otra tabla.

semi_join() muestra las observaciones del conjunto de la izquierda que tiene coincidencias con el conjunto de la derecha:

df_1 %>% semi_join(df_2, by = c("Nombre"="A"))
##   Nombre Edad Ciudad
## 1  Belén   22    Gye
## 2   Anne   26    Gye

anti_join() muestra las observaciones del conjunto de la izquierda que no tiene coincidencias con el conjunto de la derecha:

df_1 %>% anti_join(df_2, by = c("Nombre"="A"))
##     Nombre Edad Ciudad
## 1      Noé   18    Uio
## 2 Salvador   21    Cue
## 3    Pablo   25    Cue
## 4  Rafaela   23    Uio

Set operations

Las operaciones de conjuntos (del inglés set operations) tratan las observaciones como elementos de un conjunto. Las comparaciones se realizan entre conjuntos que contengan las mismas columnas, pues se revisa cómo difieren las observaciones entre los conjuntos.

Para ejemplificar crearemos otro data frame:

df_3 <- data.frame(Nombre= c('Belén', 'Noé', 'María', 'Anne', 'Pablo'),
    Edad = c(22,18,21,26,25),
    Ciudad= factor(c('Cue', 'Uio', 'Cue', 'Gye', 'Cue'))
    )
df_3
##   Nombre Edad Ciudad
## 1  Belén   22    Cue
## 2    Noé   18    Uio
## 3  María   21    Cue
## 4   Anne   26    Gye
## 5  Pablo   25    Cue

intersect() devuelve las observaciones comunes entre ambos conjuntos:

intersect(df_1, df_3)
##   Nombre Edad Ciudad
## 1    Noé   18    Uio
## 2   Anne   26    Gye
## 3  Pablo   25    Cue

union() devuelve las observaciones únicas en ambos conjuntos, es decir que no see repiten en ningun de los dos conjuntos:

union(df_1, df_3)
##     Nombre Edad Ciudad
## 1    Belén   22    Gye
## 2      Noé   18    Uio
## 3 Salvador   21    Cue
## 4     Anne   26    Gye
## 5    Pablo   25    Cue
## 6  Rafaela   23    Uio
## 7    Belén   22    Cue
## 8    María   21    Cue

setdiff() devuelve las observaciones en el primer conjunto pero que son diferentes del segundo conjunto

setdiff(df_1, df_3)
##     Nombre Edad Ciudad
## 1    Belén   22    Gye
## 2 Salvador   21    Cue
## 3  Rafaela   23    Uio

Ejercicio propuesto: ¿Qué join necesitamos realizar en nuestros datos? Efectúalo.

Datos ordenados con tidyr

El paquete tidyr provee un conjunto de herramientas que te ayudarán a ordenar datos desordenados. El objetivo de este paquete es ayudarlo a crear datos ordenados. Los datos ordenados son datos donde:

  • Cada columna es una variable.
  • Cada fila es una observación.
  • Cada valor debe tener su propia celda.

Tidy data describe una forma estándar de almacenar datos que se utiliza siempre que sea posible en todo el tidyverse.

Puedes encontrar más información del paquete tidyr en tidyr.tidyverse.org

Reglas que hacen que un conjunto de datos sea ordenado

Reglas que hacen que un conjunto de datos sea ordenado

Los analistas de datos sostienen que cerca del 80% del tiempo que utilizan para analizar una data lo dedican a la limpieza de datos, no obstante, esto te da una ventaja porque vas a gastar mucho menos tiempo en resolver preguntas analíticas.

Para estudiar este paquete, utilizaremos algunas tablas cargadas en el paquete datos. A continuación se muestra la tabla1 que no tiene, cuya estructura cumple el criterio de ‘tidy data’.

library(datos)
tabla1
## # A tibble: 6 × 4
##   pais        anio  casos  poblacion
##   <chr>      <int>  <int>      <int>
## 1 Afganistán  1999    745   19987071
## 2 Afganistán  2000   2666   20595360
## 3 Brasil      1999  37737  172006362
## 4 Brasil      2000  80488  174504898
## 5 China       1999 212258 1272915272
## 6 China       2000 213766 1280428583

Pivotar datos anchos

pivot_wider() (pivotar a lo ancho) se usa cuando una observación aparece en múltiples filas. Por ejemplo, consideremos la tabla2: una observación es un país en un año, pero cada observación aparece en dos filas.

tabla2
## # A tibble: 12 × 4
##    pais        anio tipo          cuenta
##    <chr>      <int> <chr>          <int>
##  1 Afganistán  1999 casos            745
##  2 Afganistán  1999 población   19987071
##  3 Afganistán  2000 casos           2666
##  4 Afganistán  2000 población   20595360
##  5 Brasil      1999 casos          37737
##  6 Brasil      1999 población  172006362
##  7 Brasil      2000 casos          80488
##  8 Brasil      2000 población  174504898
##  9 China       1999 casos         212258
## 10 China       1999 población 1272915272
## 11 China       2000 casos         213766
## 12 China       2000 población 1280428583

Para ordenar esto, debemos identificar dos parámetros:

  • La columna desde la que obtener los nombres de las variables. En este caso corresponde a tipo.

  • La columna desde la que obtener los valores. En este caso corresponde a cuenta.

Una vez resuelto esto, podemos usar pivot_wider():

tabla2 %>%
  pivot_wider(names_from = tipo, values_from = cuenta)
## # A tibble: 6 × 4
##   pais        anio  casos  población
##   <chr>      <int>  <int>      <int>
## 1 Afganistán  1999    745   19987071
## 2 Afganistán  2000   2666   20595360
## 3 Brasil      1999  37737  172006362
## 4 Brasil      2000  80488  174504898
## 5 China       1999 212258 1272915272
## 6 China       2000 213766 1280428583

Lo que hemos hecho es esto:

Pivotar la tabla2 para un formato ‘ancho’ y ordenado

Pivotar la tabla2 para un formato ‘ancho’ y ordenado

Pivotar datos largos

pivot_longer() (pivotar a lo largo) se usa uando en un dataset los nombres de las columnas no representan nombres de variables, sino que representan los valores de una variable. Por ejemplo, consideremos la tabla4a: los nombres de las columnas 1999 y 2000 representan los valores de la variable año, los valores en las columnas 1999 y 2000 representan valores de la variable casos y cada fila representa dos observaciones en lugar de una.

tabla4a
## # A tibble: 3 × 3
##   pais       `1999` `2000`
##   <chr>       <int>  <int>
## 1 Afganistán    745   2666
## 2 Brasil      37737  80488
## 3 China      212258 213766

Para ordenar esto, debemos identificar tres parámetros:

  • El conjunto de columnas cuyos nombres son valores y no variables. En este ejemplo son las columnas 1999 y 2000.

  • El nombre de la variable cuyos valores forman los nombres de las columnas. Llamaremos a esto key (clave) y en este caso corresponde a anio.

  • El nombre de la variable cuyos valores están repartidos por las celdas. Llamaremos a esto value (valor) y en este caso corresponde al número de casos.

Una vez resuelto esto, podemos usar pivot_longer():

tabla4a %>%
    pivot_longer(cols = c(`1999`, `2000`), 
                 names_to = "anio", 
                 values_to = "casos")
## # A tibble: 6 × 3
##   pais       anio   casos
##   <chr>      <chr>  <int>
## 1 Afganistán 1999     745
## 2 Afganistán 2000    2666
## 3 Brasil     1999   37737
## 4 Brasil     2000   80488
## 5 China      1999  212258
## 6 China      2000  213766

Lo que hemos hecho es esto:

Pivotar la tabla4a para un formato ‘largo’ y ordenado

Pivotar la tabla4a para un formato ‘largo’ y ordenado