6. dplyr I (select/filter)

1 Gramática dplyr I: select, pipe (%>%) y filter

Objetivo: seleccionar columnas de forma flexible, encadenar pasos con pipes y filtrar filas con condiciones lógicas claras.

Tip

Carga tidyverse (incluye dplyr, tidyr, ggplot2, etc.).
Si BaseSal no existe, el bloque la crea desde Salaries.

Code
library(tidyverse)
Warning: package 'tidyverse' was built under R version 4.2.2
Warning: package 'ggplot2' was built under R version 4.2.3
Warning: package 'tibble' was built under R version 4.2.3
Warning: package 'tidyr' was built under R version 4.2.2
Warning: package 'purrr' was built under R version 4.2.2
Warning: package 'stringr' was built under R version 4.2.3
Warning: package 'forcats' was built under R version 4.2.2
Warning: package 'lubridate' was built under R version 4.2.2
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Code
# Si por cualquier motivo BaseSal no existe, lo recreamos:
if (!exists("BaseSal")) {
  if (!exists("Salaries")) {
    library(readxl)
    Salaries <- readxl::read_excel(ruta_salaries)  
  }
  BaseSal <- dplyr::select(Salaries, dplyr::any_of(c("rank","sex","yrs.service","salary")))
}

1.1 Pipe %>% para leer de arriba a abajo

La pipe conecta pasos: la salida de una línea es la entrada de la siguiente. Lee como “toma esto → luego esto → luego esto”.

Code
# Sin pipe (anidado):
head(select(filter(BaseSal, sex == "Male"), rank, sex, salary))
Code
# Con pipe: más legible
BaseSal %>%
  filter(sex == "Male") %>%
  select(rank, sex, salary) %>%
  head()
Note

En R reciente puedes usar la pipe nativa |>. Con dplyr verás más a menudo %>%.

1.2 select(): elegir columnas (y reorganizar)

Code
#| label: s6-select
#| echo: true

# Selección explícita
BaseSal %>% select(rank, sex, salary) %>% head()
Code
# Helpers útiles:
BaseSal %>% select(starts_with("yr")) %>% head()     # empieza por "yr"
Code
BaseSal %>% select(ends_with("ice")) %>% head()      # termina en "ice"
Code
BaseSal %>% select(contains("sal")) %>% head()       # contiene "sal"
Code
BaseSal %>% select(matches("^(rank|sex)$")) %>% head()  # regex
Code
BaseSal %>% select(where(is.numeric)) %>% head()     # solo numéricas
Code
# Renombrar a la vez que seleccionas
BaseSal %>%
  select(cargo = rank, genero = sex, antiguedad = `yrs.service`, salario = salary) %>%
  head()
Code
# Reubicar columnas (que 'salary' quede primero)
BaseSal %>% relocate(salary, .before = 1) %>% head()
Code
# Mantener todo pero mover 'sex' al final:
BaseSal %>% relocate(sex, .after = last_col()) %>% head()
Warning

Si un nombre no existe, select() falla. Usa any_of() para no fallar: select(BaseSal, any_of(c(“rank”,“no_existe”))).

1.3 filter(): condiciones, & / |, %in%, between(), is.na()

Code
# Igualdad, distinto, mayor/menor
BaseSal %>% filter(sex == "Male") %>% head()
Code
BaseSal %>% filter(sex != "Female") %>% head()
Code
# AND (&) y OR (|)
BaseSal %>% filter(sex == "Female" & salary > 100000) %>% head()
Code
BaseSal %>% filter(sex == "Female" | salary > 150000) %>% head()
Code
# %in% para varios valores (como IN en SQL)
BaseSal %>% filter(rank %in% c("Prof","AssocProf")) %>% head()
Code
# Rangos numéricos: between(x, izq, der) incluye extremos
# (si tu columna de antigüedad se llama 'yrs.service', ajusta el nombre)
colnames(BaseSal)
[1] "rank"        "sex"         "yrs.service" "salary"     
Code
# Intentamos adivinar el nombre correcto de antigüedad:
nombre_ant <- intersect(c("yrs.service","yrs_service","years_service"), names(BaseSal))
if (length(nombre_ant) == 1) {
  BaseSal %>%
    filter(dplyr::between(.data[[nombre_ant]], 10, 20)) %>%
    select(rank, sex, !!nombre_ant, salary) %>%
    head()
}
Code
# NA: filtra no faltantes / faltantes
BaseSal %>% filter(!is.na(salary)) %>% head()
Code
BaseSal %>% filter(is.na(salary))  %>% head()
Tip

Texto: usa stringr::str_detect(col, “patrón”) para filtrar por patrones. Ej.: filter(str_detect(rank, “Prof”)) mantiene todos los que contengan “Prof”.

1.4 arrange() y distinct(): ordenar y quitar duplicados

Code
# Orden ascendente por salary
BaseSal %>% arrange(salary) %>% head()
Code
# Orden descendente
BaseSal %>% arrange(desc(salary)) %>% head()
Code
# Orden múltiple: primero por rank, luego salary desc
BaseSal %>% arrange(rank, desc(salary)) %>% head(10)
Code
# Filas únicas (en todas las columnas actuales)
BaseSal %>% distinct() %>% head()
Code
# Únicas por subset (combinación de rank y sex)
BaseSal %>% distinct(rank, sex, .keep_all = TRUE) %>% head()

1.5 Ejercicios

  1. Crea Base_gte100 con filas donde salary >= 100000 y solo columnas rank, sex, salary. Ordénala de mayor a menor salario.

  2. Deja únicamente las filas únicas por rank y sex manteniendo todas las columnas (pista: .keep_all = TRUE).

  3. Selecciona todas las columnas numéricas y, a partir de esa base, quédate con filas donde ninguna es NA (complete.cases).

  4. (Si tu columna de antigüedad existe) crea Base_10_20 con filas donde la antigüedad esté entre 10 y 20 (incluyente) y muéstrala ordenada por rank y salary descendente.

Code
#| label: s6-ejercicios
#| echo: true

# 1) salary >= 100000, columnas puntuales, ordenar desc
Base_gte100 <- BaseSal %>%
  select(rank, sex, salary) %>%
  filter(salary >= 100000) %>%
  arrange(desc(salary))
head(Base_gte100)
Code
# 2) únicas por rank + sex
Base_unique <- BaseSal %>%
  distinct(rank, sex, .keep_all = TRUE)
Base_unique
Code
# 3) solo columnas numéricas y filas completas
Base_num_complete <- BaseSal %>%
  select(where(is.numeric)) %>%
  filter(complete.cases(.))
head(Base_num_complete)
Code
# 4) antigüedad entre 10 y 20 si la columna existe
nombre_ant <- intersect(c("yrs.service","yrs_service","years_service"), names(BaseSal))
if (length(nombre_ant) == 1) {
  Base_10_20 <- BaseSal %>%
    filter(dplyr::between(.data[[nombre_ant]], 10, 20)) %>%
    arrange(rank, desc(salary))
  head(Base_10_20)
}