Patrones de Programación en R
## Objetivos de Aprendizaje
- Dominar patrones comunes de programación en R
- Comprender técnicas de depuración
- Manejar errores gracefulmente
- Escribir código R eficiente
## Organización del Código
### Estructura de Proyecto
```text
mi_proyecto/
R/
funciones.R
auxiliares.R
datos/
raw/
processed/
scripts/
analisis.R
output/
mi_proyecto.Rproj
```
### Sourcing de Scripts
```r
# Source otro archivo R
source("R/funciones.R")
# Source con manejo de errores
tryCatch({
source("R/funciones.R")
}, error = function(e) {
message("Error al hacer source de funciones: ", e$message)
})
# Funciones de paquete
devtools::source_url("https://ejemplo.com/funciones.R")
```
## Mejores Prácticas de Funciones
### Plantilla de Función
```r
# Función documentada
#' Calcular estadísticas resumidas
#'
#' @param x Un vector numérico
#' @param na.rm Eliminar valores NA (por defecto TRUE)
#' @return Una lista con media, sd y n
#' @examples
#' mi_summary(c(1, 2, 3, 4, 5))
mi_summary <- function(x, na.rm = TRUE) {
if (!is.numeric(x)) {
stop("x debe ser numérico")
}
resultado <- list(
media = mean(x, na.rm = na.rm),
sd = sd(x, na.rm = na.rm),
n = sum(!is.na(x))
)
return(resultado)
}
```
### Validación de Entrada
```r
validar_entrada <- function(x) {
if (is.null(x)) {
stop("x no puede ser NULL")
}
if (length(x) == 0) {
stop("x no puede estar vacío")
}
if (!is.numeric(x)) {
stop("x debe ser numérico")
}
invisible(TRUE) # Éxito silencioso
}
```
### Argumentos Nombrados
```r
# Usar argumentos nombrados para claridad
calcular_estadisticas(
datos = mis_datos,
grupo_por = "categoria",
na.rm = TRUE
)
# Usar ... para flexibilidad
funcion_flexible <- function(datos, ...) {
# Pasar ... a otra función
aggregate(datos, ...)
}
```
## Manejo de Errores
### Patrón tryCatch
```r
# tryCatch básico
resultado <- tryCatch({
# Intentar este código
operacion_arriesgada()
}, warning = function(w) {
# Manejar advertencia
message("Advertencia: ", w$message)
NA
}, error = function(e) {
# Manejar error
message("Error: ", e$message)
NULL
}, finally = {
# Siempre se ejecuta
cleanup()
})
```
### try para Errores Silenciosos
```r
# try captura error pero continúa
resultado <- try({
funcion_peligrosa()
}, silent = TRUE)
if (inherits(resultado, "try-error")) {
message("Operación fallida")
} else {
# Usar resultado
}
```
### Clases de Error Personalizadas
```r
# Crear clase de error personalizada
mi_error <- function(mensaje) {
err <- structure(list(message = mensaje), class = "mi_error")
}
# Manejar error personalizado
tryCatch({
stop(mi_error("Mensaje de error personalizado"))
}, mi_error = function(e) {
message("Capturé mi error: ", e$message)
})
```
## Depuración
### Función browser()
```r
funcion_debug <- function(x) {
resultado <- x * 2
browser() # El depurador se detiene aquí
return(resultado)
}
```
### debug() y undebug()
```r
# Marcar función para depuración
debug(mi_funcion)
# Ejecutar tu código
mi_funcion()
# Quitar depuración
undebug(mi_funcion)
```
### traceback()
```r
# Después de un error, ver la pila de llamadas
traceback()
# O con traceback
options(error = traceback)
```
### recover()
```r
# Establecer opción para entrar en browser en error
options(error = recover)
# Ahora los errores mostrarán pila de llamadas y te permitirán inspeccionar
```
### Depuración con cat()
```r
# Imprimir valores intermedios
procesar_datos <- function(df) {
cat("Filas de entrada:", nrow(df), "\n")
df <- filter(df, !is.na(valor))
cat("Después de filtro:", nrow(df), "\n")
return(df)
}
```
### str() para Inspección
```r
# Inspeccionar cualquier objeto
str(df)
str(mi_lista)
str(mi_funcion)
# Estructura detallada
str(df, max.level = 2)
```
## Programación Orientada a Objetos en R
### Clases S3
```r
# Crear objeto S3
mi_objeto <- structure(
list(data = c(1, 2, 3), name = "test"),
class = "mi_clase"
)
# Método print
print.mi_clase <- function(x) {
cat("Mi objeto:", x$name, "\n")
cat("Datos:", x$data, "\n")
}
# Función genérica
mi_generico <- function(x) {
UseMethod("mi_generico")
}
# Método por defecto
mi_generico.default <- function(x) {
"Por defecto"
}
# Método específico
mi_generico.mi_clase <- function(x) {
"Método de mi clase"
}
```
### Clases R6
```r
library(R6)
# Definir clase R6
Contador <- R6Class(
"Contador",
public = list(
conteo = 0,
agregar = function(n = 1) {
self$conteo <- self$conteo + n
invisible(self)
},
obtener = function() {
self$conteo
}
)
)
# Usar objeto R6
contador <- Contador$new()
contador$agregar(5)
contador$obtener() # 5
```
## Patrones de Iteración
### lapply con Progreso
```r
library(pbapply)
pbapply::pblapply(1:100, funcion_lenta)
```
### Procesamiento Paralelo
```r
library(parallel)
# Detectar núcleos
detectCores()
# Crear cluster
cl <- makeCluster(2)
# lapply paralelo
parLapply(cl, 1:10, function(x) x^2)
# Detener cluster
stopCluster(cl)
```
### Paquete Future
```r
library(future)
# Secuencial (por defecto)
plan(sequential)
# Multisesión
plan(multisession)
# Future lapply
library(future.apply)
future_lapply(1:10, function(x) x^2)
```
## Optimización de Rendimiento
### Perfilado
```r
# Perfilado de código
Rprof("perfil.out")
source("mi_script.R")
Rprof(NULL)
# Ver resultados
summaryRprof("perfil.out")
```
### Uso de Memoria
```r
# Tamaño de objeto
object.size(df)
format(object.size(df), units = "Mb")
# Uso de memoria
gc()
# Listar objetos más grandes
lsos <- function() {
sort(sapply(ls(envir = globalenv()), object.size), decreasing = TRUE)
}
lsos()
```
### Acelerar Código
```r
# Preasignar en lugar de crecer
# Lento:
resultado <- c()
for (i in 1:1000) {
resultado <- c(resultado, i^2)
}
# Rápido:
resultado <- numeric(1000)
for (i in 1:1000) {
resultado[i] <- i^2
}
# O simplemente:
resultado <- (1:1000)^2
```
### Data Table para Datos Grandes
```r
library(data.table)
# Lectura rápida
dt <- fread("archivo_grande.csv")
# Agregación rápida
dt[, .(media = mean(valor)), by = grupo]
# Uniones rápidas
merge(dt1, dt2, on = "clave")
```
## Programación Funcional
### Clausuras
```r
# Función que retorna función
hacer_potencia <- function(exp) {
function(x) x^exp
}
al_cuadrado <- hacer_potencia(2)
al_cubo <- hacer_potencia(3)
```
### Funciones purrr
```r
library(purrr)
# map - aplicar función a cada elemento
map(c(1, 2, 3), ~ .x^2) # list(1, 4, 9)
# map_dbl - retornar vector double
map_dbl(c(1, 2, 3), ~ .x^2) # 1 4 9
# safely - manejar errores
leer_csv_seguro <- safely(read_csv)
resultado <- leer_csv_seguro("archivo.csv")
resultado$result # datos o NULL
resultado$error # error o NULL
# possibly - proporcionar valor por defecto
map_chr(list(1, 2, 3), possibly(~ as.character(.x), "predeterminado"))
```
## Resumen
- Sourcear scripts con `source()`
- Validar entradas en funciones
- Usar `tryCatch()` para manejo de errores
- Depurar con `browser()`, `debug()`, `traceback()`
- S3 y R6 para patrones de POO
- Considerar procesamiento paralelo para bucles grandes
- Perilar antes de optimizar
- Usar data.table para datasets grandes
- purrr para programación funcional
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →