select(): elegir y renombrar columnas con intención

r
tidyverse
Selección por nombre, por negación y por propiedad. Helpers (starts_with, where), renombrar al seleccionar y reordenar con relocate().

Por qué select() es el alias más útil de [

Si filter() te quita filas, select() te quita (o reordena) columnas. La operación es trivial en concepto pero tiene helpers que la mayoría de la gente no usa porque nunca los descubrió. Conocerlos ahorra mucho código.

En R base, seleccionar columnas se hace con [ o $:

ventas[, c("region", "fecha", "ingresos")]
ventas$region

select() añade dos cosas que el [ no tiene: composición con el pipe y un lenguaje de selectores expresivo.

La forma básica: nombres

ventas |>
  select(region, fecha, ingresos)

Los nombres van sin comillas. Es una de las cosas que choca al venir de otros lenguajes. El tidyverse aprovecha que R permite non-standard evaluation (NSE) para hacer esto legible. Lo veremos más en detalle cuando lleguemos a las funciones avanzadas. Por ahora basta con saber que es la convención y aceptarla.

Negación: quitar columnas

ventas |>
  select(-id_interno, -timestamp_modificado)

El - quita la columna. Útil cuando es más corto especificar las que NO quieres que las que sí.

ventas |>
  select(!c(id_interno, timestamp_modificado))

Equivalente con !c(...). Práctico cuando combinas con helpers (siguiente sección).

Helpers: el verdadero superpoder

Estos selectores son lo que [ no puede hacer fácilmente:

ventas |>
  select(starts_with("ingresos_"))   # ingresos_neto, ingresos_bruto, ingresos_2024

ventas |>
  select(ends_with("_2024"))         # cualquier columna que acabe así

ventas |>
  select(contains("region"))         # cualquier columna con "region" en el nombre

ventas |>
  select(matches("^cod_[A-Z]{3}$"))  # regex completa

ventas |>
  select(num_range("Q", 1:4))        # Q1, Q2, Q3, Q4

Y el más infrautilizado: where() selecciona columnas que cumplen una función:

ventas |>
  select(where(is.numeric))          # todas las columnas numéricas

ventas |>
  select(where(\(x) is.numeric(x) && mean(x, na.rm = TRUE) > 0))

where() cambia el juego: ya no eliges por nombre sino por propiedad. Es la herramienta que conviertes en hábito en cuanto trabajas con datasets donde los nombres de columna no son del todo predecibles.

Renombrar al seleccionar

select() también renombra:

ventas |>
  select(region, fecha_venta = fecha, total = ingresos_brutos)

Sintaxis: nuevo_nombre = nombre_actual. Es una forma compacta de hacer dos operaciones en una.

Si solo quieres renombrar sin filtrar columnas, usa rename():

ventas |>
  rename(fecha_venta = fecha, total = ingresos_brutos)

select() quita las columnas no mencionadas. rename() las conserva todas. La distinción importa: si tu pipeline parece haber perdido columnas, probablemente alguien usó select() cuando quería rename().

everything() para reordenar

ventas |>
  select(id_venta, fecha, region, everything())

everything() significa “todas las demás, en su orden actual”. Útil para “pongo estas columnas al principio y dejo el resto como esté”.

relocate(): el verbo dedicado al orden

Si lo único que quieres es cambiar el orden sin filtrar nada:

ventas |>
  relocate(fecha, .before = region)

ventas |>
  relocate(id_venta, .after = last_col())

ventas |>
  relocate(where(is.character), .after = where(is.numeric))

relocate() es select() puro orden, más expresivo cuando esa es la intención real, y no se arriesga a perder columnas por accidente.

Trampas habituales

  • select(-c(a, b, c)) requiere -c(...) con c() explícito. Sin el c(), R interpreta -a - b - c como restas. La sintaxis con c() es más segura y se lee mejor.
  • Usar select() cuando lo que quieres es rename(). Si renombras una columna y no nombras las demás, las pierdes silenciosamente. Diagnóstico habitual.
  • starts_with("X") es case-sensitive por defecto. starts_with("X", ignore.case = TRUE) lo cambia. Lo mismo para ends_with y contains.
  • where() aplicada a tibbles muy anchos puede ser lenta porque evalúa la función columna por columna. Para is.numeric() no se nota. Para predicados costosos sobre 10.000 columnas, sí.

En la siguiente entrega

Has aprendido a quedarte con las filas y columnas que quieres. El siguiente paso natural es crear columnas nuevas: aplicar transformaciones, calcular ratios, recodificar categorías. Ese es el verbo mutate(), y es el más versátil del conjunto.