Material introductorio para crear funciones en R de los Viernes de Bioinfo 2026. Basado en El ABC de las funciones y loops en R por Evelia Coss
Autor/a

1 ¿Qué es una función?

Una función es una forma de organizar y reutilizar código: reúne una secuencia de operaciones bajo un mismo nombre para poder ejecutarlas cada vez que la necesitemos.

Las funciones nos permiten:

  • asignar un nombre a un conjunto de instrucciones, para poder reutilizarlo fácilmente
  • evitar tener que recordar y escribir cada paso de manera repetida
  • trabajar con entradas (inputs) y obtener resultados (outputs) de forma clara
  • estructurar mejor nuestro código y hacerlo más legible y mantenible

En la mayoría de los lenguajes de programación, las funciones son un elemento fundamental para construir soluciones más complejas a partir de partes simples.

Nota

Cuando escribes una función, estás creando tu propia herramienta: es uno de los primeros pasos para pensar como programador.

Source

2 Tips para generar una función

En resumen, la estructura básica de una función en R es la siguiente:

  • Nombre de la función: es cómo la identificamos y la llamamos después
  • Argumentos (inputs): los valores que recibe la función
  • Cuerpo de la función: donde se realizan las operaciones
  • Valor que devuelve (output): el resultado que la función devuelve

En conjunto, estos elementos nos permiten construir funciones claras y reutilizables.

Buenas prácticas al crear funciones

Al escribir funciones, es recomendable:

  • usar nombres descriptivos tanto para la función como para sus argumentos
  • mantener las funciones simples y enfocadas (una función = una tarea)
  • incluir explícitamente el return() para mayor claridad
  • agregar validaciones o mensajes cuando sea necesario
Importante

Una buena función no solo funciona, también es fácil de leer, entender y reutilizar.

3 Nuestro primer código

Problema: Realizar un algoritmo que solicite al usuario dos numeros enteros, realice su suma y la imprima en pantalla.

Mostrar código
# ---Algoritmo(Sumar)---
# 1) Solicitar al usuario los datos de entrada (variable a y b).
# --INICIO--
a <- readline("Digite el primer numero: ")
b <- readline("Digite el segundo numero: ")

# Convertir la entrada en números enteros
a <- as.integer(a)
b <- as.integer(b)

# 2) Realizar la suma de los datos de entrada.
c <- a + b 
 
# 3) Mostrar el resultado.
print(c)
# --FIN_INICIO--
# --- Fin_de_Algoritmo(Sumar) ---

¿Cuál es el problema de este enfoque?

Este código funciona correctamente, pero tiene una desventaja importante:

  • Cada vez que queramos realizar una suma, debemos volver a escribir todo el procedimiento
  • El código puede volverse repetitivo y difícil de mantener
  • No es fácil reutilizarlo en otros programas

💡 Motivación

Aquí es donde entran las funciones.

Una función nos permite encapsular este proceso en un solo bloque de código que podemos reutilizar tantas veces como queramos.

Tip

En lugar de repetir el mismo algoritmo, podemos nombrarlo y usarlo cuando lo necesitemos.

4 Nuestra primera función

Problema: Crear una función que reciba dos numeros enteros y realice su suma.

Mostrar código
my_sum <- function(a, b) {
  c <- a + b
  return(c)
}

Desglose de la función:

  • my_sum: es el nombre de la función que creamos.
  • a,b: son los argumentos de la función.
  • c <- a + b: es la operación que realiza la función.
  • return(c): devuelve el resultado de la función.
Importante

¿Qué nos falta en la función?

No estamos verificando que los valores de entrada sean números enteros.

Actualmente, la función acepta cualquier tipo de dato que pueda sumarse, lo cual puede provocar resultados inesperados.

💡 Una buena práctica es validar los datos de entrada para asegurarnos de que la función se use correctamente.

💻Ejercicio: Nuestra segunda función

Define una función que:

  • reciba dos números enteros como argumentos
  • calcule su suma
  • devuelva el resultado

Asegúrate de que la función funcione correctamente para distintos valores de entrada.

Mostrar código
my_sum <- function(a, b) {
  
  # Convertir la entrada en números enteros
  a <- as.integer(a)
  b <- as.integer(b)
  
  # Realizar la suma
  c <- a + b

  # Devolver el resultado
  return(c)
}

En esta versión estamos forzando que los datos sean enteros usando as.integer().

💡 Esto NO valida los datos, solo los convierte. Por ejemplo, 3 se convierte correctamente, pero hola se convierte en NA.

Una mejora adicional sería validar los datos de entrada antes de realizar la operación. Esto lo veremos más adelante.

4.1 Verificación / Realizar pruebas

Una vez definida la función, es importante probarla para asegurarnos de que funciona como esperamos.

Antes de ejecutarla, reflexiona:

¿Qué resultado esperas obtener al evaluar my_sum(3, 4)?

Mostrar código
my_sum(3,4)
[1] 7

💡 Después de ejecutar la función, deberías obtener como resultado: 7

Nota

Probar funciones con ejemplos simples nos permite:

  • verificar que el resultado es correcto

  • detectar posibles errores

  • entender mejor cómo funciona la función

Casos “problemáticos”:
Mostrar código
my_sum("3", "4")
[1] 7
Mostrar código
my_sum("hola", 5)
Warning in my_sum("hola", 5): NAs introducidos por coerción
[1] NA

Caso 1: my_sum("3", "4")

Aunque “3” y “4” son texto, la función los convierte con as.integer(). El resultado es: 7

✔️ En este caso, la función funciona porque los valores sí pueden convertirse a números

Caso 2: my_sum("hola", 5)

"hola" no puede convertirse a un número entero, as.integer("hola") produce: NA

Entonces la suma se convierte en: NA + 5 = NA

❌ La función no falla explícitamente, pero produce un resultado incorrecto

💡 Conclusión

  • Convertir datos (`as.integer`) no es lo mismo que validarlos
  • Nuestra función actualmente puede generar resultados inválidos sin avisarnos
  • Es importante agregar validación de entradas para evitar estos problemas

👉 Más adelante, mejoraremos la función para que detecte y maneje este tipo de errores.

5 Conversiones de temperatura

Fórmulas de Temperatura

Hasta ahora hemos trabajado con una función muy simple (una suma). Ahora veremos un ejemplo más interesante: convertir unidades de temperatura.

5.1 Conversión de Fahrenheit a Kelvin

Vamos a crear una función que convierta una temperatura dada en grados Fahrenheit a Kelvin.

Mostrar código
fahr_to_kelvin <- function(temp) {
  kelvin <- ((temp - 32) * (5 / 9)) + 273.15
  return(kelvin)
}

💡 En este caso:

  • temp es el valor de entrada (en Fahrenheit)
  • aplicamos una fórmula matemática
  • devolvemos el resultado en Kelvin

Sofware Carpentry, Funciones

5.2 Verificación / Realizar pruebas

Probemos la función con valores conocidos:

Mostrar código
fahr_to_kelvin(32) # Punto de congelación del agua
[1] 273.15
Mostrar código
fahr_to_kelvin(212) # Punto de ebullición del agua
[1] 373.15

💡 Interpretación de los resultados

  • 32°F ≈ 273.15 K → temperatura de congelación del agua
  • 212°F ≈ 373.15 K → temperatura de ebullición del agua

✔️ Esto nos permite verificar que la función está implementada correctamente.

Remark

A diferencia de my_sum, esta función:

  • realiza una operación más compleja
  • tiene un significado físico
  • muestra cómo las funciones pueden representar modelos o fórmulas reales
💻Ejercicio: Conversión de Kelvin a Celsius

Define una función que:

  • reciba una temperatura en Kelvin
  • la convierta a grados Celsius
  • devuelva el resultado

Además, prueba tu función con algunos valores.

Sofware Carpentry, Funciones

Mostrar código
kelvin_to_celsius <- function(temp) {
  celsius <- temp - 273.15
  return(celsius)
}

Verificación

Mostrar código
kelvin_to_celsius(300) 
[1] 26.85
Mostrar código
kelvin_to_celsius(400) 
[1] 126.85

6 Combinando funciones

El verdadero poder de las funciones aparece cuando las combinamos para construir soluciones más complejas.

En lugar de escribir todo desde cero, podemos reutilizar funciones ya definidas.

Considera las siguientes funciones:

Mostrar código
fahr_to_kelvin <- function(temp) {
  kelvin <- ((temp - 32) * (5 / 9)) + 273.15
  return(kelvin)
}

kelvin_to_celsius <- function(temp) {
  celsius <- temp - 273.15
  return(celsius)
}
📝Desafío: Combinando funciones

Define una función que convierta directamente de Fahrenheit a Celsius, reutilizando las funciones anteriores.

💡 Sugerencia: usa la salida de una función como entrada de la otra.

Mostrar código
fahr_to_celsius <- function(temp) {
  temp_k <- fahr_to_kelvin(temp)
  result <- kelvin_to_celsius(temp_k)
  return(result)
}

Verificación

Mostrar código
fahr_to_celsius(68)
[1] 20

7 Estructuras de control

Las estructuras de control nos permiten definir cómo y cuándo se ejecuta nuestro código.

Gracias a ellas, podemos tomar decisiones dentro de un programa, por ejemplo:

  • ejecutar una acción solo si se cumple una condición
  • repetir una operación varias veces
  • controlar el flujo de ejecución

Esto es fundamental para construir programas más complejos y, en particular, para definir funciones más robustas y flexibles.

Importante

Sin estructuras de control, nuestros programas serían siempre lineales y poco dinámicos.

R para principiantes, cap 9

7.1 Las estructuras de control más usadas

Estructura de control Descripción
if, else Si, de otro modo: Ejecuta código según una condición.
for Para cada uno en: Repite una acción un número de veces.
while Mientras: Repite mientras se cumpla una condición.
break Interrupción: Interrumpe un ciclo.
next Siguiente: Salta a la siguiente iteración.
case_when Condicional con diversas salidas: Evalúa múltiples condiciones.

Las estructuras de control nos permiten:

  • validar datos de entrada
  • manejar diferentes casos dentro de una función
  • automatizar tareas repetitivas

7.2 if, else

Las estructuras if y else nos permiten tomar decisiones dentro de nuestro código.

  • if (si): ejecuta un bloque de código solo si se cumple una condición
  • else (de otro modo): define qué hacer cuando la condición no se cumple

💡 En otras palabras, permiten que nuestro programa siga distintos caminos según la información que recibe.

Estructura general

Podemos representar su funcionamiento de manera general con el siguiente pseudocódigo:

Mostrar código
Inicio
  Recibir datos de entrada
  Evaluar condición

  if (condición es TRUE) {
    ejecutar acciones si TRUE
  } else {
    ejecutar acciones si FALSE
  }
Fin
Importante
  • Una condición siempre se evalúa como TRUE o FALSE
  • A esto lo llamamos una expresión lógica

if

SI una condición es verdadera, ENTONCES ejecuta ciertas acciones.

Podemos pensarlo de forma intuitiva:

Mostrar código
IF you are happy
   THEN smile
ENDIF

Ejemplo sencillo:

Mostrar código
if(4 > 3) {
  "Verdadero"
}
[1] "Verdadero"
¿Qué está pasando aquí?

La condición 4 > 3 es TRUE, por lo tanto, R ejecuta el código dentro de las llaves {}. El resultado es: "Verdadero".

Si la condición fuera falsa, el código dentro del if no se ejecuta.

if, else

  • SI una condición es verdadera, ENTONCES ejecuta ciertas acciones,
  • DE OTRO MODO, ejecuta una alternativa.

Podemos pensarlo de forma intuitiva:

Mostrar código
SI estás feliz ENTONCES
  sonríe
SINO
  frunce el ceño
FIN

Ejemplo sencillo:

Mostrar código
if(4 > 3) {
  "Verdadero"
} else {
  "Falso"
}
[1] "Verdadero"
¿Qué ocurre aquí?
  • La condición 4 > 3 es TRUE

  • Por lo tanto, se ejecuta el primer bloque ("Verdadero")

  • El bloque else no se ejecuta

Si la condición fuera falsa, el resultado sería:

  • "Falso"

7.3 for

El ciclo for nos permite repetir una operación varias veces, recorriendo los elementos de un objeto (por ejemplo, un vector).

Estructura general:

Mostrar código
for(elemento in objeto) {
  operacion_con_elemento
}

💡 Esto se interpreta como:

  • PARA cada elemento en un objeto
  • HAZ una operación

Ejemplo: cuadrados de un dado 🎲

Vamos a obtener el cuadrado de cada elemento en un vector del 1 al 6, que representa las caras de un dado.

Mostrar código
dado <- 1:6

for(cara in dado) {
  print(cara ^ 2)
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36

💡 ¿Qué está pasando?

  • dado <- 1:6 crea el vector: 1, 2, 3, 4, 5, 6
  • cara toma cada valor del vector, uno por uno
  • en cada iteración se calcula cara^2
  • print() solo muestra el resultado en consola
Importante

Usar print() no guarda los resultados, solo los muestra.

Si queremos reutilizar los resultados, debemos almacenarlos.

Almacenar resultados con for

Mostrar código
dado <- 1:6
mi_vector <- NULL # vector vacío para almacenar resultados

# Cara es el contador, que agarra CADA valor de dado
# Empezando con 1 hasta el 6, teniendo 6 iteraciones
for(cara in dado) {
  mi_vector[cara] <- cara ^ 2
}

mi_vector
[1]  1  4  9 16 25 36

💡 Explicación:

  • mi_vector <- NULL inicializa un objeto vacío
  • mi_vector[cara] <- cara^2 guarda cada resultado
  • el índice cara indica la posición en el vector
  • al final obtenemos: 1, 4, 9, 16, 25, 36
💻Ejercicio

Define un vector con los números del 1 al 10 y utiliza un ciclo for para:

  • calcular el cubo de cada número
  • almacenar los resultados en un vector
  • mostrar el vector final
Mostrar código
numeros <- 1:10
resultado <- NULL

for (n in numeros) {
  resultado[n] <- n^3
}

resultado
 [1]    1    8   27   64  125  216  343  512  729 1000

7.4 while

El ciclo while permite repetir una operación mientras una condición sea verdadera (TRUE).

Es decir, el código se ejecuta una y otra vez hasta que la condición deja de cumplirse.

Estructura general

Mostrar código
while(condicion) {
  operaciones
}

💡 Esto se interpreta como:

  • MIENTRAS la condición sea verdadera,
  • HAZ estas operaciones

Ejemplo: Vamos a construir un ejemplo donde:

  • generamos números aleatorios del 1 al 10
  • los vamos sumando a una variable valor
  • contamos cuántas iteraciones se necesitan para que valor alcance al menos 50
Mostrar código
conteo <- 0
valor <- 0

while (valor < 50) {
  # Genera un número aleatorio entre 1 y 10
  valor <- valor + sample(x = 1:10, size = 1)
  
  # Contador de iteraciones
  conteo <- conteo + 1
}

valor
[1] 50
Mostrar código
conteo
[1] 8

💡 ¿Qué está pasando?

  • el ciclo se repite mientras valor < 50
  • en cada iteración:
    • se suma un número aleatorio a `valor``
    • se incrementa conteo
  • el ciclo termina cuando valor ≥ 50

✔️ Al final obtenemos:

  • el valor acumulado
  • el número de iteraciones necesarias
⚠️ Caso importante

Si la condición no es verdadera desde el inicio, el ciclo nunca se ejecuta:`

Mostrar código
conteo <- 0

while ("dado" == "ficha") {
  conteo <- conteo + 1
}

conteo
[1] 0

💡 Observación

  • "dado" == "ficha" es FALSE
  • por lo tanto, el código dentro del while no se ejecuta ni una sola vez
  • conteo permanece en 0
Advertencia

Es importante que la condición eventualmente se vuelva FALSE, de lo contrario el ciclo puede ejecutarse indefinidamente (bucle infinito).

💻Ejercicio

Crea un ciclo while que:

  • comience con un valor inicial de 1

  • lo duplique en cada iteración

  • se detenga cuando el valor sea mayor que 100

  • cuente cuántas iteraciones se realizaron

Mostrar código
valor <- 1
conteo <- 0

while (valor <= 100) {
  valor <- valor * 2
  conteo <- conteo + 1
}

valor
[1] 128
Mostrar código
conteo
[1] 7

7.5 break y next

break y next son palabras reservadas en R, es decir, no podemos usarlas como nombres de variables y tienen un comportamiento específico dentro de los bucles.

  • break: interrumpe completamente el ciclo
  • next: salta a la siguiente iteración, ignorando el resto del código en la iteración actual

Ambas funcionan tanto en ciclos for como while.

Usando break

break detiene el ciclo en el momento en que se cumple una condición, aunque aún queden iteraciones pendientes.

Mostrar código
for (i in 1:10) {
  if (i == 3) {
    break
  }
  print(i)
}
[1] 1
[1] 2

💡 ¿Qué ocurre?

  • el ciclo inicia en i = 1
  • imprime 1, luego 2
  • cuando i == 3, se ejecuta break
  • el ciclo se detiene completamente

✔️ Resultado:

  • 1, 2

Usando break con while

Mostrar código
numero <- 20

while (numero > 5) {
  if (numero == 15) {
    break
  }
  numero <- numero - 1
}

numero
[1] 15

💡 ¿Qué ocurre?

  • el valor de numero va disminuyendo
  • cuando llega a 15, se ejecuta break
  • el ciclo se detiene inmediatamente

✔️ Resultado final:

  • numero = 15

Usando next

next no detiene el ciclo, solo omite la iteración actual y continúa con la siguiente.

Mostrar código
for (i in 1:4) {
  if (i == 3) {
    next
  }
  print(i)
}
[1] 1
[1] 2
[1] 4

💡 ¿Qué ocurre?

  • el ciclo recorre 1, 2, 3, 4
  • cuando i == 3, se ejecuta next
  • se salta esa iteración (no imprime nada para 3)
  • el ciclo continúa normalmente

✔️ Resultado:

  • 1, 2, 4
🧠 Resumen
  • break → detiene el ciclo completamente

  • next → salta solo una iteración

💻Ejercicio

Crea un ciclo for del 1 al 10 que:

  • imprima solo los números pares

  • use next para omitir los impares

Mostrar código
for (i in 1:10) {
  if (i %% 2 != 0) {
    next
  }
  print(i)
}
[1] 2
[1] 4
[1] 6
[1] 8
[1] 10

7.6 case_when()

La función case_when() nos permite evaluar múltiples condiciones de forma clara y ordenada.

Es especialmente útil cuando tenemos varias condiciones encadenadas, en lugar de usar muchos if, else.

Esquema general

Podemos pensar su funcionamiento de la siguiente manera:

Mostrar código
Entrada: valor

Si condición 1 → resultado 1
Si condición 2 → resultado 2
Si condición 3 → resultado 3
En otro caso → resultado por defecto

💡 Es decir, R evalúa cada condición en orden y devuelve el resultado de la primera que se cumple.

Ejemplo

case_when() forma parte del paquete dplyr.

Mostrar código
library(dplyr)

Adjuntando el paquete: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
Mostrar código
x <- 1:20

case_when(
  x %% 35 == 0 ~ "fizz buzz",
  x %% 5 == 0 ~ "fizz",
  x %% 7 == 0 ~ "buzz",
  .default = as.character(x)
)
 [1] "1"    "2"    "3"    "4"    "fizz" "6"    "buzz" "8"    "9"    "fizz"
[11] "11"   "12"   "13"   "buzz" "fizz" "16"   "17"   "18"   "19"   "fizz"

💡 ¿Qué está pasando?

  • x %% 5 == 0 identifica múltiplos de 5
  • x %% 7 == 0 identifica múltiplos de 7
  • x %% 35 == 0 identifica múltiplos de ambos

✔️ R evalúa en orden:

  • primero busca múltiplos de 35
  • luego de 5
  • luego de 7
  • si no cumple ninguna entonces usa .default

Ejemplo con data frame

También es muy útil para crear nuevas variables en un data frame.

El dataset starwars proviene del paquete dplyr.

Mostrar código
starwars %>%
  select(name:mass, gender, species) %>%
  mutate(
    type = case_when(
      height > 200 | mass > 200 ~ "large",
      species == "Droid" ~ "robot",
      .default = "other"
    )
  )
# A tibble: 87 × 6
   name               height  mass gender    species type 
   <chr>               <int> <dbl> <chr>     <chr>   <chr>
 1 Luke Skywalker        172    77 masculine Human   other
 2 C-3PO                 167    75 masculine Droid   robot
 3 R2-D2                  96    32 masculine Droid   robot
 4 Darth Vader           202   136 masculine Human   large
 5 Leia Organa           150    49 feminine  Human   other
 6 Owen Lars             178   120 masculine Human   other
 7 Beru Whitesun Lars    165    75 feminine  Human   other
 8 R5-D4                  97    32 masculine Droid   robot
 9 Biggs Darklighter     183    84 masculine Human   other
10 Obi-Wan Kenobi        182    77 masculine Human   other
# ℹ 77 more rows

💡 ¿Qué hace este código?

  • crea una nueva columna type
  • clasifica cada observación según condiciones:
    • "large" si es muy alto o pesado
    • "robot" si es un droide
    • "other" en cualquier otro caso
🧠 Resumen
  • case_when() permite manejar múltiples condiciones

  • es más claro que usar muchos if, else

  • es muy útil en análisis de datos

💻Ejercicio

Crea un vector del 1 al 20 y clasifica cada número como:

  • "par" si es divisible entre 2

  • "impar" en otro caso

  • Usa case_when().

Mostrar código
x <- 1:20

case_when(
  x %% 2 == 0 ~ "par",
  .default = "impar"
)
 [1] "impar" "par"   "impar" "par"   "impar" "par"   "impar" "par"   "impar"
[10] "par"   "impar" "par"   "impar" "par"   "impar" "par"   "impar" "par"  
[19] "impar" "par"  

8 Programación defensiva

Hasta ahora hemos visto que nuestras funciones pueden fallar si reciben datos inesperados.

Por ejemplo, en my_sum() vimos que entradas como "hola" producen resultados incorrectos (NA).

8.1 ¿Qué es la programación defensiva?

La programación defensiva consiste en asegurarnos de que nuestras funciones:

  • solo acepten los datos que esperamos

  • detecten errores en las entradas

  • detengan la ejecución si algo no es válido

Tip

💡 La idea es: anticiparnos a los errores antes de que ocurran.

Funciones útiles en R

En R podemos usar funciones como:

  • stop(): detiene la ejecución y muestra un mensaje de error
  • stopifnot(): detiene la ejecución si una condición no se cumple.(Puedes leer su manual)

Software Carpentry, Programación defensiva

8.2 Ejemplo con stop() e if()

Mostrar código
my_sum <- function(a, b) {
  if (!is.numeric(a) | !is.numeric(b)) {
    stop("Los argumentos deben ser numéricos")
  }
  return(a + b)
}

💡 ¿Qué hace este código?

  • verifica si a y b son numéricos
  • si no lo son: detiene la función con un mensaje claro
  • si sí lo son: realiza la suma

Verificación

Mostrar código
my_sum(3, 4)      # Caso correcto
my_sum("hola", 5) # Caso incorrecto

💡 ¿Qué ocurre?

  • my_sum(3, 4): devuelve 7 ✔️
  • my_sum("hola", 5): detiene la ejecución y muestra un error ❌
Nota

Ahora la función no falla en silencio, sino que avisa claramente el problema.

8.3 Ejemplo con stopifnot()

Mostrar código
my_sum <- function(a, b) {
  stopifnot(is.numeric(a), is.numeric(b))
  return(a + b)
}

💡 Diferencia clave

  • stop(): permite mensajes personalizados
  • stopifnot(): es más compacto, pero menos flexible. Da mensajes más claros del error.

Verificación:

Mostrar código
my_sum(3, 4)      # Caso correcto
my_sum("hola", 5) # Caso incorrecto

💡 ¿Qué ocurre?

  • my_sum(3, 4): devuelve 7 ✔️
  • my_sum("hola", 5): lanza un error automáticamente ❌
💻Ejercicio: Validación de entradas con stop()

Queremos asegurarnos de que el argumento temp sea un valor numérico.

Modifica la función fahr_to_kelvin() para que:

  • verifique si temp es numérico

  • en caso contrario, detenga la ejecución usando stop()

  • muestre un mensaje de error claro

💡 Sugerencia: utiliza if() junto con is.numeric().

Mostrar código
fahr_to_kelvin <- function(temp) {
  if (!is.numeric(temp)) {
    stop("temp must be a numeric vector.")
  }
  
  kelvin <- ((temp - 32) * (5 / 9)) + 273.15
  return(kelvin)
}

Verificación:

Mostrar código
fahr_to_kelvin(temp = 32)   # Caso correcto
fahr_to_kelvin(temp = "A")  # Caso incorrecto

💡 ¿Qué observas?

  • Cuando la entrada es correcta: la función devuelve el resultado ✔️
  • Cuando la entrada es incorrecta: la función se detiene con un error ❌

👉 Esto evita resultados inválidos y hace el código más seguro.

💻Ejercicio: Validación con stopifnot()

Ahora vamos a validar los datos de entrada utilizando stopifnot().

Modifica la función fahr_to_kelvin() para que:

  • verifique que temp sea numérico

  • detenga la ejecución automáticamente si la condición no se cumple

💡 Sugerencia: usa is.numeric() dentro de stopifnot().

Mostrar código
fahr_to_kelvin <- function(temp) {
  stopifnot(is.numeric(temp))
  
  kelvin <- ((temp - 32) * (5 / 9)) + 273.15
  return(kelvin)
}

Verificación:

Mostrar código
fahr_to_kelvin(temp = 32)  # Caso correcto
fahr_to_kelvin(temp = as.factor(32))  # Caso incorrecto

💡 ¿Qué observas?

  • Cuando la entrada es válida: la función funciona correctamente ✔️
  • Cuando no lo es: stopifnot() detiene la ejecución automáticamente ❌

👉 A diferencia de stop(), aquí el mensaje de error es generado por R y no es personalizado.

8.4 Usando warning() y message() con if()

Hasta ahora hemos visto cómo detener la ejecución con stop().
Sin embargo, no siempre queremos detener el programa: a veces solo queremos avisar lo que está ocurriendo.

Para eso existen:

  • warning() → muestra una advertencia, pero el código continúa

  • message() → muestra un mensaje informativo

Veamos un ejemplo:

Mostrar código
squareX <- function(x) {
  if (is.character(x)) {
    warning("Converting x to numeric")
    x <- as.numeric(x)
  } else {
    message("x appears to be numeric")
  }
  x^2
}

💡 ¿Qué hace esta función?

  • si x es texto (character):
    • muestra una advertencia
    • convierte x a número
  • si x ya es numérico:
    • muestra un mensaje informativo
  • finalmente, calcula el cuadrado de x

Verificación/Realizar pruebas

Mostrar código
squareX("4")
Warning in squareX("4"): Converting x to numeric
[1] 16
Mostrar código
squareX(4)
x appears to be numeric
[1] 16

💡 ¿Qué ocurre?

Caso 1: squareX("4")

  • se detecta que x es texto
  • aparece una advertencia
  • el valor se convierte a numérico
  • se calcula el cuadrado

✔️ Resultado: 16 (con warning)

Caso 2: squareX(4)

  • x ya es numérico
  • se muestra un mensaje
  • se calcula el cuadrado

✔️ Resultado: 16 (con message)

🧠 Comparación
Función ¿Detiene ejecución? Uso principal
stop() ❌ Sí Error crítico
warning() ✔️ No Algo no ideal ocurrió
message() ✔️ No Información para el usuario

💡 Conclusión

  • Usa stop() cuando el programa no puede continuar

  • Usa warning() cuando algo puede estar mal, pero puedes seguir

  • Usa message() para informar lo que está ocurriendo

💻Ejercicio

Modifica la función squareX() para que:

  • use stop() si x no puede convertirse a número

  • use warning() solo cuando la conversión sea posible

Mostrar código
squareX <- function(x) {
  if (is.character(x)) {
    x_num <- as.numeric(x)
    
    if (is.na(x_num)) {
      stop("No se puede convertir x a numérico")
    }
    
    warning("Converting x to numeric")
    x <- x_num
  }
  
  x^2
}

8.5 Usando el paquete assertthat

El paquete assertthat nos permite validar condiciones, de manera similar a stopifnot(), pero con mensajes de error más claros y personalizables.

Instalación y carga del paquete

Si no lo tienes instalado:

Mostrar código
install.packages("assertthat")

Para usarlo en tu sesión:

Mostrar código
library(assertthat)
Warning: package 'assertthat' was built under R version 4.5.1

💡 ¿Para qué sirve?

assert_that() se utiliza para:

  • verificar condiciones

  • detener la ejecución si no se cumplen

  • mostrar mensajes de error más informativos

Comparación con stopifnot()

Mostrar código
x <- 1:10

stopifnot(is.character(x))
Mostrar código
assert_that(is.character(x))
Mostrar código
assert_that(length(x) == 5)
Mostrar código
assert_that(is.numeric(x))

💡 Observación

  • stopifnot(): mensajes automáticos y poco descriptivos

  • assert_that(): mensajes más claros y fáciles de interpretar

Ejemplo en una función

Mostrar código
foo_message <- function(x) {
  assert_that(x == 1, msg = "x must always be 1")
  "yay"
}

Verificación / Realizar pruebas

Mostrar código
foo_message(1)
[1] "yay"
Mostrar código
foo_message(2)

💡 ¿Qué está pasando?

  • si x == 1 → la función continúa normalmente ✔️

  • si no se cumple → se detiene con un mensaje claro ❌

🧠 Resumen
  • assert_that() es una alternativa más expresiva que stopifnot()

  • permite escribir código más claro y fácil de depurar

  • es muy útil en funciones donde queremos validar condiciones específicas

💻Ejercicio

Modifica la función squareX() para que:

  • use assert_that() para verificar que x sea numérico

  • muestre un mensaje claro si no lo es

Mostrar código
squareX <- function(x) {
  assert_that(is.numeric(x), msg = "x debe ser numérico")
  x^2
}

8.6 🧠 Comparación de funciones para validación y comunicación

Función ¿Detiene ejecución? Tipo de salida Personalizable Uso principal
stop() ❌ Sí Error ✔️ Sí Error crítico, no se puede continuar
stopifnot() ❌ Sí Error ❌ No Validación rápida de condiciones
assert_that() ❌ Sí Error ✔️ Sí Validación con mensajes más claros
warning() ✔️ No Advertencia (warning) ✔️ Sí Algo no ideal, pero se puede continuar
message() ✔️ No Mensaje informativo ✔️ Sí Informar al usuario

💡 ¿Cómo elegir cuál usar?

  • Usa stop() cuando el programa no puede continuar

  • Usa stopifnot() para validaciones rápidas y simples

  • Usa assert_that() cuando quieras mensajes más claros

  • Usa warning() cuando algo puede estar mal, pero no es crítico

  • Usa message() para comunicar lo que está ocurriendo

🚨 Idea clave

No todos los problemas son errores fatales.
Saber cuándo detener, advertir o informar es parte de escribir buen código.

9 Guardar y reutilizar funciones

Hasta ahora hemos creado funciones dentro de nuestra sesión de R.
Sin embargo, si cerramos R, esas funciones se pierden.

Para evitar esto, podemos guardarlas y reutilizarlas después.

9.1 Opción A: Guardar como archivo .RData

Podemos guardar una función como un objeto en un archivo .RData.

Mostrar código
squareX <- function(x) {
  if (is.character(x)) {
    warning("Converting x to numeric")
    x <- as.numeric(x)
  } else {
    message("x appears to be numeric")
  }
  
  x^2
}

# Guardar la función
save(squareX, file = "./squareX.RData")

# Eliminar la función del entorno
rm(squareX)

Cargar la función

Para volver a usarla:

Mostrar código
load("./squareX.RData")

💡 ¿Qué está pasando?

  • save() guarda el objeto en un archivo

  • rm() elimina la función del entorno

  • load() recupera la función exactamente como estaba

✔️ Es útil cuando quieres guardar el estado de tu trabajo

⚠️ Desventaja

  • No puedes ver fácilmente el código dentro del .RData

  • No es ideal para compartir o versionar código

9.2 Opción B: Guardar en un script .R

Una mejor práctica es guardar tus funciones en un archivo de R, por ejemplo:

squareX_function.R

Luego puedes cargarlo con:

Mostrar código
source("squareX_function.R")

💡 ¿Qué hace source()?

  • ejecuta todo el código del archivo

  • carga automáticamente las funciones en tu sesión

  • permite mantener tu código organizado

9.3 🧠 Comparación

Método Ventaja Desventaja
.RData Guarda objetos directamente No es legible ni fácil de editar
.R + source Código visible y reutilizable Requiere ejecutar el script

9.4 🎯 Recomendación

Para desarrollo y proyectos, es mejor usar archivos .R y source().

💻Ejercicio

Crea un archivo llamado mis_funciones.R que contenga al menos:

  • my_sum()

  • squareX()

Luego, cárgalo en tu sesión usando source().

Mostrar código
my_sum <- function(a, b) {
  a + b
}

squareX <- function(x) {
  x^2
}
Mostrar código
source("mis_funciones.R")

10 Ejercicios extra

💻Ejercicio: Función de saludo según la hora

Escribe una función que muestre un saludo dependiendo de la hora del día:

  • “Buenos días”

  • “Buenas tardes”

  • “Buenas noches”

💡 Sugerencia: usa un argumento time que por defecto sea lubridate::now(). Esto facilitará probar tu función.

Mostrar código
library(lubridate)

Adjuntando el paquete: 'lubridate'
The following objects are masked from 'package:base':

    date, intersect, setdiff, union
Mostrar código
greeting <- function(time = now()) {
  hora <- hour(time)
  
  if (hora < 12) {
    return("Buenos días")
  } else if (hora < 18) {
    return("Buenas tardes")
  } else {
    return("Buenas noches")
  }
}

Verificación:

Mostrar código
greeting()  # Usa la hora actual
[1] "Buenas noches"
Mostrar código
greeting(ymd_hms("2024-01-01 10:00:00"))  # Mañana
[1] "Buenos días"
Mostrar código
greeting(ymd_hms("2024-01-01 15:00:00"))  # Tarde
[1] "Buenas tardes"
Mostrar código
greeting(ymd_hms("2024-01-01 21:00:00"))  # Noche
[1] "Buenas noches"
💻Ejercicio: Función tipo FizzBuzz

Escribe una función que reciba un número y:

  • si es divisible entre 3, devuelva "fizz"

  • si es divisible entre 5, devuelva "buzz"

  • si es divisible entre 3 y 5, devuelva "fizzbuzz"

  • en cualquier otro caso, devuelva el número

💡 Sugerencia: primero escribe el código sin función y verifica que funciona correctamente. Después, conviértelo en una función.

Mostrar código
fizzbuzz <- function(x) {
  if (x %% 15 == 0) {
    return("fizzbuzz")
  } else if (x %% 3 == 0) {
    return("fizz")
  } else if (x %% 5 == 0) {
    return("buzz")
  } else {
    return(x)
  }
}

Verificación:

Mostrar código
fizzbuzz(3)   # "fizz"
[1] "fizz"
Mostrar código
fizzbuzz(5)   # "buzz"
[1] "buzz"
Mostrar código
fizzbuzz(15)  # "fizzbuzz"
[1] "fizzbuzz"
Mostrar código
fizzbuzz(7)   # 7
[1] 7
💻Ejercicio: Clasificación de temperatura

Escribe una función que reciba una temperatura (en grados Celsius) y devuelva:

  • "congelación" si es menor o igual a 0

  • "frío" si está entre 0 y 10

  • "templado" si está entre 10 y 20

  • "cálido" si está entre 20 y 30

  • "caliente" si es mayor a 30

💡 Sugerencia: usa if, else if.

Mostrar código
clasificar_temp <- function(temp) {
  if (temp <= 0) {
    return("congelación")
  } else if (temp <= 10) {
    return("frío")
  } else if (temp <= 20) {
    return("templado")
  } else if (temp <= 30) {
    return("cálido")
  } else {
    return("caliente")
  }
}

Verificación

Mostrar código
clasificar_temp(-5)
[1] "congelación"
Mostrar código
clasificar_temp(15)
[1] "templado"
Mostrar código
clasificar_temp(35)
[1] "caliente"
💻Ejercicio: Suma acumulada con ciclo for

Escribe una función que:

  • reciba un número entero n

  • calcule la suma de los números del 1 hasta n

  • devuelva el resultado

💡 Sugerencia: usa un ciclo for.

Mostrar código
suma_acumulada <- function(n) {
  total <- 0
  
  for (i in 1:n) {
    total <- total + i
  }
  
  return(total)
}

Verificación:

Mostrar código
suma_acumulada(5)  # 1+2+3+4+5 = 15
[1] 15