La visualización 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. A menudo 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. En este capítulo aprenderás cómo hacer todo eso (¡y más!), incluyendo cómo transformar tus datos utilizando el paquete dplyr y el uso de un nuevo conjunto de datos sobre salida de vuelos de la ciudad de Nueva York en el año 2013.
En este capítulo nos enfocaremos en cómo usar el paquete dplyr, otro miembro central del tidyverse. Ilustraremos las ideas clave con el dataset vuelos que está contenido en el paquete datos. Utilizaremos ggplot2 como ayuda para comprender los datos.
install.packages("datos")
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.1'
## (as 'lib' is unspecified)
library(datos)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
## ✓ tibble 3.1.6 ✓ dplyr 1.0.8
## ✓ tidyr 1.2.0 ✓ stringr 1.4.0
## ✓ readr 2.1.2 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Toma nota acerca del mensaje de conflictos que se imprime cuando cargas el paquete tidyverse. Te indica que dplyr sobrescribe algunas funciones de R base. Si deseas usar la versión base de estas funciones después de cargar dplyr, necesitarás usar sus nombres completos: \(stats::filter() y stats::lag()\).
Para explorar los verbos básicos de manipulación de datos de dplyr, usaremos vuelos. Este conjunto de datos contiene los 336, 776 vuelos que partieron de la ciudad de Nueva York durante el 2013. Los datos provienen del Departamento de Estadísticas de Transporte de los Estados Unidos y están documentados en ?vuelos.
?vuelos
vuelos
## # A tibble: 336,776 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## 7 2013 1 1 555 600 -5
## 8 2013 1 1 557 600 -3
## 9 2013 1 1 557 600 -3
## 10 2013 1 1 558 600 -2
## # … with 336,766 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
Es posible que observes que este conjunto de datos se imprime de una forma un poco diferente a otros que podrías haber utilizado en el pasado: solo muestra las primeras filas y todas las columnas que caben en tu pantalla. Para ver todo el conjunto de datos, puedes ejecutar View(vuelos) que abrirá el conjunto de datos en el visor de RStudio. En este caso se imprime de manera diferente porque es un tibble. Los tibbles son data frames, pero ligeramente ajustados para que funcionen mejor en el tidyverse. Por ahora, no necesitas preocuparte por las diferencias; hablaremos en más detalle de los tibbles en Manejar o domar datos.
También podrás haber notado la fila de tres (o cuatro) abreviaturas de letras debajo de los nombres de las columnas. Estos describen el tipo de cada variable:
Hay otros tres tipos comunes de variables que no se usan en este conjunto de datos, pero que encontrarás más adelante:
En este capítulo, aprenderás las cinco funciones clave de dplyr que te permiten resolver la gran mayoría de tus desafíos de manipulación de datos:
Estas seis funciones proporcionan los verbos para este lenguaje de manipulación de datos.
Todos los verbos funcionan de manera similar:
filter() te permite filtrar un subconjunto de observaciones según sus valores. El primer argumento es el nombre del data frame. El segundo y los siguientes argumentos son las expresiones que lo filtran. Por ejemplo, podemos seleccionar todos los vuelos del 1 de enero con:
filter(vuelos, mes == 1, dia == 1)
## # A tibble: 842 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## 7 2013 1 1 555 600 -5
## 8 2013 1 1 557 600 -3
## 9 2013 1 1 557 600 -3
## 10 2013 1 1 558 600 -2
## # … with 832 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
Cuando ejecutas esa línea de código, dplyr ejecuta la operación de filtrado y devuelve un nuevo data frame. Las funciones de dplyr nunca modifican su input, por lo que si deseas guardar el resultado, necesitarás usar el operador de asignación, <-:
ene1 <- filter(vuelos, mes == 1, dia == 1)
R imprime los resultados o los guarda en una variable. Si deseas hacer ambas cosas puedes escribir toda la línea entre paréntesis:
(dic25 <- filter(vuelos, mes == 12, dia == 25))
## # A tibble: 719 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 12 25 456 500 -4
## 2 2013 12 25 524 515 9
## 3 2013 12 25 542 540 2
## 4 2013 12 25 546 550 -4
## 5 2013 12 25 556 600 -4
## 6 2013 12 25 557 600 -3
## 7 2013 12 25 557 600 -3
## 8 2013 12 25 559 600 -1
## 9 2013 12 25 559 600 -1
## 10 2013 12 25 600 600 0
## # … with 709 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
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).
Cuando comienzas con R, el error más fácil de cometer es usar = en lugar de == cuando se busca igualdad. Cuando esto suceda, obtendrás un error informativo:
#filter(vuelos, mes = 1)
Hay otro problema común que puedes encontrar al usar ==: los números de coma flotante. ¡Estos resultados pueden sorprenderte!
sqrt(2)^2 == 2
## [1] FALSE
1 / 49 * 49 == 1
## [1] FALSE
Las computadoras usan aritmética de precisión finita (obviamente, no pueden almacenar una cantidad infinita de dígitos), así que recuerda que cada número que ves es una aproximación. En lugar de confiar en ==, usa near() (cercano, en inglés):
near(sqrt(2)^2, 2)
## [1] TRUE
near(1 / 49 * 49, 1)
## [1] TRUE
Si tienes múltiples argumentos para filter() estos se combinan con “y”: cada expresión debe ser verdadera para que una fila se incluya en el output. Para otros tipos de combinaciones necesitarás usar operadores Booleanos: & es “y”, | es “o”, y ! es “no”. La figura siguiente muestra el conjunto completo de operaciones Booleanas.
Operaciones booleanas
El siguiente código sirve para encontrar todos los vuelos que partieron en noviembre o diciembre:
filter(vuelos, mes == 11 | mes == 12)
## # A tibble: 55,403 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 11 1 5 2359 6
## 2 2013 11 1 35 2250 105
## 3 2013 11 1 455 500 -5
## 4 2013 11 1 539 545 -6
## 5 2013 11 1 542 545 -3
## 6 2013 11 1 549 600 -11
## 7 2013 11 1 550 600 -10
## 8 2013 11 1 554 600 -6
## 9 2013 11 1 554 600 -6
## 10 2013 11 1 554 600 -6
## # … with 55,393 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
El orden de las operaciones no funciona como en español. No puedes escribir filter(vuelos, mes == (11 | 12)), que literalmente puede traducirse como “encuentra todos los vuelos que partieron en noviembre o diciembre”. En cambio, encontrará todos los meses que son iguales a 11 | 12, una expresión que resulta en ‘TRUE’ (verdadero). En un contexto numérico (como aquí), ‘TRUE’ se convierte en uno, por lo que encuentra todos los vuelos en enero, no en noviembre o diciembre. ¡Esto es bastante confuso!
Una manera rápida y útil para resolver este problema es x %in% y (es decir, x en y). Esto seleccionará cada fila donde x es uno de los valores en y. Podríamos usarlo para reescribir el código de arriba:
nov_dic <- filter(vuelos, mes %in% c(11, 12))
nov_dic
## # A tibble: 55,403 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 11 1 5 2359 6
## 2 2013 11 1 35 2250 105
## 3 2013 11 1 455 500 -5
## 4 2013 11 1 539 545 -6
## 5 2013 11 1 542 545 -3
## 6 2013 11 1 549 600 -11
## 7 2013 11 1 550 600 -10
## 8 2013 11 1 554 600 -6
## 9 2013 11 1 554 600 -6
## 10 2013 11 1 554 600 -6
## # … with 55,393 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
A veces puedes simplificar subconjuntos complicados al recordar la ley de De Morgan: !(x & y) es lo mismo que !x | !y, y !(x | y) es lo mismo que !x & !y. Por ejemplo, si deseas encontrar vuelos que no se retrasaron (en llegada o partida) en más de dos horas, puedes usar cualquiera de los dos filtros siguientes:
filter(vuelos, !(atraso_llegada > 120 | atraso_salida > 120))
## # A tibble: 316,050 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## 7 2013 1 1 555 600 -5
## 8 2013 1 1 557 600 -3
## 9 2013 1 1 557 600 -3
## 10 2013 1 1 558 600 -2
## # … with 316,040 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
filter(vuelos , atraso_llegada <= 120, atraso_salida <= 120)
## # A tibble: 316,050 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## 7 2013 1 1 555 600 -5
## 8 2013 1 1 557 600 -3
## 9 2013 1 1 557 600 -3
## 10 2013 1 1 558 600 -2
## # … with 316,040 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
Siempre que empieces a usar en filter() expresiones complejas que tengan varias partes, considera convertirlas en variables explícitas. Eso hace que sea mucho más fácil verificar tu trabajo. Aprenderás cómo crear nuevas variables en breve.
Una característica importante de R que puede hacer que la comparación sea difícil son los valores faltantes, o NAs (del inglés “no disponibles”). NA representa un valor desconocido, lo que hace que los valores perdidos sean “contagiosos”: casi cualquier operación que involucre un valor desconocido también será desconocida.
NA > 5
## [1] NA
10 == NA
## [1] NA
NA + 10
## [1] NA
NA / 2
## [1] NA
El resultado más confuso es este:
NA == NA
## [1] NA
Es más fácil entender por qué esto es cierto con un poco más de contexto:
Sea x la edad de María. No sabemos qué edad tiene.
x <- NA
Sea y la edad de Juan. No sabemos qué edad tiene.
y <- NA
¿Tienen Juan y María la misma edad?
y==x
## [1] NA
¡No sabemos!
Si deseas determinar si falta un valor, usa \(is.na():\)
is.na(x)
## [1] TRUE
\(filter()\) solo incluye filas donde la condición es TRUE; excluye tanto los valores FALSE como NA. Si deseas conservar valores perdidos, solicitalos explícitamente:
df <- tibble(x = c(1, NA, 3))
filter(df, x > 1)
## # A tibble: 1 × 1
## x
## <dbl>
## 1 3
filter(df, is.na(x) | x > 1)
## # A tibble: 2 × 1
## x
## <dbl>
## 1 NA
## 2 3
Encuentra todos los vuelos que:
Tuvieron un retraso de llegada de dos o más horas
Volaron a Houston (IAH oHOU)
Fueron operados por United, American o Delta
Partieron en invierno del hemisferio sur (julio, agosto y septiembre)
Llegaron más de dos horas tarde, pero no salieron tarde
Se retrasaron por lo menos una hora, pero repusieron más de 30 minutos en vuelo
Partieron entre la medianoche y las 6 a.m. (incluyente)
Otra función de dplyr que es útil para usar filtros es \(between()\). ¿Qué hace? ¿Puedes usarla para simplificar el código necesario para responder a los desafíos anteriores?
¿Cuántos vuelos tienen datos faltantes en horario_salida? ¿Qué otras variables tienen valores faltantes? ¿Qué representan estas filas?
¿Por qué NA ^ 0 no es faltante? ¿Por qué NA | TRUE no es faltante? ¿Por qué FALSE & NA no es faltante? ¿Puedes descubrir la regla general? (¡NA * 0 es un contraejemplo complicado!)
\(arrange()\) funciona de manera similar a filter() excepto que en lugar de seleccionar filas, cambia su orden. La función toma un data frame y un conjunto de nombres de columnas (o expresiones más complicadas) para ordenar según ellas. Si proporcionas más de un nombre de columna, cada columna adicional se utilizará para romper empates en los valores de las columnas anteriores:
arrange(vuelos, anio, mes, dia)
## # A tibble: 336,776 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## 7 2013 1 1 555 600 -5
## 8 2013 1 1 557 600 -3
## 9 2013 1 1 557 600 -3
## 10 2013 1 1 558 600 -2
## # … with 336,766 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
Usa desc() para reordenar por una columna en orden descendente:
arrange(vuelos, desc(atraso_salida))
## # A tibble: 336,776 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 9 641 900 1301
## 2 2013 6 15 1432 1935 1137
## 3 2013 1 10 1121 1635 1126
## 4 2013 9 20 1139 1845 1014
## 5 2013 7 22 845 1600 1005
## 6 2013 4 10 1100 1900 960
## 7 2013 3 17 2321 810 911
## 8 2013 6 27 959 1900 899
## 9 2013 7 22 2257 759 898
## 10 2013 12 5 756 1700 896
## # … with 336,766 more rows, and 13 more variables: horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
## # vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
## # tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
## # fecha_hora <dttm>
Los valores faltantes siempre se ordenan al final:
df <- tibble(x = c(5, 2, NA))
arrange(df, x)
## # A tibble: 3 × 1
## x
## <dbl>
## 1 2
## 2 5
## 3 NA
arrange(df, desc(x))
## # A tibble: 3 × 1
## x
## <dbl>
## 1 5
## 2 2
## 3 NA