Capítulo9 Transformación: Seleccionar variables
Fecha de la última revisión
## [1] "2026-09-09"
9.1 Funciones en este módulo
- starts_with() # selecciona variables que empiezan con una palabra o letra
- ends_with() # selecciona variables que terminan con una palabra o letra
- rename() # cambiar el nombre de una variable
- contains() # selecciona variables que contienen una palabra o letra
- everything() # selecciona todas las variables
- if_else() # reemplaza valores en una columna basado en una condición
Es una función para seleccionar basado en una características del nombre de la columna
Con once columnas, escribir los nombres a mano no cuesta nada. Con las 19 de
vuelos ya empieza a cansar, y con las doscientas de una tabla de expresión
génica o de un censo es sencillamente inviable. Para eso están los ayudantes de
selección: en vez de nombrar las columnas una por una, se describe qué tienen
en común sus nombres.
Hay dos ventajas menos obvias, y son las que importan de verdad. La primera es que
el código se explica solo: select(starts_with("atraso")) dice qué se quiere,
mientras que una lista de cinco nombres obliga a mirarlos uno por uno para
entenderla. La segunda es que se adapta: si mañana el archivo trae una columna
nueva que también empieza por atraso, el ayudante la incluye sin que haya que
tocar el código, y la lista escrita a mano se queda corta en silencio.
Los ayudantes de select(): describir columnas en vez de nombrarlas.
starts_with("x")/ends_with("x"): por el principio o el final del nombre.contains("x"): el texto aparece en cualquier parte del nombre.matches("expresión"): para casos que las anteriores no cubren, con expresiones regulares.everything(): todas las columnas que quedan; se usa al final para reordenar.where(is.numeric): selecciona por el tipo de la columna, no por su nombre. Muy útil antes de un cálculo.last_col(),num_range("x", 1:3): por posición y por nombres numerados.
Los ayudantes como starts_with(), ends_with() y contains() te evitan escribir cada nombre de columna a mano, sobre todo en tablas con muchas variables.
## [1] "anio" "mes" "dia"
## [4] "horario_salida" "salida_programada" "atraso_salida"
## [7] "horario_llegada" "llegada_programada" "atraso_llegada"
## [10] "aerolinea" "vuelo" "codigo_cola"
## [13] "origen" "destino" "tiempo_vuelo"
## [16] "distancia" "hora" "minuto"
## [19] "fecha_hora"
## # A tibble: 336,776 × 1
## horario_salida
## <int>
## 1 517
## 2 533
## 3 542
## 4 544
## 5 554
## 6 554
## 7 555
## 8 557
## 9 557
## 10 558
## # ℹ 336,766 more rows
## # A tibble: 336,776 × 2
## horario_salida horario_llegada
## <int> <int>
## 1 517 830
## 2 533 850
## 3 542 923
## 4 544 1004
## 5 554 812
## 6 554 740
## 7 555 913
## 8 557 709
## 9 557 838
## 10 558 753
## # ℹ 336,766 more rows
starts_with() no distingue mayúsculas de minúsculas por defecto, a diferencia de
lo que pasa en las comparaciones con ==. Si necesitas que las distinga, hay que
pedirlo con starts_with("Horario", ignore.case = FALSE).
## # A tibble: 336,776 × 2
## horario_salida atraso_salida
## <int> <dbl>
## 1 517 2
## 2 533 4
## 3 542 2
## 4 544 -1
## 5 554 -6
## 6 554 -4
## 7 555 -5
## 8 557 -3
## 9 557 -3
## 10 558 -2
## # ℹ 336,766 more rows
## # A tibble: 336,776 × 3
## horario_salida salida_programada atraso_salida
## <int> <int> <dbl>
## 1 517 515 2
## 2 533 529 4
## 3 542 540 2
## 4 544 545 -1
## 5 554 600 -6
## 6 554 558 -4
## 7 555 600 -5
## 8 557 600 -3
## 9 557 600 -3
## 10 558 600 -2
## # ℹ 336,766 more rows
## # A tibble: 336,776 × 2
## vuelo tiempo_vuelo
## <int> <dbl>
## 1 1545 227
## 2 1714 227
## 3 1141 160
## 4 725 183
## 5 461 116
## 6 1696 150
## 7 507 158
## 8 5708 53
## 9 79 140
## 10 301 138
## # ℹ 336,766 more rows
Compara los tres ayudantes con el mismo texto. ends_with("salida") devuelve las
columnas que terminan en esa palabra; contains("salida") devuelve esas y
además las que la llevan en medio o al principio. contains() es el más amplio y
por eso el más arriesgado: contains("ue") selecciona columnas por una
coincidencia de dos letras que no significa nada, y es un buen recordatorio de que
conviene mirar el resultado antes de seguir.
Cuando uses un ayudante por primera vez sobre una tabla, corre el select() solo,
sin encadenar nada más, y mira los nombres que salieron. Toma dos segundos y evita
descubrir tres pasos después que se coló una columna que no debía.
#vuelos %>%
# dplyr::filter(contains("AA")) # no funciona con filter
vuelos %>%
dplyr::filter(aerolinea %in% c("AA", "DL"))## # A tibble: 80,839 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 542 540 2
## 2 2013 1 1 554 600 -6
## 3 2013 1 1 558 600 -2
## 4 2013 1 1 559 600 -1
## 5 2013 1 1 602 610 -8
## 6 2013 1 1 606 610 -4
## 7 2013 1 1 606 610 -4
## 8 2013 1 1 615 615 0
## 9 2013 1 1 623 610 13
## 10 2013 1 1 628 630 -2
## # ℹ 80,829 more rows
## # ℹ 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>
La línea comentada de arriba es importante: los ayudantes de selección solo
funcionan dentro de select() (y de funciones emparentadas como across() o
rename_with()), nunca dentro de filter(). La razón es que hablan de nombres
de columnas, mientras que filter() trabaja con valores dentro de las
columnas. Son dos cosas distintas y no se mezclan.
Para buscar un texto dentro de los valores está str_detect(), del paquete
stringr, que tiene su propio capítulo:
filter(str_detect(aerolinea, "^A")).
9.2 rename()
rename(nuevo = viejo): cambiar el nombre de columnas.
- Qué hace: renombra columnas; el nombre nuevo va primero, el original después.
- Relacionadas:
select(nuevo = viejo, ...)renombra y selecciona a la vez;everything()reordena poniendo unas columnas al frente y el resto después.
Cambiar el nombre de la columna
vuelos %>%
rename(aeropuerto_origen=origen) %>%
rename(aeropuerto_destino=destino) %>%
dplyr::select(contains("aeropuerto")) # nombre nuevo= nombre original## # A tibble: 336,776 × 2
## aeropuerto_origen aeropuerto_destino
## <chr> <chr>
## 1 EWR IAH
## 2 LGA IAH
## 3 JFK MIA
## 4 JFK BQN
## 5 LGA ATL
## 6 EWR ORD
## 7 EWR FLL
## 8 LGA IAD
## 9 JFK MCO
## 10 LGA ORD
## # ℹ 336,766 more rows
El orden de rename() sorprende a todo el mundo la primera vez: el nombre nuevo
va a la izquierda del igual y el viejo a la derecha, al revés de como se lee la
frase “renombrar origen a aeropuerto_origen”. La manera de recordarlo es pensar
que se está creando algo nuevo a partir de algo viejo, igual que en
mutate(velocidad = distancia / tiempo): a la izquierda siempre va lo que se
produce.
Los dos rename() encadenados se pueden juntar en uno solo, separando por comas:
rename(aeropuerto_origen = origen, aeropuerto_destino = destino). Y si hay que
renombrar muchas de golpe, rename_with() aplica una función a todos los nombres:
rename_with(toupper) los pone en mayúsculas.
select() también renombra: select(aeropuerto_origen = origen) selecciona y
renombra en un solo paso. La diferencia es que select() se queda solo con lo
que nombras y rename() conserva todas las columnas.
9.3 Reorganizar el orden de las columnas, usando select() y everything() en la misma función
## # A tibble: 6 × 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
## # ℹ 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 tibble: 336,776 × 19
## distancia aerolinea anio mes dia horario_salida salida_programada
## <dbl> <chr> <int> <int> <int> <int> <int>
## 1 1400 UA 2013 1 1 517 515
## 2 1416 UA 2013 1 1 533 529
## 3 1089 AA 2013 1 1 542 540
## 4 1576 B6 2013 1 1 544 545
## 5 762 DL 2013 1 1 554 600
## 6 719 UA 2013 1 1 554 558
## 7 1065 B6 2013 1 1 555 600
## 8 229 EV 2013 1 1 557 600
## 9 944 B6 2013 1 1 557 600
## 10 733 AA 2013 1 1 558 600
## # ℹ 336,766 more rows
## # ℹ 12 more variables: atraso_salida <dbl>, horario_llegada <int>,
## # llegada_programada <int>, atraso_llegada <dbl>, vuelo <int>,
## # codigo_cola <chr>, origen <chr>, destino <chr>, tiempo_vuelo <dbl>,
## # hora <dbl>, minuto <dbl>, fecha_hora <dttm>
Este es el idioma para reordenar columnas sin escribirlas todas: se nombran
las que se quieren al frente y se cierra con everything(), que recoge el resto
en el orden en que estaban. Es muy útil cuando una tabla ancha tiene la columna
identificadora perdida en la posición doce.
dplyr tiene además una función dedicada a esto, relocate(), que hace lo mismo
de forma más explícita y permite colocar columnas en medio:
relocate(distancia, aerolinea) las lleva al frente, y
relocate(distancia, .after = aerolinea) la coloca justo después de otra.
Seleccionar de un conjunto de variables en orden que aparece en el data.frame
## # A tibble: 336,776 × 4
## mes dia horario_salida salida_programada
## <int> <int> <int> <int>
## 1 1 1 517 515
## 2 1 1 533 529
## 3 1 1 542 540
## 4 1 1 544 545
## 5 1 1 554 600
## 6 1 1 554 558
## 7 1 1 555 600
## 8 1 1 557 600
## 9 1 1 557 600
## 10 1 1 558 600
## # ℹ 336,766 more rows
## [1] 10 11 12 13 14 15
## # A tibble: 336,776 × 4
## horario_salida salida_programada atraso_salida horario_llegada
## <int> <int> <dbl> <int>
## 1 517 515 2 830
## 2 533 529 4 850
## 3 542 540 2 923
## 4 544 545 -1 1004
## 5 554 600 -6 812
## 6 554 558 -4 740
## 7 555 600 -5 913
## 8 557 600 -3 709
## 9 557 600 -3 838
## 10 558 600 -2 753
## # ℹ 336,766 more rows
Este bloque compara dos maneras de seleccionar por posición.
select(mes:salida_programada) usa los nombres con dos puntos, que se lee
“desde esta columna hasta esta otra”. vuelos[, c(4:7)] usa los números de
columna con la notación de corchetes de R base.
Las dos funcionan, y la primera es mucho mejor por una razón práctica: si el archivo cambia y alguien añade una columna al principio, la selección por nombres sigue trayendo lo mismo y la selección por números trae otras columnas, sin error y sin aviso. Cualquier código que dependa de la posición de una columna es frágil.
En la notación de corchetes, la coma no es opcional y su lado importa:
vuelos[, 4:7] selecciona columnas (lo de después de la coma) y
vuelos[4:7, ] selecciona filas. Olvidar la coma o ponerla del lado
equivocado devuelve algo perfectamente válido y completamente distinto de lo que
querías. Es otra razón para preferir select() y filter(), que dicen en su
propio nombre qué seleccionan.
- Ejercicios:
Hacer los ejercicios en la sección 5.4.1 del libro en español
https://es.r4ds.hadley.nz/05-transform.html#ejercicios-2
## # A tibble: 587 × 18
## ...1 Fecha Muertes IncrementoMuertes CasosPCR_Salud IncCasosPCR_Salud
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1 3/12/20 0 0 NA NA
## 2 2 3/13/20 0 0 NA NA
## 3 3 3/14/20 0 0 NA NA
## 4 4 3/15/20 0 0 NA NA
## 5 5 3/16/20 0 0 NA NA
## 6 6 3/17/20 0 0 NA NA
## 7 7 3/18/20 0 0 NA NA
## 8 8 3/19/20 0 0 NA NA
## 9 9 3/20/20 0 0 NA NA
## 10 10 3/21/20 0 0 NA NA
## # ℹ 577 more rows
## # ℹ 12 more variables: CasosSaludNuevo <dbl>, IncCasosSaludNuevo <dbl>,
## # HospitCOV19 <dbl>, CamasICU <dbl>, CamasICU_disp <dbl>, Ventiladores <dbl>,
## # MuertesSalud <dbl>, IncMueSalud <dbl>, VacDoses <dbl>, VacAdm <dbl>,
## # N1MoreDoses <dbl>, N2Doses <dbl>
Un ejemplo con datos reales de Puerto Rico. Nota la ruta relativa,
"Datos/url_COVID_PR.csv", que funciona porque el documento vive dentro del
proyecto.
library(tidyverse)
df2=url_COVID_PR %>%
dplyr::select(IncCasosSaludNuevo) %>%
mutate(Cambios_Casos=IncCasosSaludNuevo-lag(IncCasosSaludNuevo,7))
df2## # A tibble: 587 × 2
## IncCasosSaludNuevo Cambios_Casos
## <dbl> <dbl>
## 1 2 NA
## 2 3 NA
## 3 3 NA
## 4 0 NA
## 5 9 NA
## 6 7 NA
## 7 6 NA
## 8 5 3
## 9 14 11
## 10 12 9
## # ℹ 577 more rows
lag() toma el valor de una fila anterior: lag(x, 7) devuelve, para cada
fila, el valor que había siete filas antes. Restarlo del valor actual da el cambio
respecto a la semana pasada, que es la manera habitual de mirar una serie diaria
con ciclo semanal, porque compara lunes con lunes y domingo con domingo en vez de
comparar contra el día anterior.
Las primeras siete filas salen NA, y tiene que ser así: para el primer día no
hay una semana anterior con la que comparar. Es un NA legítimo, no un error.
lag() y su pareja lead() se trabajan a fondo en el capítulo de mutate().
lag() va por posición en la tabla, no por fecha. Si las filas no están
ordenadas cronológicamente, o si faltan días, la resta compara lo que no debe. La
costumbre es arrange(fecha) antes de cualquier lag(), y comprobar que no
falten fechas en medio.
9.4 Ejercicios del capítulo
Sobre los ejercicios de este capítulo. Cada ejercicio se entrega con tres
cosas: el script, el resultado (que el bloque corra) y una explicación
en palabras. Los ejercicios usan millas del paquete datos, no vuelos, que
es el conjunto con el que se enseña en el capítulo. Las once columnas de millas
son: fabricante, modelo, cilindrada, anio, cilindros, transmision,
traccion, ciudad, autopista, combustible y clase. La hoja completa para
entregar está en Ejercicios/Ejercicios_Capitulo_09_seleccionar.Rmd.
9.4.1 Ejercicio 1. Por el principio del nombre
Datos: millas. Selecciona todas las columnas cuyo nombre empiece por la
letra c.
Verificación: deben salir exactamente 5 columnas.
En palabras: escribe también la versión con los cinco nombres a mano. ¿Cuál de
las dos prefieres y por qué? Da una razón que tenga que ver con lo que pasaría si
mañana el archivo trajera una columna nueva llamada consumo.
9.4.2 Ejercicio 2. Por el final y por dentro
Datos: millas. Selecciona las columnas que terminen en "a", y después las
que contengan "cil".
Verificación: la primera selección devuelve 2 columnas y la segunda también 2, pero no son las mismas dos.
En palabras: explica la diferencia entre ends_with() y contains() usando
tus dos resultados como ejemplo. ¿Cuál de los dos podría traerte una columna que
no querías, y por qué?
9.4.3 Ejercicio 3. Seleccionar por tipo
Datos: millas. Selecciona todas las columnas numéricas usando
where(is.numeric), y después todas las de texto con where(is.character).
Verificación: deben salir 5 numéricas y 6 de texto, y 5 más 6 tienen
que dar las 11 columnas de millas.
En palabras: anio y cilindros quedaron entre las numéricas. ¿Te sirve esa
selección si lo que querías era “las variables sobre las que tiene sentido sacar
un promedio”? Explica.
9.4.4 Ejercicio 4. Renombrar
Datos: millas. Renombra ciudad a consumo_ciudad y autopista a
consumo_autopista, en una sola llamada a rename(). Después comprueba con
contains("consumo") que los dos nombres nuevos existen.
Verificación: la tabla renombrada tiene que seguir teniendo 11 columnas, y
el select(contains("consumo")) tiene que devolver 2.
En palabras: en rename(), ¿va primero el nombre nuevo o el viejo? Explica
cómo lo recuerdas, y qué error te da R si los pones al revés.
9.4.5 Ejercicio 5. Reordenar
Datos: millas. Reordena las columnas para que clase y fabricante queden al
frente y el resto conserve su orden original, usando everything(). Después haz
lo mismo con relocate().
Verificación: las dos versiones tienen que devolver 11 columnas en el
mismo orden, y las dos primeras tienen que ser clase y fabricante.
Compruébalo comparando names() de las dos tablas.
En palabras: ¿qué habría pasado si hubieras escrito
select(clase, fabricante) sin everything()? Explica en qué se diferencian
“seleccionar” y “reordenar”.
9.4.6 Ejercicio 6. Nombres frente a posiciones
Datos: millas. Selecciona las columnas de la tercera a la sexta de dos maneras:
con select() usando los nombres y los dos puntos, y con la notación de corchetes
de R base usando los números.
Verificación: las dos tienen que devolver las mismas 4 columnas.
Compruébalo con identical() sobre los names() de ambas.
En palabras: imagina que alguien añade al principio de millas una columna
id. ¿Cuál de tus dos versiones seguiría trayendo las mismas variables y cuál no?
Explica por qué eso hace que una de las dos sea peligrosa en un análisis que se
repite cada semestre.
9.4.7 Ejercicio 7 (BONO). Quitar en vez de escoger
Datos: millas. Construye una tabla que tenga todas las columnas menos
modelo y transmision, usando el signo de menos dentro de select(). Después
construye otra que quite de golpe todas las que empiecen por c.
Verificación: la primera tabla tiene que tener 9 columnas y la segunda 6.
En palabras: el signo de menos dentro de select() no resta nada. Explica qué
hace realmente, y por qué select(millas, -c(modelo, transmision)) y
select(millas, -modelo, -transmision) dan lo mismo.