Introducción al análisis de datos para otras carreras
# Convertir múltiples temperaturas temp1_k <- (temp1_c + 273.15) temp2_k <- (temp2_c + 273.15) temp3_k <- (temp3_c + 273.15) temp4_k <- (temp4_c + 273.15) # ... y así sucesivamente
# Crear función una vez celsius_a_kelvin <- function(celsius) { kelvin <- celsius + 273.15 return(kelvin) } # Usar múltiples veces temp1_k <- celsius_a_kelvin(temp1_c) temp2_k <- celsius_a_kelvin(temp2_c) temp3_k <- celsius_a_kelvin(temp3_c) temp4_k <- celsius_a_kelvin(temp4_c)
Resultado: Menos código, más claro, menos errores
nombre_funcion <- function(parametro1, parametro2) { # Cuerpo de la función resultado <- parametro1 + parametro2 # Valor de retorno return(resultado) }
calcular_promedio
mejor que calc
convertir_unidades
calcular
, convertir
, analizar
promedio_ponderado
# Función para calcular IMC calcular_imc <- function(peso_kg, altura_m) { # Validar datos de entrada if (peso_kg <= 0 || altura_m <= 0) { return("Error: valores deben ser positivos") } # Cálculo del IMC imc <- peso_kg / (altura_m^2) # Clasificación if (imc < 18.5) { categoria <- "Bajo peso" } else if (imc < 25) { categoria <- "Normal" } else if (imc < 30) { categoria <- "Sobrepeso" } else { categoria <- "Obesidad" } # Resultado detallado resultado <- list( imc = round(imc, 2), categoria = categoria ) return(resultado) } # Uso de la función resultado <- calcular_imc(70, 1.75) print(resultado)
return(42)
return(c(1, 2, 3))
return(list(media=5, sd=2))
return(data.frame(...))
# Función con parámetros obligatorios calcular_velocidad <- function(distancia, tiempo) { velocidad <- distancia / tiempo return(velocidad) } # Uso: AMBOS parámetros requeridos vel <- calcular_velocidad(100, 10) # OK # vel <- calcular_velocidad(100) # Error!
# Función con valores por defecto calcular_interes <- function(capital, tasa, tiempo = 1) { interes <- capital * tasa * tiempo return(interes) } # Uso flexible int1 <- calcular_interes(1000, 0.05) # tiempo = 1 int2 <- calcular_interes(1000, 0.05, 2) # tiempo = 2
# Función que devuelve múltiples estadísticas analizar_datos <- function(datos) { resultado <- list( media = mean(datos), mediana = median(datos), desviacion = sd(datos), maximo = max(datos), minimo = min(datos) ) return(resultado) } # Uso numeros <- c(10, 20, 30, 40, 50) stats <- analizar_datos(numeros) print(stats$media) # Acceso individual print(stats$mediana) # Acceso individual
calcular_inflacion <- function(precio_inicial, precio_final, mostrar_porcentaje = TRUE) { inflacion <- (precio_final - precio_inicial) / precio_inicial if (mostrar_porcentaje) { return(inflacion * 100) } else { return(inflacion) } }
analizar_likert <- function(respuestas, escala_min = 1, escala_max = 5) { # Validar que las respuestas estén en el rango validas <- respuestas >= escala_min & respuestas <= escala_max if (!all(validas)) { warning("Algunas respuestas están fuera del rango") } resultado <- list( promedio = mean(respuestas, na.rm = TRUE), moda = as.numeric(names(sort(table(respuestas), decreasing = TRUE)[1])), frecuencias = table(respuestas) ) return(resultado) }
Importante: Siempre valida los datos de entrada para evitar errores inesperados
# Variables locales - solo existen dentro de la función mi_funcion <- function(x) { variable_local <- x * 2 # Solo existe aquí return(variable_local) } resultado <- mi_funcion(5) print(resultado) # 10 # print(variable_local) # Error! No existe fuera
# Variable global - accesible desde cualquier lugar variable_global <- 100 mi_funcion2 <- function(x) { # Puede acceder a la variable global resultado <- x + variable_global return(resultado) } print(mi_funcion2(5)) # 105
Cuidado: Evita modificar variables globales dentro de funciones. Puede causar comportamientos inesperados.
# Mejor práctica calcular_con_base <- function(x, base) { resultado <- x + base return(resultado) } # Uso explícito print(calcular_con_base(5, 100)) # 105
temperatura_inicial
no t1
precio_producto
concentracion
no conc
calcular_
, convertir_
calcular_promedio_ponderado
analizar_datos
no funcion_analizar_datos
# Función con debugging calcular_nota_final <- function(parciales, proyecto, participacion) { # Debug: mostrar valores de entrada cat("Parciales:", parciales, "\n") cat("Proyecto:", proyecto, "\n") cat("Participación:", participacion, "\n") # Validación if (any(c(parciales, proyecto, participacion) < 0 | c(parciales, proyecto, participacion) > 100)) { return("Error: notas deben estar entre 0 y 100") } # Cálculo nota_final <- (parciales * 0.6) + (proyecto * 0.3) + (participacion * 0.1) # Debug: mostrar resultado cat("Nota final calculada:", nota_final, "\n") return(round(nota_final, 2)) }
Tip: Usa cat()
o print()
para seguir el flujo de tu función durante el desarrollo
# Instalar desde CRAN install.packages("nombre_libreria") # Instalar múltiples install.packages(c("ggplot2", "dplyr", "readr"))
# Cargar librería library(nombre_libreria) # Verificar si está instalada if (!require(ggplot2)) { install.packages("ggplot2") library(ggplot2) }
Incluidas por defecto:
stats
- Funciones estadísticasutils
- Utilidades generalesgraphics
- Gráficos básicospsych
- Análisis psicométricocar
- Análisis de regresiónmoments
- Momentos estadísticosggplot2
- Gráficos avanzadosplotly
- Gráficos interactivoscorrplot
- Matrices de correlacióndplyr
- Manipulación de data framesreadr
- Lectura de archivostidyr
- Organización de datosCRAN: Comprehensive R Archive Network - repositorio oficial con más de 18,000 librerías
# Datos de ejemplo datos <- c(10, 15, 20, 25, 30, 35, 40) # Medidas básicas media <- mean(datos) # Media aritmética mediana <- median(datos) # Mediana moda <- names(table(datos))[table(datos) == max(table(datos))] print(paste("Media:", media)) print(paste("Mediana:", mediana))
# Extremos minimo <- min(datos) maximo <- max(datos) rango <- max(datos) - min(datos) # Cuartiles y percentiles cuartiles <- quantile(datos, c(0.25, 0.5, 0.75)) percentil_90 <- quantile(datos, 0.90) # Resumen completo resumen <- summary(datos) print(resumen)
# Variabilidad varianza <- var(datos) # Varianza muestral desv_estandar <- sd(datos) # Desviación estándar coef_variacion <- sd(datos) / mean(datos) * 100 # Rango intercuartílico iqr <- IQR(datos) print(paste("CV:", round(coef_variacion, 2), "%"))
# Datos de población de especies poblacion <- c(120, 135, 142, 158, 163, 170, 185, 192) # Tasa de crecimiento tasa_crecimiento <- diff(poblacion) / poblacion[-length(poblacion)] * 100 # Tendencia tendencia <- lm(poblacion ~ seq_along(poblacion)) print(summary(tendencia)$coefficients[2,1]) # Pendiente
# Precios de productos precios <- c(1200, 1250, 1180, 1300, 1275, 1320, 1290) # Volatilidad (coeficiente de variación) volatilidad <- sd(precios) / mean(precios) * 100 # Detección de outliers Q1 <- quantile(precios, 0.25) Q3 <- quantile(precios, 0.75) outliers <- precios[precios < Q1 - 1.5*IQR(precios) | precios > Q3 + 1.5*IQR(precios)] print(paste("Volatilidad:", round(volatilidad, 2), "%")) print(paste("Outliers encontrados:", length(outliers)))
# Notas de estudiantes notas <- c(85, 92, 78, 88, 91, 84, 89, 95, 82, 90) # Distribución de notas aprobados <- sum(notas >= 70) porcentaje_aprobacion <- aprobados / length(notas) * 100 # Clasificación excelente <- sum(notas >= 90) bueno <- sum(notas >= 80 & notas < 90) regular <- sum(notas >= 70 & notas < 80) print(paste("Aprobación:", porcentaje_aprobacion, "%")) print(paste("Excelente:", excelente, "estudiantes"))
# Instalar y cargar # install.packages("psych") library(psych) # Análisis descriptivo completo datos_psico <- c(4, 5, 3, 4, 5, 2, 4, 3, 5, 4) describe(datos_psico) # Alfa de Cronbach (confiabilidad) # Para escalas múltiples escala <- data.frame( item1 = c(4, 5, 3, 4, 5), item2 = c(3, 4, 4, 3, 5), item3 = c(4, 4, 3, 4, 4) ) alpha(escala)
# install.packages("moments") library(moments) datos <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) # Asimetría (skewness) asimetria <- skewness(datos) print(paste("Asimetría:", round(asimetria, 3))) # Curtosis (kurtosis) curtosis <- kurtosis(datos) print(paste("Curtosis:", round(curtosis, 3))) # Interpretación automática if (abs(asimetria) < 0.5) { print("Distribución simétrica") } else { print("Distribución asimétrica") }
# Para estudiantes de agronomía/biología # install.packages("agricolae") library(agricolae) # Diseño completamente aleatorizado tratamientos <- c("Control", "Fertilizante_A", "Fertilizante_B") repeticiones <- 4 diseño <- design.crd(tratamientos, repeticiones) print(diseño$book) # Análisis de varianza para experimentos # rendimiento <- c(10, 12, 15, 11, 14, 16, 13, 15, 18, 16, 17, 19) # tratamiento <- rep(tratamientos, each = 4) # modelo <- aov(rendimiento ~ tratamiento) # LSD.test(modelo, "tratamiento")
# Para estudiantes de economía/finanzas # install.packages("quantmod") library(quantmod) # Simulación de precios (datos reales requieren internet) precios <- c(100, 102, 98, 105, 107, 103, 110, 108, 112, 115) fechas <- seq(as.Date("2025-01-01"), by="day", length.out=10) # Retornos diarios retornos <- diff(log(precios)) * 100 # Media y volatilidad de retornos media_retorno <- mean(retornos) volatilidad <- sd(retornos) print(paste("Retorno promedio:", round(media_retorno, 2), "%")) print(paste("Volatilidad:", round(volatilidad, 2), "%"))
# Función para conversiones múltiples convertir_energia <- function(valor, de, a) { # Factor de conversión a Joules a_joules <- list( "cal" = 4.184, # calorías "kcal" = 4184, # kilocalorías "eV" = 1.602e-19, # electronvolts "J" = 1 # joules ) # Convertir a joules primero, luego a unidad final joules <- valor * a_joules[[de]] resultado <- joules / a_joules[[a]] return(round(resultado, 6)) } # Ejemplos de uso cal_a_joules <- convertir_energia(100, "cal", "J") print(paste("100 cal =", cal_a_joules, "J")) ev_a_cal <- convertir_energia(1, "eV", "cal") print(paste("1 eV =", ev_a_cal, "cal"))
# Función para análisis de rentabilidad analisis_inversion <- function(inversion_inicial, flujos_anuales, tasa_descuento) { # Valor presente neto (VPN) periodos <- 1:length(flujos_anuales) flujos_descontados <- flujos_anuales / (1 + tasa_descuento)^periodos vpn <- sum(flujos_descontados) - inversion_inicial # Tasa interna de retorno (aproximación) # Función simple para encontrar TIR tir_aprox <- function(tasa) { flujos_desc <- flujos_anuales / (1 + tasa)^periodos abs(sum(flujos_desc) - inversion_inicial) } # Buscar TIR entre 0% y 50% tasas <- seq(0, 0.5, 0.001) tir <- tasas[which.min(sapply(tasas, tir_aprox))] # Periodo de recuperación flujos_acum <- cumsum(flujos_anuales) periodo_recup <- which(flujos_acum >= inversion_inicial)[1] resultado <- list( vpn = round(vpn, 2), tir = round(tir * 100, 2), periodo_recuperacion = periodo_recup, recomendacion = ifelse(vpn > 0, "ACEPTAR", "RECHAZAR") ) return(resultado) } # Ejemplo: inversión de $10,000 con flujos de $3,000 anuales por 5 años resultado <- analisis_inversion(10000, rep(3000, 5), 0.10) print(resultado)
# Función para calcular distancia entre coordenadas distancia_geografica <- function(lat1, lon1, lat2, lon2, unidad = "km") { # Fórmula de Haversine rad <- pi / 180 dlat <- (lat2 - lat1) * rad dlon <- (lon2 - lon1) * rad a <- sin(dlat/2)^2 + cos(lat1 * rad) * cos(lat2 * rad) * sin(dlon/2)^2 c <- 2 * atan2(sqrt(a), sqrt(1-a)) radio_tierra <- ifelse(unidad == "km", 6371, 3959) # km o millas distancia <- radio_tierra * c return(round(distancia, 2)) } # Ejemplo: San José a Cartago (aproximado) san_jose <- c(9.9281, -84.0907) cartago <- c(9.8644, -83.9186) dist <- distancia_geografica(san_jose[1], san_jose[2], cartago[1], cartago[2]) print(paste("Distancia San José - Cartago:", dist, "km"))
# Función para analizar escalas de satisfacción analizar_satisfaccion <- function(respuestas, etiquetas = NULL) { if (is.null(etiquetas)) { etiquetas <- c("Muy insatisfecho", "Insatisfecho", "Neutral", "Satisfecho", "Muy satisfecho") } # Estadísticas básicas media <- mean(respuestas, na.rm = TRUE) mediana <- median(respuestas, na.rm = TRUE) # Distribución de frecuencias frecuencias <- table(respuestas) porcentajes <- prop.table(frecuencias) * 100 # Indicadores clave satisfechos <- sum(respuestas >= 4, na.rm = TRUE) total_validos <- sum(!is.na(respuestas)) porcentaje_satisfaccion <- (satisfechos / total_validos) * 100 # Nivel de consenso (baja dispersión = alto consenso) consenso <- ifelse(sd(respuestas, na.rm = TRUE) < 1, "Alto", "Bajo") resultado <- list( media = round(media, 2), mediana = mediana, frecuencias = as.vector(frecuencias), porcentajes = round(as.vector(porcentajes), 1), satisfaccion_general = round(porcentaje_satisfaccion, 1), nivel_consenso = consenso, interpretacion = etiquetas[round(media)] ) return(resultado) } # Ejemplo: encuesta de satisfacción con un servicio respuestas <- c(4, 5, 3, 4, 5, 2, 4, 3, 5, 4, 4, 5, 3, 4, 4) resultado <- analizar_satisfaccion(respuestas) print(paste("Satisfacción promedio:", resultado$interpretacion)) print(paste("% Satisfechos:", resultado$satisfaccion_general, "%"))
# Función robusta para cálculo de IMC calcular_imc_robusto <- function(peso, altura, unidad_peso = "kg", unidad_altura = "m") { # Validar tipos de datos if (!is.numeric(peso) || !is.numeric(altura)) { stop("Peso y altura deben ser números") } # Validar valores positivos if (peso <= 0 || altura <= 0) { stop("Peso y altura deben ser valores positivos") } # Conversiones de unidades if (unidad_peso == "lb") { peso <- peso * 0.453592 # libras a kg } if (unidad_altura == "cm") { altura <- altura / 100 # cm a metros } else if (unidad_altura == "ft") { altura <- altura * 0.3048 # pies a metros } # Validar rangos razonables if (peso < 1 || peso > 500) { warning("Peso fuera del rango típico (1-500 kg)") } if (altura < 0.3 || altura > 3.0) { warning("Altura fuera del rango típico (0.3-3.0 m)") } # Cálculo imc <- peso / (altura^2) # Clasificación según OMS clasificacion <- case_when( imc < 18.5 ~ "Bajo peso", imc < 25 ~ "Normal", imc < 30 ~ "Sobrepeso", imc < 35 ~ "Obesidad clase I", imc < 40 ~ "Obesidad clase II", TRUE ~ "Obesidad clase III" ) # Usar función base si case_when no está disponible if (!exists("case_when")) { if (imc < 18.5) { clasificacion <- "Bajo peso" } else if (imc < 25) { clasificacion <- "Normal" } else if (imc < 30) { clasificacion <- "Sobrepeso" } else if (imc < 35) { clasificacion <- "Obesidad clase I" } else if (imc < 40) { clasificacion <- "Obesidad clase II" } else { clasificacion <- "Obesidad clase III" } } resultado <- list( imc = round(imc, 2), clasificacion = clasificacion, peso_usado_kg = round(peso, 2), altura_usada_m = round(altura, 2) ) return(resultado) }
# Función segura para lectura de archivos leer_datos_seguro <- function(archivo) { resultado <- tryCatch({ # Intentar leer el archivo datos <- read.csv(archivo, stringsAsFactors = FALSE) # Validar que no esté vacío if (nrow(datos) == 0) { warning("El archivo está vacío") return(NULL) } # Retornar datos con información adicional list( datos = datos, filas = nrow(datos), columnas = ncol(datos), exito = TRUE, mensaje = "Archivo leído exitosamente" ) }, error = function(e) { # En caso de error list( datos = NULL, filas = 0, columnas = 0, exito = FALSE, mensaje = paste("Error al leer archivo:", e$message) ) }) return(resultado) } # Uso de la función # resultado <- leer_datos_seguro("datos.csv") # if (resultado$exito) { # print("Datos cargados correctamente") # print(head(resultado$datos)) # } else { # print(resultado$mensaje) # }
# Función para calcular promedio con diferentes métodos calcular_promedio_avanzado <- function(datos, metodo = "aritmetico", na.rm = TRUE, trim = 0) { # Validar datos if (!is.numeric(datos)) { stop("Los datos deben ser numéricos") } # Remover NA si se solicita if (na.rm) { datos <- datos[!is.na(datos)] } # Verificar que quedan datos if (length(datos) == 0) { return(NA) } # Calcular según el método resultado <- switch(metodo, "aritmetico" = mean(datos, trim = trim), "geometrico" = exp(mean(log(datos[datos > 0]))), "armonico" = 1/mean(1/datos[datos != 0]), "mediana" = median(datos), "trimmed" = mean(datos, trim = 0.1), { warning("Método no reconocido, usando aritmético") mean(datos) } ) return(round(resultado, 4)) } # Ejemplos de uso datos_ejemplo <- c(10, 20, 30, 15, 25) # Diferentes métodos media_arit <- calcular_promedio_avanzado(datos_ejemplo, "aritmetico") media_geom <- calcular_promedio_avanzado(datos_ejemplo, "geometrico") mediana <- calcular_promedio_avanzado(datos_ejemplo, "mediana") print(paste("Media aritmética:", media_arit)) print(paste("Media geométrica:", media_geom)) print(paste("Mediana:", mediana))
Importante: Siempre incluye validación de datos y manejo de errores en funciones que serán usadas por otros
# Archivo: funciones_quimica.R # Función para conversión de concentraciones molar_a_ppm <- function(molaridad, peso_molecular) { ppm <- molaridad * peso_molecular * 1000 return(ppm) } # Función para pH a concentración H+ ph_a_concentracion <- function(ph) { concentracion_h <- 10^(-ph) return(concentracion_h) } # Función para calcular diluciones calcular_dilucion <- function(c1, v1, c2) { # C1*V1 = C2*V2 v2 <- (c1 * v1) / c2 return(v2) } # Función para validar rangos químicos validar_ph <- function(ph) { if (ph < 0 || ph > 14) { warning("pH fuera del rango normal (0-14)") return(FALSE) } return(TRUE) }
# Cargar todas las funciones del archivo source("funciones_quimica.R") # Ahora puedes usar las funciones concentracion_nacl <- molar_a_ppm(0.1, 58.44) print(paste("NaCl 0.1M =", concentracion_nacl, "ppm")) volumen_final <- calcular_dilucion(1, 10, 0.1) print(paste("Volumen final:", volumen_final, "mL"))
#' Convertir molaridad a partes por millón #' #' Esta función convierte una concentración en molaridad #' a partes por millón (ppm) #' #' @param molaridad Concentración en mol/L #' @param peso_molecular Peso molecular del compuesto en g/mol #' @return Concentración en ppm #' @examples #' molar_a_ppm(0.1, 58.44) # NaCl 0.1M #' @export molar_a_ppm <- function(molaridad, peso_molecular) { # Validar entrada if (!is.numeric(molaridad) || !is.numeric(peso_molecular)) { stop("Molaridad y peso molecular deben ser números") } if (molaridad < 0 || peso_molecular <= 0) { stop("Valores deben ser positivos") } ppm <- molaridad * peso_molecular * 1000 return(round(ppm, 2)) }
# Funciones para análisis educativo calcular_promedio_ponderado <- function(notas, pesos) { if (length(notas) != length(pesos)) { stop("Notas y pesos deben tener la misma longitud") } if (sum(pesos) != 1) { warning("Los pesos no suman 1, normalizando...") pesos <- pesos / sum(pesos) } promedio <- sum(notas * pesos) return(round(promedio, 2)) } analizar_desercion <- function(matriculados, graduados) { tasa_graduacion <- (graduados / matriculados) * 100 tasa_desercion <- 100 - tasa_graduacion return(list( graduacion = round(tasa_graduacion, 2), desercion = round(tasa_desercion, 2) )) } clasificar_rendimiento <- function(promedio) { if (promedio >= 90) return("Excelente") if (promedio >= 80) return("Muy bueno") if (promedio >= 70) return("Bueno") if (promedio >= 60) return("Regular") return("Deficiente") }
# Funciones para análisis económico calcular_inflacion_anual <- function(precios_inicial, precios_final) { inflacion <- ((precios_final - precios_inicial) / precios_inicial) * 100 return(round(inflacion, 2)) } valor_presente <- function(valor_futuro, tasa, periodos) { vp <- valor_futuro / (1 + tasa)^periodos return(round(vp, 2)) } indice_precios <- function(precios_actuales, precios_base, base = 100) { indice <- (precios_actuales / precios_base) * base return(round(indice, 2)) }
Funciones que pueden operar sobre vectores completos de una vez, en lugar de elemento por elemento.
# Método con bucle (lento) temperaturas_c <- c(20, 25, 30, 35, 40) temperaturas_k <- numeric(length(temperaturas_c)) for (i in 1:length(temperaturas_c)) { temperaturas_k[i] <- temperaturas_c[i] + 273.15 } # Método vectorizado (rápido) temperaturas_k <- temperaturas_c + 273.15 print(temperaturas_k)
# Función que ya es vectorizada convertir_temperatura <- function(temp_c) { # Automáticamente funciona con vectores temp_k <- temp_c + 273.15 return(temp_k) } # Función que requiere vectorización clasificar_temperatura_simple <- function(temp) { if (temp < 0) return("Congelación") if (temp < 25) return("Frío") if (temp < 35) return("Moderado") return("Caliente") } # Vectorizar función no vectorizada clasificar_temperatura <- Vectorize(clasificar_temperatura_simple) # Uso vectorizado temps <- c(-5, 10, 20, 30, 40) clasificaciones <- clasificar_temperatura(temps) print(clasificaciones)
# Datos de ejemplo estudiantes <- data.frame( nombre = c("Ana", "Luis", "María", "Pedro"), parcial1 = c(85, 90, 78, 92), parcial2 = c(88, 87, 82, 89), proyecto = c(90, 95, 85, 88) ) # Aplicar función a columnas numéricas promedios <- apply(estudiantes[, 2:4], 1, mean) estudiantes$promedio <- round(promedios, 1) # Función para clasificar múltiples estudiantes clasificar_multiples <- function(promedios) { ifelse(promedios >= 90, "Excelente", ifelse(promedios >= 80, "Muy bueno", ifelse(promedios >= 70, "Bueno", "Regular"))) } estudiantes$categoria <- clasificar_multiples(estudiantes$promedio) print(estudiantes)
# Análisis de datos por grupos datos_ventas <- data.frame( region = rep(c("Norte", "Sur", "Este", "Oeste"), each = 3), mes = rep(c("Ene", "Feb", "Mar"), 4), ventas = c(100, 120, 110, 90, 95, 105, 85, 100, 95, 110, 125, 115) ) # Calcular promedio por región (vectorizado) promedio_por_region <- tapply(datos_ventas$ventas, datos_ventas$region, mean) # Función personalizada para análisis completo analisis_por_grupo <- function(valores, grupos) { resultado <- list( promedio = tapply(valores, grupos, mean), maximo = tapply(valores, grupos, max), minimo = tapply(valores, grupos, min), cv = tapply(valores, grupos, function(x) sd(x)/mean(x)*100) ) return(resultado) } analisis <- analisis_por_grupo(datos_ventas$ventas, datos_ventas$region) print(analisis$promedio)
Ventaja: Las funciones vectorizadas son mucho más rápidas y eficientes que los bucles, especialmente con grandes conjuntos de datos
# Función con debugging incorporado calcular_estadisticas_debug <- function(datos, mostrar_proceso = FALSE) { if (mostrar_proceso) { cat("Iniciando análisis de", length(datos), "observaciones\n") } # Verificar datos faltantes datos_limpios <- datos[!is.na(datos)] faltantes <- length(datos) - length(datos_limpios) if (mostrar_proceso && faltantes > 0) { cat("Removidos", faltantes, "valores faltantes\n") } # Calcular estadísticas if (mostrar_proceso) { cat("Calculando estadísticas...\n") } media <- mean(datos_limpios) mediana <- median(datos_limpios) desv <- sd(datos_limpios) if (mostrar_proceso) { cat("Proceso completado exitosamente\n") } return(list(media = media, mediana = mediana, sd = desv)) } # Uso con debugging resultado <- calcular_estadisticas_debug(c(1, 2, NA, 4, 5), mostrar_proceso = TRUE)
# Función con punto de parada para debugging funcion_compleja <- function(x, y, z) { resultado1 <- x + y # Punto de parada para inspección # browser() # Descomenta para debugging interactivo resultado2 <- resultado1 * z if (resultado2 > 100) { browser() # Parada condicional } return(resultado2) } # Durante debugging podrás: # - Ver valores de variables # - Ejecutar código paso a paso # - Modificar variables temporalmente
# Función para testing test_conversion_temperatura <- function() { # Test 1: Casos conocidos resultado1 <- celsius_a_kelvin(0) if (abs(resultado1 - 273.15) > 0.001) { stop("Error: 0°C debe ser 273.15K") } # Test 2: Vectorización resultado2 <- celsius_a_kelvin(c(0, 100)) esperado <- c(273.15, 373.15) if (!all(abs(resultado2 - esperado) < 0.001)) { stop("Error en vectorización") } # Test 3: Casos extremos resultado3 <- celsius_a_kelvin(-273.15) if (abs(resultado3 - 0) > 0.001) { stop("Error: -273.15°C debe ser 0K") } cat("Todos los tests pasaron exitosamente\n") return(TRUE) } # Ejecutar tests test_conversion_temperatura()
# Función helper para tests assert_equal <- function(actual, esperado, tolerancia = 0.001, mensaje = "") { if (!all(abs(actual - esperado) < tolerancia)) { stop(paste("Test falló:", mensaje, "\nEsperado:", esperado, "\nObtenido:", actual)) } cat("✓ Test pasó:", mensaje, "\n") } # Suite de tests para función estadística test_suite_estadisticas <- function() { cat("Ejecutando suite de tests...\n\n") # Datos de prueba datos_test <- c(10, 20, 30, 40, 50) # Test media resultado_media <- mean(datos_test) assert_equal(resultado_media, 30, mensaje = "Cálculo de media") # Test desviación estándar resultado_sd <- sd(datos_test) assert_equal(resultado_sd, 15.81139, mensaje = "Cálculo de SD") # Test con datos faltantes datos_na <- c(10, 20, NA, 40, 50) resultado_na <- mean(datos_na, na.rm = TRUE) assert_equal(resultado_na, 30, mensaje = "Media con NA") cat("\n🎉 Todos los tests completados exitosamente!\n") } # Ejecutar suite completa test_suite_estadisticas()
Buena práctica: Crea tests para cada función que escribas. Te ahorrará tiempo detectando errores temprano
"Don't Repeat Yourself (DRY): Si lo escribes dos veces, crea una función"
"Las funciones transforman código repetitivo en herramientas poderosas. Las librerías te conectan con el conocimiento colectivo de miles de desarrolladores. Juntas, te convierten en un analista de datos eficiente."
¡Gracias por su atención!
¿Preguntas sobre funciones o librerías?