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.

library(datos)
names(vuelos)
##  [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"
library(tidyverse)
vuelos %>% 
  dplyr::select(horario_salida)
## # 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
vuelos %>% 
  dplyr::select(starts_with("horario"))
## # 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).

vuelos %>% 
  dplyr::select(ends_with("salida"))
## # 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
vuelos %>% 
  dplyr::select(contains("salida"))
## # 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
vuelos %>% 
  dplyr::select(contains("ue"))
## # 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

head(vuelos)
## # 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>
vuelos %>% 
    dplyr::select(distancia, aerolinea, everything())
## # 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

vuelos |> dplyr::select(mes:salida_programada)
## # 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
x=c(10:15)


x
## [1] 10 11 12 13 14 15
vuelos[,c(4:7)]
## # 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.


  1. 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

?mean

library(readr)
url_COVID_PR <- read_csv("Datos/url_COVID_PR.csv")

url_COVID_PR
## # 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.

# Escribe tu código aquí

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é?

# Escribe tu código aquí

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.

# Escribe tu código aquí

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.

# Escribe tu código aquí

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”.

# Escribe tu código aquí

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.

# Escribe tu código aquí

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.

# Escribe tu código aquí