← Volver al inicio
Universidad Nacional de Costa Rica

EIY403

Introducción al análisis de datos para otras carreras

Clase 7: Funciones y librerías en R
II Semestre 2025
Escuela de Informática y Computación
Funciones y librerías en R
1 / 15
Universidad Nacional de Costa Rica

Agenda de la clase

Bloque 1: Funciones en R

  • ¿Por qué crear funciones propias?
  • Anatomía de una función
  • Creación paso a paso
  • Parámetros y valores de retorno
  • Funciones para diferentes disciplinas

Bloque 2: Variables en funciones

  • Alcance de variables (scope)
  • Variables locales vs globales
  • Buenas prácticas en naming
  • Debugging de funciones

Bloque 3: Librerías esenciales

  • ¿Qué son las librerías?
  • Instalación y carga
  • Librerías para estadísticas básicas
  • Funciones estadísticas útiles

Bloque 4: Aplicaciones interdisciplinarias

  • Funciones para ciencias naturales
  • Análisis de datos económicos
  • Aplicaciones en ciencias sociales
  • Herramientas para ingeniería
2 / 15
Universidad Nacional de Costa Rica

¿Por qué crear nuestras propias funciones?

"Las funciones nos permiten automatizar tareas repetitivas y crear herramientas personalizadas para nuestro campo de estudio"

Ventajas de crear funciones

  • Reutilización: Escribir una vez, usar muchas veces
  • Organización: Código más limpio y estructurado
  • Mantenimiento: Cambios centralizados
  • Legibilidad: Nombres descriptivos vs. código repetido
  • Testing: Probar componentes individuales
  • Colaboración: Compartir herramientas con colegas

Casos típicos universitarios

  • Ciencias: Conversiones de unidades repetitivas
  • Economía: Cálculos de indicadores específicos
  • Psicología: Análisis de escalas y cuestionarios
  • Ingeniería: Fórmulas técnicas complejas

Ejemplo: sin función vs con función

Sin función (repetitivo)

# 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

Con función (elegante)

# 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

3 / 15
Universidad Nacional de Costa Rica

Anatomía de una función en R

Estructura básica

nombre_funcion <- function(parametro1, parametro2) {
    # Cuerpo de la función
    resultado <- parametro1 + parametro2
    
    # Valor de retorno
    return(resultado)
}

Componentes esenciales

  • Nombre: Identificador único y descriptivo
  • function(): Palabra clave que define la función
  • Parámetros: Variables de entrada (argumentos)
  • Cuerpo: Código que realiza el trabajo
  • return(): Valor que devuelve la función

Convenciones de nombres

  • Descriptivos: calcular_promedio mejor que calc
  • Snake case: convertir_unidades
  • Verbos: calcular, convertir, analizar
  • Sin espacios: promedio_ponderado

Ejemplo detallado: función para biología

# 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)

Tipos de retorno

  • Número único: return(42)
  • Vector: return(c(1, 2, 3))
  • Lista: return(list(media=5, sd=2))
  • Data frame: return(data.frame(...))
4 / 15
Universidad Nacional de Costa Rica

Parámetros: la entrada de nuestras funciones

Tipos de parámetros

Parámetros obligatorios

# 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!

Parámetros con valores por defecto

# 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

Múltiples valores de retorno

# 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

Ejemplos interdisciplinarios

Economía
Cálculo de inflación
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)
    }
}
Psicología
Análisis de escala Likert
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

5 / 15
Universidad Nacional de Costa Rica

Variables en funciones: scope y buenas prácticas

Alcance de variables (scope)

Variables locales

# 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

Variables globales

# 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.

Buena práctica: pasar todo como parámetros

# Mejor práctica
calcular_con_base <- function(x, base) {
    resultado <- x + base
    return(resultado)
}

# Uso explícito
print(calcular_con_base(5, 100))  # 105

Convenciones de nombres

Para variables

  • Descriptivas: temperatura_inicial no t1
  • Snake case: precio_producto
  • Sin abreviaciones: concentracion no conc
  • Consistentes: Mismo estilo en todo el código

Para funciones

  • Verbos de acción: calcular_, convertir_
  • Específicas: calcular_promedio_ponderado
  • Sin redundancia: analizar_datos no funcion_analizar_datos

Debugging de funciones

# 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

6 / 15
Universidad Nacional de Costa Rica

Librerías en R: ampliando nuestras herramientas

"Las librerías son colecciones de funciones especializadas creadas por la comunidad global de R"

¿Qué son las librerías?

  • Paquetes de funciones: Herramientas especializadas
  • Código compartido: Desarrollado por expertos
  • Documentación incluida: Manuales y ejemplos
  • Actualizaciones regulares: Mejoras continuas
  • Gratis y open source: Acceso libre

Flujo de trabajo con librerías

1. Instalación (una sola vez)
# Instalar desde CRAN
install.packages("nombre_libreria")

# Instalar múltiples
install.packages(c("ggplot2", "dplyr", "readr"))
2. Carga (cada sesión)
# Cargar librería
library(nombre_libreria)

# Verificar si está instalada
if (!require(ggplot2)) {
    install.packages("ggplot2")
    library(ggplot2)
}

Librerías esenciales para principiantes

Base R

Incluidas por defecto:

  • stats - Funciones estadísticas
  • utils - Utilidades generales
  • graphics - Gráficos básicos
Para estadísticas
  • psych - Análisis psicométrico
  • car - Análisis de regresión
  • moments - Momentos estadísticos
Para visualización
  • ggplot2 - Gráficos avanzados
  • plotly - Gráficos interactivos
  • corrplot - Matrices de correlación
Para manipulación de datos
  • dplyr - Manipulación de data frames
  • readr - Lectura de archivos
  • tidyr - Organización de datos

CRAN: Comprehensive R Archive Network - repositorio oficial con más de 18,000 librerías

7 / 15
Universidad Nacional de Costa Rica

Funciones estadísticas esenciales

Funciones base de R

Medidas de tendencia central

# 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))

Valores extremos y posición

# 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)

Medidas de dispersión

# 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), "%"))

Funciones útiles por disciplina

Biología
Análisis de poblaciones
# 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
Economía
Análisis de precios
# 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)))
Educación
Análisis de calificaciones
# 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"))
8 / 15
Universidad Nacional de Costa Rica

Librerías especializadas por área

Psych - Análisis psicológico
# 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)
Moments - Momentos estadísticos
# 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")
}
Agricolae - Diseño experimental
# 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")
Quantmod - Análisis financiero
# 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), "%"))
Consejos para elegir librerías
  • Popularidad: Revisa el número de descargas en CRAN
  • Documentación: Busca librerías con buenos manuales
  • Mantenimiento: Verifica que tengan actualizaciones recientes
  • Comunidad: Prefiere librerías con soporte activo
  • Compatibilidad: Asegúrate de que funcionen con tu versión de R
9 / 15
Universidad Nacional de Costa Rica

Casos prácticos: funciones para diferentes disciplinas

Física
Conversiones de unidades
# 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"))
Administración
Análisis financiero básico
# 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)
Geografía
Análisis de coordenadas
# 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"))
Sociología
Análisis de encuestas
# 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, "%"))
10 / 15
Universidad Nacional de Costa Rica

Funciones robustas: validación y manejo de errores

Validación de entrada

# 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)
}

Manejo de errores con try/catch

# 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)
# }

Funciones con múltiples métodos

# 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

11 / 15
Universidad Nacional de Costa Rica

Organizando funciones: hacia tus propias librerías

Creación de archivos de funciones

# 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 funciones personalizadas

# 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"))

Documentación de funciones

#' 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))
}

Organización por módulos temáticos

Archivo: estadisticas_educacion.R

# 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")
}

Archivo: analisis_economico.R

# 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))
}

Mejores prácticas

  • Un archivo por tema: Funciones relacionadas juntas
  • Nombres descriptivos: Para archivos y funciones
  • Documentación completa: Qué hace, parámetros, ejemplos
  • Validación consistente: Verificar datos de entrada
  • Testing: Probar con datos conocidos
12 / 15
Universidad Nacional de Costa Rica

Funciones vectorizadas: procesamiento eficiente

¿Qué son las funciones vectorizadas?

Funciones que pueden operar sobre vectores completos de una vez, en lugar de elemento por elemento.

Comparación: bucle vs vectorización

# 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)

Crear funciones vectorizadas

# 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)

Funciones vectorizadas útiles

Para aplicar a columnas

# 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)

Para análisis por grupos

# 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

13 / 15
Universidad Nacional de Costa Rica

Debugging y testing de funciones

Técnicas de debugging

Mensajes informativos

# 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 browser() para debugging interactivo

# 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

Testing de funciones

Tests básicos

# 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()

Tests con validación automática

# 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

14 / 15
Universidad Nacional de Costa Rica

Resumen y próxima clase

Lo que aprendimos hoy

  • Creación de funciones: Sintaxis, parámetros, valores de retorno
  • Variables y scope: Alcance local vs global
  • Librerías esenciales: Instalación, carga y uso básico
  • Funciones estadísticas: Herramientas para análisis de datos
  • Validación y testing: Crear funciones robustas
  • Aplicaciones interdisciplinarias: Funciones para diferentes carreras
  • Vectorización: Procesamiento eficiente de datos

Habilidades desarrolladas

  • Automatización: Crear herramientas reutilizables
  • Organización: Estructurar código de manera limpia
  • Debugging: Encontrar y corregir errores
  • Documentación: Escribir código autodocumentado
  • Testing: Validar funcionamiento correcto

Librerías que ahora conoces

  • psych: Análisis psicométrico
  • moments: Asimetría y curtosis
  • agricolae: Diseño experimental
  • quantmod: Análisis financiero

Para prepararse

  • Pensar en 3 tareas repetitivas de tu carrera
  • Identificar cálculos que haces frecuentemente
  • Revisar las funciones básicas de R
  • Traer ejemplos de datos de tu disciplina

Conceptos clave para recordar

Mantra del programador eficiente

"Don't Repeat Yourself (DRY): Si lo escribes dos veces, crea una función"

Reflexión final

"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?

15 / 15