Cheatsheet R
Linguagem para estatística, análise de dados e visualização
R
Básico
Variables y asignación
# Asignación (forma preferida) x <- 10 nombre <- "Ana" # Otras formas y = 20 # también funciona 30 -> z # hacia la derecha (raro) # Constantes integradas pi # 3.141593 LETTERS # "A"..."Z" letters # "a"..."z" # Eliminar objetos de la memoria rm(x) rm(list = ls()) # limpiar todo
En R, la asignación se hace sobre todo con el operador <-. El signo = también funciona, pero por convención queda reservado a los argumentos de funciones. rm() elimina objetos de la memoria.
Operaciones vectorizadas
v <- c(10, 20, 30, 40, 50) # Aritmética (elemento a elemento) v * 2 # 20,40,60,80,100 v + c(1, 2, 3, 4, 5) v ^ 2 # Estadísticas sum(v) # 150 mean(v) # 30 median(v) # 30 sd(v) # desviación estándar min(v); max(v) range(v) # mínimo y máximo # Lógicos any(v > 40) # TRUE all(v > 0) # TRUE which(v > 25) # posiciones: 3 4 5
Las operaciones son vectorizadas: se aplican a todos los elementos de una vez, sin necesitar bucles. Funciones como sum(), mean() y sd() resumen el vector.
Valores especiales
# NA: valor faltante (missing) x <- c(1, NA, 3) is.na(x) # FALSE TRUE FALSE mean(x) # NA mean(x, na.rm = TRUE) # 2 # NULL: ausencia de valor (vacío) y <- NULL is.null(y) # TRUE length(y) # 0 # NaN e Inf 0 / 0 # NaN (not a number) 1 / 0 # Inf (infinito) is.nan(0 / 0) # TRUE is.finite(Inf) # FALSE
NA representa un dato faltante y se propaga en los cálculos (usa na.rm = TRUE para ignorarlo). NULL es la ausencia de valor. NaN e Inf surgen de operaciones matemáticas inválidas.
Tipos de datos
# Tipos atómicos
42L # integer
3.14 # double (numeric)
"texto" # character
TRUE # logical
1 + 2i # complex
# Ver el tipo
class(x) # clase
typeof(x) # tipo interno
is.numeric(x) # TRUE/FALSE
is.character(x)
# Convertir
as.numeric("42") # 42
as.character(42) # "42"
as.integer(3.7) # 3
as.logical(1) # TRUELos seis tipos atómicos son integer, double, character, logical, complex y raw. Usa class() y typeof() para inspeccionarlos y as.*() para convertir.
Strings
s <- "Hola Mundo"
# Unir texto
paste("Hola", "Mundo") # "Hola Mundo"
paste0("a", "b") # "ab" (sin espacio)
paste(1:3, collapse = ", ") # "1, 2, 3"
# Formatear
sprintf("%.2f", pi) # "3.14"
sprintf("%s tiene %d", "Ana", 30)
# Manipular
nchar(s) # 10
toupper(s) # "HOLA MUNDO"
substr(s, 1, 4) # "Hola"
gsub("o", "0", s) # sustituir
strsplit(s, " ") # lista de palabrasR trata el texto como vectores de character. paste() une trozos, sprintf() formatea y funciones como nchar(), toupper() y gsub() manipulan el contenido.
Crear vectores
# Combinar valores v <- c(1, 2, 3, 4, 5) # Secuencias v <- 1:10 # 1 a 10 v <- seq(1, 10, by = 2) # 1,3,5,7,9 v <- seq(0, 1, length.out = 5) # Repetir v <- rep(0, times = 5) # 0,0,0,0,0 v <- rep(c(1, 2), each = 3) # 1,1,1,2,2,2 # Vector vacío de un tipo v <- numeric(5) v <- character(3)
Un vector es la estructura más básica de R y guarda elementos del mismo tipo. c() combina valores, : y seq() crean secuencias y rep() repite patrones.
Factores
# Variable categórica
sexo <- factor(c("M", "F", "F", "M"))
# Con orden definido
nota <- factor(
c("B", "A", "C", "A"),
levels = c("C", "B", "A"),
ordered = TRUE
)
# Con etiquetas
grupo <- factor(
c(1, 2, 1, 2),
labels = c("Control", "Tratamiento")
)
# Operaciones
levels(sexo) # "F" "M"
nlevels(sexo) # 2
table(sexo) # conteo por nivelUn factor guarda variables categóricas con un conjunto fijo de levels. Es esencial en modelos estadísticos y gráficos. ordered = TRUE crea niveles con orden.
Acceder a vectores
v <- c(10, 20, 30, 40, 50)
# Por posición (¡empieza en 1!)
v[1] # primero
v[-1] # todos excepto el 1.º
v[2:4] # posiciones 2 a 4
v[c(1, 3, 5)] # posiciones específicas
# Por condición lógica
v[v > 25] # 30, 40, 50
# Por nombre
names(v) <- c("a", "b", "c", "d", "e")
v["a"] # 10La indexación en R empieza en 1, no en 0. Los índices negativos excluyen elementos y un vector lógico selecciona los que son TRUE. Con names() puedes acceder por nombre.
Operadores
# Aritméticos 10 %% 3 # resto: 1 10 %/% 3 # división entera: 3 2 ^ 10 # potencia: 1024 # Comparación == != > < >= <= # Lógicos & | ! # vectorizados && || # escalares (para if) # Pertenece a 3 %in% c(1, 2, 3) # TRUE # Pipes (encadenar operaciones) x |> sum() |> sqrt() # nativo (R 4.1+) x %>% sum() %>% sqrt() # magrittr
R tiene operadores como %% (resto) y %/% (división entera). %in% comprueba pertenencia y los pipes |> (nativo) y %>% (magrittr) encadenan funciones de izquierda a derecha.
Controlo de Fluxo
If / Else
x <- 5
if (x > 0) {
print("Positivo")
} else if (x < 0) {
print("Negativo")
} else {
print("Cero")
}
# switch por valor
dia <- switch("2",
"1" = "Lunes",
"2" = "Martes",
"Otro"
)El if evalúa una condición lógica y ejecuta un bloque. Puedes encadenar else if y else. switch() es una alternativa limpia para comparar un valor con varias opciones.
Familia apply
mat <- matrix(1:12, nrow = 3) # apply: por fila (1) o columna (2) apply(mat, 1, sum) # suma por fila apply(mat, 2, mean) # media por columna # lapply: lista -> lista lapply(1:5, function(x) x ^ 2) # sapply: lista -> vector (simplificado) sapply(1:5, function(x) x ^ 2) # vapply: tipo de retorno garantizado vapply(1:5, sqrt, numeric(1)) # mapply: varios argumentos mapply(function(x, y) x + y, 1:3, 4:6) # tapply: por grupo tapply(valores, grupos, mean)
La familia apply aplica una función a elementos sin escribir bucles. lapply() devuelve una lista, sapply() simplifica a vector y vapply() garantiza el tipo de retorno, siendo más seguro.
ifelse vectorizado
notas <- c(12, 8, 15, 6) # Se aplica a cada elemento resultado <- ifelse(notas >= 10, "Aprobado", "Suspendido") # "Aprobado" "Suspendido" "Aprobado" "Suspendido" # Anidado clase <- ifelse(notas >= 14, "Alta", ifelse(notas >= 10, "Media", "Baja")) # case_when (dplyr, más legible) library(dplyr) df |> mutate(nivel = case_when( notas >= 14 ~ "Alta", notas >= 10 ~ "Media", TRUE ~ "Baja" ))
A diferencia del if, la función ifelse() es vectorizada: evalúa un vector entero de una vez. Para varias condiciones, case_when() de dplyr es más legible.
purrr y map
library(purrr) # map: devuelve lista map(1:5, ~ .x ^ 2) # Tipos específicos map_dbl(1:5, sqrt) # double map_chr(1:5, as.character) # texto map_lgl(1:5, ~ .x > 3) # lógico # Dos argumentos map2(nombres, edades, ~ paste(.x, .y)) # Filtrar keep(1:10, ~ .x %% 2 == 0) # pares discard(1:10, ~ .x > 5) # Reducir a un valor reduce(list(df1, df2, df3), merge)
El paquete purrr trae programación funcional moderna. map() aplica una función y las variantes map_dbl() y map_chr() garantizan el tipo. La fórmula ~ .x es una función anónima abreviada.
For loops
# Iterar una secuencia
for (i in 1:5) {
print(i)
}
# Recorrer elementos
for (x in c("a", "b", "c")) {
cat(x, "\n")
}
# Con índice seguro
v <- c(10, 20, 30)
for (i in seq_along(v)) {
cat(i, ":", v[i], "\n")
}
# break y next
for (i in 1:10) {
if (i == 3) next # salta el 3
if (i == 7) break # para en el 7
print(i)
}El bucle for recorre cada elemento de un vector. seq_along() genera índices seguros. next salta una iteración y break termina el bucle. En R, prefiere operaciones vectorizadas a bucles.
Condiciones vectorizadas
v <- c(5, 2, 8, 1, 9)
# Posiciones que cumplen la condición
which(v > 4) # 1 3 5
which.max(v) # 5 (posición del máximo)
which.min(v) # 4
# Comprobaciones globales
any(v > 8) # TRUE (¿alguno?)
all(v > 0) # TRUE (¿todos?)
# cut: dividir en intervalos
grupos <- cut(edades,
breaks = c(0, 18, 65, Inf),
labels = c("Joven", "Adulto", "Mayor")
)which() devuelve las posiciones que satisfacen una condición; which.max() y which.min() dan la posición del extremo. any() y all() prueban el vector entero y cut() agrupa valores en intervalos.
While y Repeat
# while: repite mientras sea verdadero
x <- 1
while (x <= 5) {
print(x)
x <- x + 1
}
# repeat: bucle infinito (necesita break)
x <- 1
repeat {
if (x > 5) break
print(x)
x <- x + 1
}
# Convergencia numérica
valor <- 1
repeat {
nuevo <- (valor + 2 / valor) / 2
if (abs(nuevo - valor) < 1e-6) break
valor <- nuevo
}
valor # raíz cuadrada de 2while repite mientras la condición sea verdadera. repeat crea un bucle infinito que solo para con break — útil para algoritmos de convergencia.
Tratamiento de errores
resultado <- tryCatch({
log("abc") # genera error
}, error = function(e) {
message("Error: ", e$message)
NA # valor de respaldo
}, warning = function(w) {
message("Aviso: ", w$message)
NULL
}, finally = {
message("Finalizado") # se ejecuta siempre
})
# Señalizar condiciones
stop("Error fatal") # para con error
warning("Cuidado") # aviso
message("Información") # mensaje
# Validar entradas
stopifnot(is.numeric(x), x > 0)tryCatch() captura errores, avisos y ejecuta código final con finally. stop() lanza un error, warning() un aviso y stopifnot() valida condiciones de entrada de forma compacta.
Funções
Definir funciones
# Función básica
suma <- function(a, b) {
a + b
}
# Con valores por defecto
saludar <- function(nombre, saludo = "Hola") {
paste(saludo, nombre)
}
saludar("Ana") # "Hola Ana"
saludar("Ana", "Buenos días") # "Buenos días Ana"
# Argumentos por nombre
saludar(saludo = "Hi", nombre = "Bob")
# Retorno implícito (última expresión)
doble <- function(x) x * 2Una función se crea con function(). El valor por defecto se define con = en los argumentos. R devuelve automáticamente la última expresión evaluada, sin necesitar return().
Funciones como objetos
# Asignar a una variable
f <- sum
f(1, 2, 3) # 6
# Pasar como argumento
aplicar <- function(datos, funcion) {
funcion(datos)
}
aplicar(1:10, mean) # 5.5
aplicar(1:10, sum) # 55
# Devolver una función
potencia <- function(n) {
function(x) x ^ n
}
cuadrado <- potencia(2)
cuadrado(5) # 25
# Composición
componer <- function(f, g) function(x) f(g(x))
sqrt_log <- componer(sqrt, log)
# Negate: invertir una función lógica
no_na <- Negate(is.na)En R, las funciones son objetos de primera clase: puedes asignarlas a variables, pasarlas como argumentos y devolverlas. Negate() crea una función que invierte el resultado lógico de otra.
Funciones anónimas
# Forma clásica
sapply(1:5, function(x) x ^ 2)
# Shorthand (R 4.1+)
sapply(1:5, \(x) x ^ 2)
# Varios argumentos
mapply(\(x, y) x + y, 1:3, 4:6)
# Cuerpo con varias líneas
sapply(1:5, \(x) {
y <- x ^ 2
y + 1
})
# Pasar a funciones de orden superior
Filter(\(x) x > 3, 1:10)
Map(\(a, b) a * b, 1:3, 4:6)Una función anónima no tiene nombre y se usa directamente como argumento. Desde R 4.1 puedes escribir \(x) en vez de function(x). Son ideales para las familias apply y Map.
Métodos S3
# Crear un objeto con clase
crear_persona <- function(nombre, edad) {
structure(
list(nombre = nombre, edad = edad),
class = "Persona"
)
}
# Método para el genérico print
print.Persona <- function(x, ...) {
cat(x$nombre, "tiene", x$edad, "años\n")
}
# Nuevo genérico con UseMethod
saludar <- function(x) UseMethod("saludar")
saludar.Persona <- function(x) paste("Hola,", x$nombre)
saludar.default <- function(x) "¡Hola!"
# Usar
p <- crear_persona("Ana", 30)
class(p) # "Persona"
inherits(p, "Persona") # TRUEEl sistema S3 es el más común en R. Un objeto es una lista con un atributo class. Métodos como print.Persona se despachan con UseMethod() según la clase del objeto.
Ámbito léxico y closures
x <- 10
funcion <- function() {
x <- 20 # variable local
x
}
funcion() # 20
x # 10 (global intacta)
# Closure: función que guarda el entorno
crear_mult <- function(multiplicador) {
function(x) x * multiplicador
}
triple <- crear_mult(3)
triple(5) # 15
# Superasignación (evitar)
contador <- 0
incrementar <- function() {
contador <<- contador + 1
}R usa ámbito léxico: una función búsqueda variables primero en su entorno y después en el entorno donde fue creada. Una closure guarda ese entorno, permitiendo estado persistente. <<- modifica variables fuera de la función (usar con cautela).
Recursión
# Factorial
fact <- function(n) {
if (n <= 1) return(1)
n * fact(n - 1)
}
# Fibonacci
fib <- function(n) {
if (n <= 1) return(n)
fib(n - 1) + fib(n - 2)
}
# Recall: autorreferencia segura
f <- function(n) {
if (n == 0) return(1)
n * Recall(n - 1)
}
# Con memoization (guardar resultados)
memo <- new.env()
fib_memo <- function(n) {
if (n <= 1) return(n)
clave <- as.character(n)
if (is.null(memo[[clave]]))
memo[[clave]] <- fib_memo(n-1) + fib_memo(n-2)
memo[[clave]]
}Una función recursiva se llama a sí misma hasta alcanzar un caso base. Recall() se refiere a la propia función de forma segura. La memoization guarda resultados en un environment para evitar recálculos.
Argumentos avanzados
# ... (dots): argumentos variables
mi_plot <- function(x, ...) {
plot(x, ...)
}
mi_plot(1:10, col = "red", pch = 19)
# missing(): ¿argumento proporcionado?
f <- function(a, b) {
if (missing(b)) b <- a
a + b
}
# match.arg: validar opciones
f <- function(color = c("red", "blue", "green")) {
color <- match.arg(color)
color
}
# on.exit: limpieza garantizada
leer <- function(path) {
con <- file(path)
on.exit(close(con))
readLines(con)
}
# do.call: llamar con lista de args
do.call(paste, list("a", "b", "c"))Los ... (dots) aceptan argumentos variables y los pasan a otras funciones. missing() verifica si un argumento fue dado, match.arg() valida opciones y on.exit() garantiza limpieza incluso en caso de error.
Debugging
# browser(): pausa interactiva
mi_funcion <- function(x) {
browser() # REPL de depuración
x * 2
}
# debug(): depurar función entera
debug(mi_funcion)
mi_funcion(5)
undebug(mi_funcion)
# traceback(): pila de llamadas
traceback()
# options(error): recuperarse de errores
options(error = recover)
# Validar y medir tiempo
stopifnot(is.numeric(x), x > 0)
system.time(resultado <- calculo())browser() pausa la ejecución para inspeccionar variables; debug() activa la depuración de una función. traceback() muestra la pila de llamadas tras un error y system.time() mide la duración.
Estruturas de Dados
Data frames
# Crear
df <- data.frame(
nombre = c("Ana", "Ray", "Eva"),
edad = c(30, 25, 35),
nota = c(14, 16, 12)
)
# Acceder
df$nombre # columna por nombre
df[["nombre"]] # ídem
df[1, 2] # fila 1, columna 2
df[1:2, ] # filas 1 y 2
df[, "edad"] # columna edad
# Información
nrow(df); ncol(df)
dim(df) # 3 3
str(df) # estructura
head(df) # primeras filas
summary(df) # resumen estadísticoUn data.frame es una tabla con columnas de tipos diferentes, la estructura central para datos en R. Se accede a las columnas con $ o [[]]. str() muestra la estructura y summary() un resumen.
Modificar data frames
df <- data.frame(x = 1:3, y = c("a", "b", "c"))
# Añadir columna
df$z <- df$x * 2
df <- transform(df, total = x + z)
# Eliminar columna
df$z <- NULL
# Renombrar
names(df)[1] <- "valor"
# Filtrar filas
df[df$x > 1, ]
subset(df, x > 1 & y != "a")
# Ordenar
df[order(df$x), ] # ascendente
df[order(-df$x), ] # descendente
# Cruzar tablas
merge(df1, df2, by = "id")Puedes crear columnas con $ o transform() y eliminarlas asignando NULL. order() devuelve el orden de ordenación y merge() cruza dos data.frame por una columna común.
Listas
# Lista heterogénea lst <- list( nombre = "Ana", edad = 30, notas = c(14, 16, 12), activo = TRUE ) # Acceder lst$nombre # "Ana" lst[[1]] # 1.º elemento lst[1] # sublista lst[["nombre"]] # "Ana" # Modificar lst$email <- "a@b.c" # añadir lst$edad <- NULL # eliminar # Aplanar unlist(lst) # vector simple # length(lst) # número de elementos
Una list guarda elementos de tipos diferentes, incluidas otras listas. [[]] extrae el elemento en sí, mientras que [] devuelve una sublista. unlist() aplana la lista a un vector.
Tibbles
library(tibble)
# Crear tibble
tb <- tibble(
nombre = c("Ana", "Ray"),
edad = c(30, 25)
)
# Por filas
tribble(
~nombre, ~edad,
"Ana", 30,
"Ray", 25
)
# Columnas-lista
tb2 <- tibble(
id = 1:3,
datos = list(c(1, 2), c(3, 4, 5), c(6))
)
# Convertir
as_tibble(df)
as.data.frame(tb)Un tibble es un data.frame moderno del tidyverse: no convierte texto en factores, preserva nombres de columnas e imprime de forma más clara. tribble() lo crea por filas y acepta columnas-lista.
Matrices
# Crear (rellenada por columna) m <- matrix(1:12, nrow = 3, ncol = 4) # Por fila m <- matrix(1:12, nrow = 3, byrow = TRUE) # Acceder m[1, 2] # fila 1, columna 2 m[1, ] # fila 1 m[, 2] # columna 2 m[1:2, 1:2] # submatriz # Álgebra lineal t(m) # transpuesta m %*% n # producto matricial solve(m) # inversa det(m) # determinante eigen(m) # autovalores y autovectores
Una matrix es un vector bidimensional con elementos del mismo tipo. Por defecto se rellena por columnas (byrow = TRUE lo cambia). %*% hace producto matricial y solve() calcula la inversa.
Fechas y tiempos
library(lubridate)
# Crear fechas (orden de los componentes)
ymd("2024-03-15")
dmy("15/03/2024")
ymd_hms("2024-03-15 14:30:00")
# Extraer componentes
year(d); month(d); day(d)
wday(d, label = TRUE) # día de la semana
# Aritmética
d + days(7)
d - months(1)
interval(d1, d2) / days(1) # n.º de días
# Base R
Sys.Date()
as.Date("2024-01-01")
format(Sys.Date(), "%d/%m/%Y")
difftime(d2, d1, units = "days")El paquete lubridate simplifica las fechas: ymd() y dmy() interpretan el orden de los componentes. Suma periodos con days() y months(). En R base, usa as.Date() y difftime().
Arrays y combinación
# Array con n dimensiones
arr <- array(1:24, dim = c(2, 3, 4))
arr[1, 2, 3]
# Combinar por filas
rbind(c(1, 2), c(3, 4))
# Combinar por columnas
cbind(c(1, 2), c(3, 4))
# Unir data frames
rbind(df1, df2) # apilar filas
cbind(df1, df2) # lado a lado
# Nombrar dimensiones
dimnames(m) <- list(
c("f1", "f2", "f3"),
c("c1", "c2", "c3", "c4")
)Un array generaliza la matriz a más dimensiones. rbind() une por filas y cbind() por columnas, tanto para vectores como para data.frame. dimnames() da nombres a las dimensiones.
Inspección y conversión
x <- c(1, 2, 3)
# Inspeccionar
class(x) # "numeric"
typeof(x) # "double"
str(x) # estructura compacta
length(x) # 3
attributes(x) # atributos
# Convertir entre tipos
as.integer(c(1.5, 2.9)) # 1 2
as.character(1:3) # "1" "2" "3"
as.numeric(c("1", "2")) # 1 2
as.logical(c(0, 1, 2)) # FALSE TRUE TRUE
# Reestructurar
unlist(list(1, 2, 3))
as.vector(matrix(1:6))class() y typeof() revelan el tipo; str() muestra la estructura de forma compacta. Las funciones as.*() convierten entre tipos — atención: as.integer() trunca los decimales.
Manipulação de Dados
dplyr — verbos básicos
library(dplyr) # filter: seleccionar filas df |> filter(edad > 25, activo == TRUE) # select: elegir columnas df |> select(nombre, edad) df |> select(-nota) # eliminar # mutate: crear/modificar columnas df |> mutate( doble = edad * 2, senior = ifelse(edad > 30, "S", "J") ) # arrange: ordenar df |> arrange(desc(edad), nombre) # slice: por posición df |> slice(1:5) df |> slice_max(nota, n = 3) # rename df |> rename(id = código)
dplyr manipula datos con verbos claros unidos por el pipe |>. filter() selecciona filas, select() columnas, mutate() crea columnas, arrange() ordena y rename() renombra.
across — varias columnas
library(dplyr)
# Aplicar a varias columnas
df |> mutate(across(c(x, y, z), ~ .x * 2))
# Por tipo
df |> mutate(across(where(is.numeric), round, 2))
# Varias funciones a la vez
df |> summarise(
across(
where(is.numeric),
list(media = mean, sd = sd),
na.rm = TRUE
)
)
# Condiciones sobre columnas
df |> filter(if_any(ends_with("_p"), ~ .x > 0.5))
df |> filter(if_all(starts_with("q"), ~ .x > 0))
# Renombrar en masa
df |> rename_with(toupper)across() aplica la misma función a varias columnas de una vez, combinándose con where() o selectores. if_any() e if_all() filtran filas según condiciones en varias columnas.
stringr — texto
library(stringr)
# Detectar y contar
str_detect(s, "patrón")
str_count(s, "a")
# Extraer y sustituir
str_extract(s, "\d+")
str_replace(s, "viejo", "nuevo")
str_replace_all(s, "\d", "#")
# Manipular
str_pad("42", 5, pad = "0") # "00042"
str_trim(" texto ")
str_to_title("hola mundo")
# Dividir y unir
str_split(s, ",")
str_c("a", "b", "c", sep = "-")
str_flatten(c("a", "b"), collapse = ", ")El paquete stringr ofrece funciones de texto con nombres consistentes (todas empiezan por str_). str_detect() búsqueda patrones, str_replace() sustituye y str_pad() rellena.
dplyr — agrupar y resumir
library(dplyr)
df |>
group_by(departamento) |>
summarise(
media = mean(salario),
total = n(),
max_sal = max(salario),
.groups = "drop"
)
# Varios grupos
df |>
group_by(anio, mes) |>
summarise(ventas = sum(valor))
# mutate por grupo
df |>
group_by(equipo) |>
mutate(rank = rank(-puntos))
# Conteo rápido
df |> count(categoria, sort = TRUE)group_by() divide los datos en grupos y summarise() calcula resúmenes por grupo, como mean() y n() (conteo). count() es un atajo para contar por categoría.
tidyr — reshape
library(tidyr)
# Ancho -> largo
df |> pivot_longer(
cols = c(anio1, anio2, anio3),
names_to = "anio",
values_to = "valor"
)
# Largo -> ancho
df |> pivot_wider(
names_from = categoria,
values_from = valor
)
# Separar / unir columnas
df |> separate(nombre, c("primero", "último"), " ")
df |> unite("completo", primero, último, sep = " ")tidyr cambia la forma de los datos. pivot_longer() pasa columnas a filas (formato largo) y pivot_wider() hace lo contrario. separate() divide una columna y unite() une varias.
dplyr — joins
library(dplyr)
# Mantener solo coincidencias
inner_join(pedidos, clientes, by = "id")
# Todos los de la izquierda
left_join(pedidos, clientes, by = "id")
# Todos los de la derecha
right_join(pedidos, clientes, by = "id")
# Todos los de ambos
full_join(df1, df2, by = "id")
# Filtro: solo los que existen
semi_join(pedidos, clientes, by = "id")
# Exclusión: los que no existen
anti_join(pedidos, clientes, by = "id")
# Claves con nombres diferentes
left_join(a, b, by = c("id" = "código"))Los join combinan tablas por una clave. left_join() mantiene todas las filas de la izquierda; inner_join() solo las coincidencias. semi_join() y anti_join() filtran sin añadir columnas.
tidyr — NAs y nesting
library(tidyr) # Rellenar NAs hacia abajo df |> fill(columna, .direction = "down") # Eliminar filas con NA df |> drop_na() df |> drop_na(edad, nombre) # solo estas cols # Sustituir NAs df |> replace_na(list(edad = 0)) # Anidar datos por grupo df |> nest(datos = -grupo) # Desanidar df |> unnest(datos)
fill() rellena valores faltantes con el último valor conocido. drop_na() elimina filas con NA y replace_na() los sustituye. nest() guarda subtablas en una columna-lista.
Selección de columnas (tidyselect)
library(dplyr)
# Por patrón de nombre
df |> select(starts_with("pre"))
df |> select(ends_with("_id"))
df |> select(contains("fecha"))
df |> select(matches("^col\d+$"))
# Por tipo
df |> select(where(is.numeric))
df |> select(where(is.character))
# Variable con nombres
cols <- c("a", "b", "c")
df |> select(all_of(cols)) # error si falta
df |> select(any_of(cols)) # tolerante
# Reordenar
df |> select(id, everything())tidyselect elige columnas por patrones: starts_with(), ends_with(), contains() y matches() (regex). where() selecciona por tipo y everything() recoge las restantes.
Datos faltantes (base R)
v <- c(1, NA, 3, NA, 5) # Detectar is.na(v) # vector lógico sum(is.na(v)) # total de NAs colSums(is.na(df)) # por columna # Eliminar na.omit(df) # filas con NA df[complete.cases(df), ] # Sustituir v[is.na(v)] <- 0 ifelse(is.na(v), 0, v) # Ignorar en los cálculos mean(v, na.rm = TRUE) sum(v, na.rm = TRUE)
is.na() detecta valores faltantes. na.omit() y complete.cases() los eliminan; también puedes sustituirlos por índice. La opción na.rm = TRUE ignora los NA en las funciones estadísticas.
Avançado
Leer y escribir datos
# CSV (base R)
df <- read.csv("datos.csv")
write.csv(df, "out.csv", row.names = FALSE)
# readr (más rápido, tibble)
library(readr)
df <- read_csv("datos.csv")
write_csv(df, "out.csv")
# Excel
library(readxl)
df <- read_excel("datos.xlsx", sheet = 1)
# RDS (un objeto R)
saveRDS(objeto, "datos.rds")
objeto <- readRDS("datos.rds")
# Líneas de texto
lineas <- readLines("archivo.txt")
writeLines(lineas, "out.txt")read.csv() lee archivos CSV en R base; read_csv() de readr es más rápido y devuelve un tibble. saveRDS() y readRDS() guardan objetos R en formato nativo.
Programación paralela
library(parallel)
# N.º de núcleos disponibles
detectCores()
# Crear cluster
cl <- makeCluster(4)
# lapply paralelo
resultados <- parLapply(cl, datos, funcion)
# Exportar variables/funciones
clusterExport(cl, c("datos", "funcion"))
# Terminar (¡importante!)
stopCluster(cl)
# Alternativa simple (solo Unix)
mclapply(datos, funcion, mc.cores = 4)El paquete parallel distribuye el trabajo entre varios núcleos. makeCluster() crea los workers, parLapply() aplica en paralelo y stopCluster() libera los recursos. En Unix, mclapply() es más simple.
Paquetes
# Instalar desde CRAN
install.packages("dplyr")
install.packages(c("ggplot2", "tidyr"))
# Cargar
library(dplyr)
require(ggplot2) # devuelve TRUE/FALSE
# Usar sin cargar
dplyr::filter(df, x > 1)
# Información
installed.packages()
packageVersion("dplyr")
sessionInfo()
# Actualizar
update.packages()install.packages() instala desde CRAN y library() carga el paquete en la sesión. El operador :: usa una función sin cargar el paquete, evitando conflictos de nombres. sessionInfo() lista los paquetes activos.
Clases R6
library(R6)
# Definir clase
Persona <- R6Class("Persona",
public = list(
nombre = NULL,
edad = NULL,
initialize = function(nombre, edad) {
self$nombre <- nombre
self$edad <- edad
},
saludar = function() {
paste("Hola,", self$nombre)
}
),
private = list(
secreto = "privado"
)
)
# Usar
p <- Persona$new("Ana", 30)
p$saludar()
p$nombre <- "Ray"R6 trae programación orientada a objetos por referencia (al contrario que S3). public y private definen miembros, initialize() es el constructor y self refiere al objeto. Se crea con $new().
data.table
library(data.table)
# Leer muy rápido
dt <- fread("grande.csv")
# Sintaxis: dt[i, j, by]
# Filtrar (i), calcular (j), agrupar (by)
dt[edad > 25, .(media = mean(salario)), by = dep]
# Añadir/modificar columnas por referencia
dt[, nuevo := salario * 2]
# Varias operaciones
dt[, .(total = .N, media = mean(valor)), by = grupo]
# Ordenar
setorder(dt, -edad)
# Convertir
setDT(df) # data.frame -> data.table
as.data.table(df)data.table es ideal para datos grandes. La sintaxis dt[i, j, by] filtra, calcula y agrupa en una sola expresión. := modifica columnas por referencia (sin copiar) y fread() lee archivos muy rápido.
Ambientes (environments)
# Crear ambiente
env <- new.env()
# Asignar y acceder
env$x <- 10
env[["y"]] <- 20
env$x # 10
# Listar y verificar
ls(env)
exists("x", envir = env)
# Eliminar
rm("x", envir = env)
# Ambiente global y de funciones
globalenv()
environment()
# Usado en closures y memoization
cache <- new.env()Un environment asocia nombres a valores, como una lista, pero sin orden y por referencia. Es la base del ámbito (scope) en R y del sistema de objetos. Se usa mucho para cachés y estado mutable, como en memoization.
Rendimiento
# Vectorizar (evitar bucles) # Malo: r <- numeric(length(x)) for (i in seq_along(x)) r[i] <- x[i] * 2 # Bueno: r <- x * 2 # Prealocar resultados resultado <- numeric(n) # apply en vez de for resultados <- lapply(lista, procesar) # Medir tiempo system.time(calculo()) # Comparar métodos microbenchmark::microbenchmark( metodo1(), metodo2(), times = 100 ) # Profiling Rprof(); código(); Rprof(NULL) summaryRprof()
La regla de oro es vectorizar: las operaciones sobre vectores enteros son mucho más rápidas que los bucles. Prealoca resultados con numeric(), usa lapply() y mide con system.time() o microbenchmark().
Proyectos y reproducibilidad
# renv: fijar versiones de paquetes
renv::init() # crear renv.lock
renv::snapshot() # guardar estado actual
renv::restore() # restaurar del lockfile
# devtools: crear paquetes
devtools::create("miPaquete")
devtools::document() # generar documentación
devtools::install()
devtools::check()
# usethis: configurar proyectos
usethis::create_project("analisis")
usethis::use_git()
usethis::use_testthat()
# Buenas prácticas
# - Un proyecto por carpeta (.Rproj)
# - Rutas relativas
# - renv.lock versionadorenv hace los análisis reproducibles al fijar versiones de paquetes en un renv.lock. devtools y usethis ayudan a crear paquetes y proyectos. Usa siempre rutas relativas y un proyecto por carpeta.
Visualização
ggplot2 — básico
library(ggplot2)
# Estructura: datos + estética + geometría
ggplot(df, aes(x = edad, y = salario)) +
geom_point()
# Añadir línea de tendencia
ggplot(df, aes(x = edad, y = salario)) +
geom_point() +
geom_smooth(method = "lm")
# Título y ejes
ggplot(df, aes(x = edad, y = salario)) +
geom_point() +
labs(
title = "Salario por edad",
x = "Edad",
y = "Salario (€)"
)ggplot2 construye gráficos por capas: ggplot() define los datos, aes() la estética (ejes, colores) y los geom_*() el tipo de gráfico. Las capas se suman con +.
Facets
library(ggplot2) # Un panel por nivel de una variable ggplot(df, aes(x = edad, y = salario)) + geom_point() + facet_wrap(~grupo) # Rejilla con dos variables ggplot(df, aes(x = edad, y = salario)) + geom_point() + facet_grid(sexo ~ escalon) # Escalas libres por panel ggplot(df, aes(x = anio, y = ventas)) + geom_line() + facet_wrap(~producto, scales = "free_y")
Los facets dividen el gráfico en varios paneles. facet_wrap() crea paneles por una variable y facet_grid() una rejilla con dos. scales = "free_y" deja que cada panel tenga su escala.
Geoms esenciales
library(ggplot2) # Histograma ggplot(df, aes(x = edad)) + geom_histogram(bins = 20, fill = "steelblue") # Boxplot ggplot(df, aes(x = grupo, y = valor)) + geom_boxplot() # Barras (conteo) ggplot(df, aes(x = categoria, fill = tipo)) + geom_bar(position = "dodge") # Líneas ggplot(df, aes(x = anio, y = ventas)) + geom_line() # Densidad ggplot(df, aes(x = valor, fill = grupo)) + geom_density(alpha = 0.4)
Cada geom dibuja un tipo de gráfico: geom_histogram() para distribuciones, geom_boxplot() para resúmenes, geom_bar() para conteos y geom_line() para series. alpha controla la transparencia.
Capas múltiples
library(ggplot2)
ggplot(df, aes(x = anio, y = valor)) +
# Línea por serie (agrupada)
geom_line(aes(group = id, color = id), alpha = 0.4) +
# Puntos encima
geom_point(aes(color = id)) +
# Línea de referencia vertical
geom_vline(xintercept = 2020, linetype = "dashed") +
# Texto anotado
annotate("text", x = 2021, y = 100, label = "Evento") +
theme_minimal()Puedes superponer varias capas: geom_line() con geom_point() encima, líneas de referencia con geom_vline() y anotaciones con annotate(). El orden de las capas define qué queda encima.
Colores y escalas
library(ggplot2)
p <- ggplot(df, aes(x, y, color = grupo)) +
geom_point()
# Colores manuales
p + scale_color_manual(values = c("red", "blue"))
# Paletas ColorBrewer
p + scale_color_brewer(palette = "Set2")
# Ejes
p + scale_x_continuous(limits = c(0, 100))
p + scale_y_log10()
# Relleno (fill)
ggplot(df, aes(x, fill = tipo)) +
geom_bar() +
scale_fill_brewer(palette = "Pastel1")Las scale_*() controlan colores y ejes. scale_color_manual() define colores fijos y scale_color_brewer() usa paletas de ColorBrewer. Usa color para líneas/puntos y fill para rellenos.
Gráficos de R base
# Dispersión plot(df$edad, df$salario, main = "Título", xlab = "Edad", ylab = "Salario") # Histograma hist(df$edad, breaks = 20, col = "lightblue") # Boxplot boxplot(valor ~ grupo, data = df) # Barras barplot(table(df$categoria), col = "gray") # Líneas plot(1:10, type = "l", col = "red") lines(1:10, 10:1, col = "blue") # Parámetros gráficos par(mfrow = c(1, 2)) # 2 gráficos lado a lado
R base incluye gráficos sin paquetes extra: plot() para dispersión, hist() para histogramas, boxplot() y barplot(). par(mfrow) dispone varios gráficos en la misma ventana.
Temas y labels
library(ggplot2) p <- ggplot(df, aes(x, y)) + geom_point() # Temas listos p + theme_minimal() p + theme_classic() p + theme_dark() # Personalizar elementos p + theme( plot.title = element_text(size = 16, face = "bold"), legend.position = "bottom", panel.grid = element_blank() ) # Etiquetas completas p + labs( title = "Título", subtitle = "Subtítulo", caption = "Fuente: datos", color = "Grupo" )
Los temas listos como theme_minimal() cambian el aspecto global. theme() ajusta elementos individuales con element_text() y element_blank(). labs() define títulos y leyendas.
Guardar gráficos
library(ggplot2)
p <- ggplot(df, aes(x, y)) + geom_point()
# ggsave (recomendado)
ggsave("grafico.png", p, width = 10, height = 6, dpi = 300)
# Formato por el sufijo
ggsave("grafico.pdf", p)
ggsave("grafico.svg", p)
# Dispositivos de R base
png("grafico.png", width = 800, height = 600)
plot(1:10)
dev.off() # cerrar y guardar
pdf("grafico.pdf")
hist(df$edad)
dev.off()ggsave() guarda el último gráfico; el formato se detecta por el sufijo (.png, .pdf, .svg). En R base, abre un dispositivo como png(), dibuja y cierra con dev.off().
Estatística e Modelos
Estadística descriptiva
v <- c(12, 15, 18, 22, 25, 30) # Medidas de tendencia central mean(v) # media median(v) # mediana # Dispersión sd(v) # desviación estándar var(v) # varianza IQR(v) # rango intercuartílico range(v) # mínimo y máximo # Cuantiles quantile(v) quantile(v, probs = c(0.1, 0.9)) # Resumen completo summary(v) summary(df) # de todas las columnas
R calcula estadísticas directamente: mean() y median() para el centro, sd() y var() para la dispersión. quantile() da los cuartiles y summary() un resumen rápido de cinco números.
Regresión lineal
# Ajustar el modelo modelo <- lm(salario ~ edad + experiencia, data = df) # Resumen (coeficientes, R², p-values) summary(modelo) # Coeficientes coef(modelo) confint(modelo) # intervalos de confianza # Predecir nuevos valores predict(modelo, newdata = nuevos) # Fórmulas lm(y ~ x1 * x2, data = df) # con interacción lm(y ~ x1 + I(x1^2), data = df) # término cuadrático lm(y ~ ., data = df) # todas las columnas
lm() ajusta una regresión lineal con la fórmula y ~ x. summary() muestra los coeficientes, el R² y los p-values. * añade interacciones e I() permite términos como x^2.
Correlación
# Coeficiente de Pearson
cor(x, y)
# Otros métodos
cor(x, y, method = "spearman")
cor(x, y, method = "kendall")
# Con prueba de hipótesis
cor.test(x, y) # valor + p-value
# Matriz de correlación
cor(df[, c("edad", "salario", "nota")])
# Ignorar NAs
cor(x, y, use = "complete.obs")cor() calcula la correlación entre dos variables (por defecto Pearson). cor.test() añade el p-value y el intervalo de confianza. Con varias columnas, devuelve una matriz de correlación.
GLM y regresión logística
# Regresión logística (resultado binario) modelo <- glm(aprobado ~ nota + horas, data = df, family = binomial) summary(modelo) # Probabilidades predichas predict(modelo, newdata = nuevos, type = "response") # Regresión de Poisson (conteos) glm(conteo ~ x, data = df, family = poisson) # Otras familias glm(y ~ x, family = Gamma) glm(y ~ x, family = gaussian) # Razones de probabilidades (odds ratios) exp(coef(modelo))
glm() generaliza la regresión lineal mediante el argumento family. binomial hace regresión logística y poisson modela conteos. type = "response" devuelve probabilidades en vez de log-odds.
Distribuciones
# Cuatro prefijos: d, p, q, r # r: generar valores aleatorios rnorm(100, mean = 0, sd = 1) # normal runif(100, min = 0, max = 1) # uniforme rbinom(100, size = 10, prob = 0.5) # d: densidad / probabilidad dnorm(0) # densidad en 0 dbinom(5, 10, 0.5) # p: probabilidad acumulada pnorm(1.96) # cerca de 0.975 # q: cuantil (inverso de p) qnorm(0.975) # cerca de 1.96 qt(0.975, df = 10)
Cada distribución tiene cuatro funciones: r* genera valores aleatorios, d* da la densidad, p* la probabilidad acumulada y q* el cuantil. Ejemplos: rnorm(), pnorm(), qnorm().
Diagnóstico y comparación
modelo <- lm(y ~ x1 + x2, data = df) # Gráficos de diagnóstico (4) plot(modelo) # Intervalos de confianza confint(modelo) # Tabla ANOVA anova(modelo) # Comparar modelos m1 <- lm(y ~ x1, data = df) m2 <- lm(y ~ x1 + x2, data = df) AIC(m1, m2) # menor es mejor anova(m1, m2) # prueba F # Residuos resid(modelo) fitted(modelo)
plot(modelo) genera cuatro gráficos de diagnóstico de los residuos. AIC() compara modelos (menor es mejor) y anova() prueba si añadir variables mejora el ajuste. resid() y fitted() extraen residuos y valores ajustados.
Pruebas de hipótesis
# Prueba t (una muestra) t.test(v, mu = 0) # Prueba t (dos muestras) t.test(grupo1, grupo2) t.test(valor ~ grupo, data = df) # Chi-cuadrado chisq.test(tabla) # Normalidad shapiro.test(v) # Mann-Whitney (no paramétrico) wilcox.test(grupo1, grupo2) # ANOVA modelo <- aov(valor ~ grupo, data = df) summary(modelo) TukeyHSD(modelo) # comparaciones múltiples
t.test() compara medias, chisq.test() prueba asociaciones en tablas y shapiro.test() verifica la normalidad. aov() hace análisis de varianza y TukeyHSD() compara grupos entre sí.
broom — modelos tidy
library(broom) modelo <- lm(salario ~ edad + experiencia, data = df) # Coeficientes como data frame tidy(modelo) # Resumen del modelo (R², AIC...) glance(modelo) # Predicciones con residuos augment(modelo) # Funciona con muchos modelos tidy(glm(aprobado ~ nota, data = df, family = binomial)) tidy(t.test(grupo1, grupo2)) glance(aov(valor ~ grupo, data = df))
El paquete broom convierte resultados de modelos en data.frame limpios. tidy() da los coeficientes, glance() un resumen de una línea y augment() los datos con predicciones y residuos.