← R EspañolChapter 12 of 13

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 →