R

R

Tutorial R: Funciones

Aprende a crear funciones en R, manejar argumentos posicionales, nombrados y predeterminados, y dominar el retorno de valores para código modular.

Aprende R y certifícate

Definición y sintaxis de funciones en R

Las funciones son bloques de código reutilizables que realizan una tarea específica en R. Funcionan como pequeñas "máquinas" que pueden recibir datos, procesarlos y devolver un resultado. El uso de funciones nos permite organizar mejor nuestro código, evitar repeticiones y hacer nuestros programas más legibles y mantenibles.

Estructura básica de una función

En R, una función se define utilizando la palabra clave function. La estructura general es:

nombre_funcion <- function(argumentos) {
  # Cuerpo de la función
  # Código que realiza operaciones
  
  return(valor)  # Opcional: valor que devuelve la función
}

Los componentes principales de una función son:

  • Nombre: Identificador que usaremos para llamar a la función
  • Argumentos: Valores que la función recibe para trabajar
  • Cuerpo: Conjunto de instrucciones que la función ejecuta
  • Valor de retorno: Resultado que la función devuelve al finalizar

Creando funciones simples

Veamos un ejemplo sencillo de una función que calcula el área de un rectángulo:

calcular_area <- function(largo, ancho) {
  area <- largo * ancho
  return(area)
}

Para utilizar esta función, simplemente la llamamos con los valores que queremos procesar:

# Calculamos el área de un rectángulo de 5x3
mi_area <- calcular_area(5, 3)
print(mi_area)  # Muestra 15

Funciones sin argumentos

También podemos crear funciones que no requieran ningún argumento:

saludar <- function() {
  mensaje <- "¡Hola, bienvenido a R!"
  return(mensaje)
}

# Llamamos a la función
saludo <- saludar()
print(saludo)

El retorno implícito

En R, si no utilizamos la instrucción return(), la función devolverá automáticamente el resultado de la última expresión evaluada. Esto se conoce como retorno implícito:

# Función con retorno implícito
calcular_area_implicita <- function(largo, ancho) {
  largo * ancho  # Esta expresión será el valor devuelto
}

# Ambas llamadas producen el mismo resultado
area1 <- calcular_area(5, 3)
area2 <- calcular_area_implicita(5, 3)
print(area1)  # 15
print(area2)  # 15

Aunque el retorno implícito funciona, es una buena práctica usar return() explícitamente en funciones complejas para mejorar la legibilidad del código.

Funciones con múltiples instrucciones

Las funciones pueden contener tantas líneas de código como necesitemos:

analizar_numero <- function(x) {
  # Realizamos varios cálculos
  cuadrado <- x^2
  cubo <- x^3
  raiz <- sqrt(x)
  
  # Creamos un vector con los resultados
  resultados <- c(original = x, cuadrado = cuadrado, 
                  cubo = cubo, raiz = raiz)
  
  return(resultados)
}

# Analizamos el número 4
analisis <- analizar_numero(4)
print(analisis)

Documentación de funciones

Es una buena práctica documentar nuestras funciones para que otros (o nosotros mismos en el futuro) entiendan qué hacen:

#' Calcula el área de un círculo
#' 
#' @param radio El radio del círculo
#' @return El área del círculo
area_circulo <- function(radio) {
  if (radio < 0) {
    stop("El radio no puede ser negativo")
  }
  
  area <- pi * radio^2
  return(area)
}

Verificación de argumentos

Es importante validar los argumentos que recibe nuestra función para evitar errores:

calcular_promedio <- function(numeros) {
  # Verificamos que el argumento sea un vector numérico
  if (!is.numeric(numeros)) {
    stop("El argumento debe ser un vector numérico")
  }
  
  # Verificamos que no esté vacío
  if (length(numeros) == 0) {
    warning("Vector vacío, devolviendo NA")
    return(NA)
  }
  
  # Calculamos el promedio
  suma <- sum(numeros)
  cantidad <- length(numeros)
  promedio <- suma / cantidad
  
  return(promedio)
}

# Probamos la función
calcular_promedio(c(10, 20, 30, 40))  # Devuelve 25

Alcance de variables

Las variables definidas dentro de una función tienen un alcance local, lo que significa que solo existen dentro de la función:

mi_funcion <- function() {
  variable_local <- 10
  print(paste("Dentro de la función:", variable_local))
  return(variable_local)
}

resultado <- mi_funcion()
# Esto generaría un error porque variable_local no existe fuera de la función
# print(variable_local)

Anidamiento de funciones

Podemos definir funciones dentro de otras funciones. Las funciones internas solo son accesibles desde la función que las contiene:

calcular_estadisticas <- function(datos) {
  # Función interna para calcular la varianza
  calcular_varianza <- function(valores) {
    media <- mean(valores)
    suma_cuadrados <- sum((valores - media)^2)
    varianza <- suma_cuadrados / length(valores)
    return(varianza)
  }
  
  # Usamos la función interna
  media <- mean(datos)
  varianza <- calcular_varianza(datos)
  
  return(list(media = media, varianza = varianza))
}

# Probamos la función
numeros <- c(2, 4, 6, 8, 10)
estadisticas <- calcular_estadisticas(numeros)
print(estadisticas)

Las funciones son uno de los pilares fundamentales de la programación en R, ya que nos permiten escribir código modular, reutilizable y más fácil de mantener. Dominar la creación de funciones es esencial para avanzar en el aprendizaje de este lenguaje.

Argumentos posicionales, nombrados y predeterminados

Cuando trabajamos con funciones en R, la forma en que pasamos los argumentos puede hacer nuestro código más flexible y legible. R ofrece tres métodos principales para proporcionar argumentos a las funciones: posicionales, nombrados y predeterminados.

Argumentos posicionales

Los argumentos posicionales son aquellos que se pasan a una función en el mismo orden en que fueron definidos. Este es el método más básico y común para llamar a funciones:

# Definimos una función con tres argumentos
dibujar_rectangulo <- function(alto, ancho, color) {
  mensaje <- paste("Dibujando un rectángulo de", alto, "x", ancho, "en color", color)
  return(mensaje)
}

# Llamada con argumentos posicionales
resultado <- dibujar_rectangulo(5, 10, "rojo")
print(resultado)  # "Dibujando un rectángulo de 5 x 10 en color rojo"

En este ejemplo, R asigna el valor 5 al parámetro alto, 10 al parámetro ancho y "rojo" al parámetro color, basándose exclusivamente en la posición de cada argumento en la llamada.

La principal ventaja de los argumentos posicionales es su brevedad. Sin embargo, pueden generar confusión cuando una función tiene muchos parámetros o cuando no recordamos el orden exacto de los mismos.

Argumentos nombrados

Los argumentos nombrados nos permiten especificar explícitamente a qué parámetro corresponde cada valor, independientemente del orden:

# Usando argumentos nombrados
resultado <- dibujar_rectangulo(ancho = 10, color = "azul", alto = 5)
print(resultado)  # "Dibujando un rectángulo de 5 x 10 en color azul"

Observa cómo, a pesar de haber cambiado el orden de los argumentos, la función sigue funcionando correctamente porque cada valor está explícitamente asociado a su parámetro.

Los argumentos nombrados ofrecen varias ventajas:

  • Claridad: Hacen el código más legible, especialmente con funciones que tienen muchos parámetros
  • Flexibilidad: Permiten proporcionar los argumentos en cualquier orden
  • Seguridad: Reducen la posibilidad de errores por confusión en el orden de los parámetros

Combinación de argumentos posicionales y nombrados

En R, podemos combinar ambos enfoques en una misma llamada a función:

# Combinando argumentos posicionales y nombrados
resultado <- dibujar_rectangulo(5, ancho = 10, color = "verde")
print(resultado)  # "Dibujando un rectángulo de 5 x 10 en color verde"

Sin embargo, es importante seguir esta regla: los argumentos posicionales deben aparecer siempre antes que los nombrados. El siguiente código generaría un error:

# Esto generaría un error
# resultado <- dibujar_rectangulo(alto = 5, 10, "verde")

Argumentos predeterminados

Los argumentos predeterminados (o valores por defecto) nos permiten especificar valores que se utilizarán cuando el usuario no proporcione un valor para ese parámetro:

# Función con argumentos predeterminados
crear_usuario <- function(nombre, edad = 30, ciudad = "Madrid") {
  perfil <- paste(nombre, "tiene", edad, "años y vive en", ciudad)
  return(perfil)
}

Esta función tiene tres parámetros, pero solo nombre es obligatorio. Los parámetros edad y ciudad tienen valores predeterminados que se utilizarán si no se proporcionan explícitamente.

Veamos diferentes formas de llamar a esta función:

# Solo proporcionamos el argumento obligatorio
usuario1 <- crear_usuario("Ana")
print(usuario1)  # "Ana tiene 30 años y vive en Madrid"

# Proporcionamos nombre y edad, pero usamos el valor predeterminado para ciudad
usuario2 <- crear_usuario("Carlos", 25)
print(usuario2)  # "Carlos tiene 25 años y vive en Madrid"

# Proporcionamos todos los argumentos
usuario3 <- crear_usuario("Elena", 42, "Barcelona")
print(usuario3)  # "Elena tiene 42 años y vive en Barcelona"

# Usando argumentos nombrados podemos omitir argumentos intermedios
usuario4 <- crear_usuario(nombre = "Pablo", ciudad = "Valencia")
print(usuario4)  # "Pablo tiene 30 años y vive en Valencia"

Buenas prácticas con argumentos

Para escribir código más mantenible y claro, considera estas recomendaciones:

  • Coloca los argumentos obligatorios al principio de la definición de la función
  • Usa argumentos nombrados cuando llames a funciones con muchos parámetros
  • Proporciona valores predeterminados razonables que cubran los casos de uso más comunes
  • Documenta claramente el propósito de cada argumento

Verificación de argumentos con valores predeterminados

Podemos combinar los valores predeterminados con la verificación de argumentos para crear funciones más robustas:

calcular_descuento <- function(precio, porcentaje = 10, maximo = 50) {
  # Verificamos que los argumentos sean válidos
  if (!is.numeric(precio) || precio <= 0) {
    stop("El precio debe ser un número positivo")
  }
  
  if (!is.numeric(porcentaje) || porcentaje < 0 || porcentaje > 100) {
    stop("El porcentaje debe estar entre 0 y 100")
  }
  
  # Calculamos el descuento
  descuento <- precio * (porcentaje / 100)
  
  # Aplicamos el límite máximo si corresponde
  if (!is.null(maximo) && descuento > maximo) {
    descuento <- maximo
  }
  
  return(list(precio_original = precio, 
              descuento = descuento, 
              precio_final = precio - descuento))
}

# Probamos la función con diferentes combinaciones de argumentos
calcular_descuento(100)  # 10% de descuento por defecto
calcular_descuento(100, 20)  # 20% de descuento
calcular_descuento(500, 15, 60)  # 15% con máximo personalizado
calcular_descuento(precio = 200, maximo = 30)  # Omitimos el argumento intermedio

Argumentos especiales: puntos suspensivos (...)

R también permite crear funciones que acepten un número variable de argumentos mediante el parámetro especial ... (puntos suspensivos):

sumar_valores <- function(...) {
  # Capturamos todos los argumentos en una lista
  argumentos <- list(...)
  
  # Verificamos que todos sean numéricos
  for (i in seq_along(argumentos)) {
    if (!is.numeric(argumentos[[i]])) {
      stop("Todos los argumentos deben ser numéricos")
    }
  }
  
  # Sumamos todos los valores
  total <- sum(unlist(argumentos))
  return(total)
}

# Podemos llamar a la función con cualquier número de argumentos
sumar_valores(1, 2, 3)  # Devuelve 6
sumar_valores(10, 20, 30, 40, 50)  # Devuelve 150

Este mecanismo es especialmente útil cuando queremos crear funciones envoltorio que pasen argumentos a otras funciones:

graficar_mejorado <- function(datos, ...) {
  # Establecemos algunos parámetros por defecto
  par(mar = c(5, 4, 2, 1))
  
  # Llamamos a la función plot pasando todos los argumentos adicionales
  plot(datos, ...)
  
  # Añadimos una cuadrícula
  grid()
}

# Podemos usar esta función pasando argumentos que serán enviados a plot()
x <- 1:10
y <- x^2
graficar_mejorado(x, y, type = "b", col = "blue", main = "Gráfico cuadrático")

Dominar estos diferentes tipos de argumentos te permitirá crear funciones más flexibles y fáciles de usar, mejorando significativamente la calidad de tu código en R.

Retorno de valores

El retorno de valores es una característica fundamental de las funciones en R que permite devolver resultados después de ejecutar operaciones. Cuando una función completa su ejecución, puede proporcionar datos que serán utilizados en otras partes del programa.

Retorno explícito con return()

La forma más clara de devolver un valor desde una función es utilizando la instrucción return():

calcular_cuadrado <- function(numero) {
  resultado <- numero^2
  return(resultado)
}

# Llamamos a la función y capturamos el valor retornado
valor <- calcular_cuadrado(4)
print(valor)  # Muestra 16

La instrucción return() hace dos cosas importantes:

  • Devuelve el valor especificado
  • Finaliza inmediatamente la ejecución de la función

Este segundo punto es especialmente útil para crear salidas anticipadas en una función:

dividir_seguro <- function(numerador, denominador) {
  # Verificación de seguridad
  if (denominador == 0) {
    return("Error: División por cero")
  }
  
  # Este código solo se ejecuta si el denominador no es cero
  resultado <- numerador / denominador
  return(resultado)
}

print(dividir_seguro(10, 2))    # Muestra 5
print(dividir_seguro(10, 0))    # Muestra "Error: División por cero"

Tipos de valores de retorno

En R, una función puede devolver prácticamente cualquier tipo de dato:

# Función que retorna un valor numérico
calcular_area_triangulo <- function(base, altura) {
  return((base * altura) / 2)
}

# Función que retorna un vector
generar_secuencia <- function(inicio, fin) {
  return(inicio:fin)
}

# Función que retorna una lista
analizar_texto <- function(texto) {
  caracteres <- nchar(texto)
  palabras <- length(strsplit(texto, "\\s+")[[1]])
  mayusculas <- sum(grepl("[A-Z]", strsplit(texto, "")[[1]]))
  
  return(list(
    caracteres = caracteres,
    palabras = palabras,
    mayusculas = mayusculas
  ))
}

# Probamos las funciones
print(calcular_area_triangulo(5, 3))  # Muestra 7.5
print(generar_secuencia(5, 10))       # Muestra 5 6 7 8 9 10
resultado <- analizar_texto("Hola Mundo R")
print(resultado)                      # Muestra una lista con los análisis

Retorno de múltiples valores

A diferencia de otros lenguajes, R permite devolver múltiples valores de forma sencilla utilizando estructuras de datos como vectores, listas o data frames:

# Usando un vector nombrado
estadisticas_basicas <- function(numeros) {
  return(c(
    minimo = min(numeros),
    maximo = max(numeros),
    promedio = mean(numeros)
  ))
}

# Usando una lista (más flexible para diferentes tipos de datos)
analizar_datos <- function(numeros) {
  return(list(
    resumen = summary(numeros),
    varianza = var(numeros),
    es_normal = shapiro.test(numeros)$p.value > 0.05
  ))
}

# Probamos las funciones
datos <- c(12, 15, 21, 18, 9, 14)
print(estadisticas_basicas(datos))
analisis <- analizar_datos(datos)
print(analisis$resumen)
print(paste("¿Distribución normal?", analisis$es_normal))

Retorno implícito

Como ya vimos brevemente, R tiene la característica de retorno implícito: si no se especifica una instrucción return(), la función devolverá automáticamente el resultado de la última expresión evaluada:

# Estas dos funciones son equivalentes
suma_explicita <- function(a, b) {
  return(a + b)
}

suma_implicita <- function(a, b) {
  a + b  # La última expresión evaluada se retorna automáticamente
}

print(suma_explicita(3, 4))  # Muestra 7
print(suma_implicita(3, 4))  # También muestra 7

Este comportamiento es útil para escribir funciones concisas, especialmente cuando son simples:

# Función concisa para calcular el área de un círculo
area_circulo <- function(radio) pi * radio^2

# Función para verificar si un número es par
es_par <- function(x) x %% 2 == 0

print(area_circulo(3))  # Muestra 28.27433
print(es_par(4))        # Muestra TRUE
print(es_par(7))        # Muestra FALSE

Retorno condicional

Las funciones pueden devolver diferentes valores dependiendo de ciertas condiciones:

clasificar_temperatura <- function(temp) {
  if (temp < 0) {
    return("Congelación")
  } else if (temp < 10) {
    return("Muy frío")
  } else if (temp < 20) {
    return("Fresco")
  } else if (temp < 30) {
    return("Templado")
  } else {
    return("Caluroso")
  }
}

print(clasificar_temperatura(-5))  # Muestra "Congelación"
print(clasificar_temperatura(25))  # Muestra "Templado"

Retorno invisible

R ofrece una función especial llamada invisible() que permite devolver un valor sin mostrarlo automáticamente cuando la función se ejecuta directamente en la consola:

configurar_opciones <- function(tamano_fuente, color_fondo) {
  # Simulamos configuración de opciones
  opciones <- list(
    tamano = tamano_fuente,
    color = color_fondo,
    timestamp = Sys.time()
  )
  
  # Devolvemos invisiblemente
  invisible(opciones)
}

# Al llamar directamente, no muestra nada
configurar_opciones(12, "azul")

# Pero el valor está disponible si lo asignamos
config <- configurar_opciones(14, "gris")
print(config)  # Ahora sí muestra el valor

Esta característica es útil para funciones que realizan acciones (como configurar algo o graficar) pero que también quieren proporcionar datos para uso opcional.

Captura y uso de valores retornados

Los valores devueltos por las funciones pueden ser:

  • Asignados a variables
  • Utilizados directamente en expresiones
  • Pasados como argumentos a otras funciones
# Asignación a variable
resultado <- sqrt(16)

# Uso directo en expresión
if (es_par(7)) {
  print("Es par")
} else {
  print("Es impar")
}

# Pasando el resultado a otra función
datos <- c(1, 4, 9, 16, 25)
print(sum(sqrt(datos)))  # Calcula la suma de las raíces cuadradas

Encadenamiento de funciones

Una práctica común en R es encadenar funciones, donde el valor retornado por una función se convierte en la entrada de otra:

# Procesamiento de datos encadenado
datos_procesados <- datos %>%
  filtrar_valores_atipicos() %>%
  normalizar() %>%
  agrupar_por_categoria()

Este patrón es especialmente común cuando se utilizan paquetes como dplyr o magrittr que implementan el operador pipe (%>%).

Buenas prácticas para el retorno de valores

Para escribir funciones más claras y mantenibles:

  • Documenta claramente qué devuelve tu función
  • Mantén consistencia en los tipos de retorno (evita que una función devuelva a veces un número y otras veces una cadena)
  • Usa return() explícito en funciones complejas para mejorar la legibilidad
  • Considera devolver estructuras de datos nombradas para resultados múltiples
  • Maneja adecuadamente los casos especiales y errores
#' Calcula el índice de masa corporal (IMC)
#' 
#' @param peso Peso en kilogramos
#' @param altura Altura en metros
#' @return Un valor numérico que representa el IMC
calcular_imc <- function(peso, altura) {
  # Validación de entradas
  if (!is.numeric(peso) || !is.numeric(altura)) {
    stop("Peso y altura deben ser valores numéricos")
  }
  
  if (peso <= 0 || altura <= 0) {
    stop("Peso y altura deben ser valores positivos")
  }
  
  # Cálculo del IMC
  imc <- peso / (altura^2)
  
  return(imc)
}

El manejo efectivo de los valores de retorno es esencial para crear funciones útiles y reutilizables en R, permitiéndote construir programas más modulares y mantenibles.

Aprende R online

Otros ejercicios de programación de R

Evalúa tus conocimientos de esta lección Funciones con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de R

Accede a todas las lecciones de R y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación De R Y Rstudio

R

Introducción Y Entorno

Introducción A R

R

Introducción Y Entorno

Operadores

R

Sintaxis

Estructuras De Datos

R

Sintaxis

Funciones

R

Sintaxis

Estructuras De Control Iterativo

R

Sintaxis

Scopes Y Closures

R

Sintaxis

Estructuras De Control Condicional

R

Sintaxis

Funciones Anónimas

R

Sintaxis

Tipos De Datos Y Variables

R

Sintaxis

Sistema R6: Clases Referenciales Y Encapsulamiento

R

Programación Orientada A Objetos

Sistema S4: Clases Formales Y Validación

R

Programación Orientada A Objetos

Herencia Y Polimorfismo En R

R

Programación Orientada A Objetos

Sistemas De Oop En R

R

Programación Orientada A Objetos

Sistema S3: Clases Implícitas Y Métodos Genéricos

R

Programación Orientada A Objetos

Tidyverse Para Transformación De Datos

R

Manipulación De Datos

Lubridate Para Fechas Y Tiempo

R

Manipulación De Datos

Group_by Y Summarize Para Agrupación Y Resumen

R

Manipulación De Datos

Stringr Para Expresiones Regulares

R

Manipulación De Datos

Tidyr Para Limpieza De Valores Faltantes

R

Manipulación De Datos

Joins En R Para Combinación Y Relaciones De Tablas

R

Manipulación De Datos

Pivot_longer Y Pivot_wider Para Reestructuración

R

Manipulación De Datos

Mutate Y Transmute Para Transformación

R

Manipulación De Datos

Dplyr Para Filtrado Y Selección

R

Manipulación De Datos

Readr Y Read.csv Para Importar Datos

R

Manipulación De Datos

Gráficos Bivariantes En R

R

Visualización De Datos

Gráficos Univariantes En R

R

Visualización De Datos

Facetas En Ggplot2

R

Visualización De Datos

Personalización Y Temas

R

Visualización De Datos

Ggplot2 Para Visualización De Datos

R

Visualización De Datos

Gráficos Multivariantes En R

R

Visualización De Datos

Correlación En R

R

Estadística

Regresión Lineal En R

R

Estadística

Pruebas De Hipótesis En R

R

Estadística

Anova En R

R

Estadística

Estadística Descriptiva En R

R

Estadística

Accede GRATIS a R y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la estructura básica y sintaxis para definir funciones en R.
  • Aprender a utilizar argumentos posicionales, nombrados y con valores predeterminados.
  • Entender el concepto de retorno de valores, tanto explícito como implícito, y cómo manejar múltiples valores.
  • Conocer buenas prácticas para la documentación, validación de argumentos y manejo del alcance de variables.
  • Saber cómo usar argumentos especiales como los puntos suspensivos (...) para funciones flexibles.