← R EnglishChapter 12 of 13

R Programming Patterns

## Learning Objectives - Master common R programming patterns - Understand debugging techniques - Handle errors gracefully - Write efficient R code ## Code Organization ### Project Structure ```text my_project/ R/ functions.R helpers.R data/ raw/ processed/ scripts/ analysis.R output/ my_project.Rproj ``` ### Sourcing Scripts ```r # Source another R file source("R/functions.R") # Source with error handling tryCatch({ source("R/functions.R") }, error = function(e) { message("Error sourcing functions: ", e$message) }) # Package functions devtools::source_url("https://example.com/functions.R") ``` ## Functions Best Practices ### Function Template ```r # Documented function #' Calculate summary statistics #' #' @param x A numeric vector #' @param na.rm Remove NA values (default TRUE) #' @return A list with mean, sd, and n #' @examples #' my_summary(c(1, 2, 3, 4, 5)) my_summary <- function(x, na.rm = TRUE) { if (!is.numeric(x)) { stop("x must be numeric") } result <- list( mean = mean(x, na.rm = na.rm), sd = sd(x, na.rm = na.rm), n = sum(!is.na(x)) ) return(result) } ``` ### Input Validation ```r validate_input <- function(x) { if (is.null(x)) { stop("x cannot be NULL") } if (length(x) == 0) { stop("x cannot be empty") } if (!is.numeric(x)) { stop("x must be numeric") } invisible(TRUE) # Silent success } ``` ### Named Arguments ```r # Use named arguments for clarity calculate_stats( data = my_data, group_by = "category", na.rm = TRUE ) # Use ... for flexibility flexible_function <- function(data, ...) { # Pass ... to another function aggregate(data, ...) } ``` ## Error Handling ### tryCatch Pattern ```r # Basic tryCatch result <- tryCatch({ # Try this code risky_operation() }, warning = function(w) { # Handle warning message("Warning: ", w$message) NA }, error = function(e) { # Handle error message("Error: ", e$message) NULL }, finally = { # Always runs cleanup() }) ``` ### try for Silent Errors ```r # try catches error but continues result <- try({ dangerous_function() }, silent = TRUE) if (inherits(result, "try-error")) { message("Operation failed") } else { # Use result } ``` ### Custom Error Classes ```r # Create custom error class my_error <- function(message) { err <- structure(list(message = message), class = "my_error") } # Handle custom error tryCatch({ stop(my_error("Custom error message")) }, my_error = function(e) { message("Caught my error: ", e$message) }) ``` ## Debugging ### browser() Function ```r debug_function <- function(x) { result <- x * 2 browser() # Debugger stops here return(result) } ``` ### debug() and undebug() ```r # Mark function for debugging debug(my_function) # Run your code my_function() # Remove debugging undebug(my_function) ``` ### traceback() ```r # After an error, see the call stack traceback() # Or with traceback options(error = traceback) ``` ### recover() ```r # Set option to enter browser on error options(error = recover) # Now errors will show call stack and let you inspect ``` ### cat() Debugging ```r # Print intermediate values process_data <- function(df) { cat("Input rows:", nrow(df), "\n") df <- filter(df, !is.na(value)) cat("After filter:", nrow(df), "\n") return(df) } ``` ### str() for Inspection ```r # Inspect any object str(df) str(my_list) str(my_function) # Detailed structure str(df, max.level = 2) ``` ## Object-Oriented Programming in R ### S3 Classes ```r # Create S3 object my_object <- structure( list(data = c(1, 2, 3), name = "test"), class = "my_class" ) # Print method print.my_class <- function(x) { cat("My object:", x$name, "\n") cat("Data:", x$data, "\n") } # Generic function my_generic <- function(x) { UseMethod("my_generic") } # Default method my_generic.default <- function(x) { "Default" } # Specific method my_generic.my_class <- function(x) { "My class method" } ``` ### R6 Classes ```r library(R6) # Define R6 class Counter <- R6Class( "Counter", public = list( count = 0, add = function(n = 1) { self$count <- self$count + n invisible(self) }, get = function() { self$count } ) ) # Use R6 object counter <- Counter$new() counter$add(5) counter$get() # 5 ``` ## Iteration Patterns ### lapply with Progress ```r library(pbapply) pbapply::pblapply(1:100, slow_function) ``` ### Parallel Processing ```r library(parallel) # Detect cores detectCores() # Create cluster cl <- makeCluster(2) # Parallel lapply parLapply(cl, 1:10, function(x) x^2) # Stop cluster stopCluster(cl) ``` ### Future Package ```r library(future) # Sequential (default) plan(sequential) # Multisession plan(multisession) # Future lapply library(future.apply) future_lapply(1:10, function(x) x^2) ``` ## Performance Optimization ### Profiling ```r # Profile code Rprof("profile.out") source("my_script.R") Rprof(NULL) # View results summaryRprof("profile.out") ``` ### Memory Usage ```r # Object size object.size(df) format(object.size(df), units = "Mb") # Memory usage gc() # List largest objects lsos <- function() { sort(sapply(ls(envir = globalenv()), object.size), decreasing = TRUE) } lsos() ``` ### Speeding Up Code ```r # Preallocate instead of growing # Slow: result <- c() for (i in 1:1000) { result <- c(result, i^2) } # Fast: result <- numeric(1000) for (i in 1:1000) { result[i] <- i^2 } # Or just: result <- (1:1000)^2 ``` ### Data Table for Large Data ```r library(data.table) # Fast reading dt <- fread("large_file.csv") # Fast aggregation dt[, .(mean = mean(value)), by = group] # Fast joins merge(dt1, dt2, on = "key") ``` ## Functional Programming ### Closures ```r # Function that returns function make_power <- function(exp) { function(x) x^exp } square <- make_power(2) cube <- make_power(3) ``` ### purrr Functions ```r library(purrr) # map - apply function to each element map(c(1, 2, 3), ~ .x^2) # list(1, 4, 9) # map_dbl - return double vector map_dbl(c(1, 2, 3), ~ .x^2) # 1 4 9 # safely - handle errors safe_read_csv <- safely(read_csv) result <- safe_read_csv("file.csv") result$result # data or NULL result$error # error or NULL # possibly - provide default map_chr(list(1, 2, 3), possibly(~ as.character(.x), "default")) ``` ## Summary - Source scripts with `source()` - Validate inputs in functions - Use `tryCatch()` for error handling - Debug with `browser()`, `debug()`, `traceback()` - S3 and R6 for OOP patterns - Consider parallel processing for large loops - Profile before optimizing - Use data.table for large datasets - purrr for functional programming

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →