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.
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 enterosa <-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 + breturn(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?
Respuesta
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.
Solución
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 resultadoreturn(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.
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.2if, 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ónif (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 smileENDIF
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íeSINO frunce el ceñoFIN
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 elseno se ejecuta
Si la condición fuera falsa, el resultado sería:
"Falso"
7.3for
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:6for(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:6mi_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 iteracionesfor(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
Solución
Mostrar código
numeros <-1:10resultado <-NULLfor (n in numeros) { resultado[n] <- n^3}resultado
[1] 1 8 27 64 125 216 343 512 729 1000
7.4while
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 <-0valor <-0while (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:`
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
Solución
Mostrar código
valor <-1conteo <-0while (valor <=100) { valor <- valor *2 conteo <- conteo +1}valor
[1] 128
Mostrar código
conteo
[1] 7
7.5break 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 in1: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 <-20while (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 in1: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
Solución
Mostrar código
for (i in1:10) {if (i %%2!=0) {next }print(i)}
[1] 2
[1] 4
[1] 6
[1] 8
[1] 10
7.6case_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: valorSi condición 1 → resultado 1Si condición 2 → resultado 2Si condición 3 → resultado 3En 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:20case_when( x %%35==0~"fizz buzz", x %%5==0~"fizz", x %%7==0~"buzz",.default =as.character(x))
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().
Solución
Mostrar código
x <-1:20case_when( x %%2==0~"par",.default ="impar")
fahr_to_kelvin(temp =32) # Caso correctofahr_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
Solución
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:10stopifnot(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
Solución
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ónsave(squareX, file ="./squareX.RData")# Eliminar la función del entornorm(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().
Solución
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.
Solución
Mostrar código
library(lubridate)
Adjuntando el paquete: 'lubridate'
The following objects are masked from 'package:base':
date, intersect, setdiff, union
suma_acumulada <-function(n) { total <-0for (i in1:n) { total <- total + i }return(total)}
Verificación:
Mostrar código
suma_acumulada(5) # 1+2+3+4+5 = 15
[1] 15
Ejecutar el código
---title: "El ABC de las funciones"author: "Haydeé Peruyero, [https://haydeeperuyero.github.io/](https://haydeeperuyero.github.io/)"lang: esdescription: "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](https://eveliacoss.github.io/ViernesBioinfo2023/Clase5_20Oct2023/D5_Loop.html#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.::: callout-noteCuando escribes una función, estás creando tu propia herramienta: es uno de los primeros pasos para pensar como programador.:::[Source](https://swcarpentry.github.io/r-novice-gapminder-es/10-functions.html)# Tips para generar una función](figures/structure_functions.png)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 devuelveEn 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::: callout-importantUna buena función no solo funciona, también es fácil de leer, entender y reutilizar.:::# Nuestro primer código> **Problema:** Realizar un algoritmo que solicite al usuario *dos numeros enteros*, realice su suma y la imprima en pantalla.```{r Primer codigo, eval=FALSE}# ---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 enterosa <-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.::: callout-tipEn lugar de repetir el mismo algoritmo, podemos nombrarlo y usarlo cuando lo necesitemos.:::# Nuestra primera función> **Problema:** Crear una función que reciba *dos numeros enteros* y realice su suma.```{r Funcion - my_sum v1}my_sum <-function(a, b) { c <- a + breturn(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.:::: callout-important¿Qué nos falta en la función?::: {.callout-tip collapse="true" icon="false"}## RespuestaNo 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.::::::::::: {.callout-note icon="false"}## 💻Ejercicio: Nuestra segunda funciónDefine una función que:- reciba dos números enteros como argumentos- calcule su suma- devuelva el resultadoAsegúrate de que la función funcione correctamente para distintos valores de entrada.::: {.callout-tip collapse="true" icon="false"}## Solución```{r Funcion - my_sum v2}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 resultadoreturn(c)}```En esta versión estamos forzando que los datos sean enteros usando `as.integer()`.💡 Esto [NO]{style="color: red;"} 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.:::::::## Verificación / Realizar pruebasUna 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)`?**```{r Verfiicar my_sum}my_sum(3,4)```💡 Después de ejecutar la función, deberías obtener como resultado: `7`::: callout-noteProbar funciones con ejemplos simples nos permite:- verificar que el resultado es correcto- detectar posibles errores- entender mejor cómo funciona la función:::::: callout-caution## Casos “problemáticos”:```{r}my_sum("3", "4")my_sum("hola", 5)```**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.:::# Conversiones de temperaturaHasta ahora hemos trabajado con una función muy simple (una suma). Ahora veremos un ejemplo más interesante: convertir unidades de temperatura.## Conversión de Fahrenheit a KelvinVamos a crear una función que convierta una temperatura dada en grados Fahrenheit a Kelvin.```{r Funcion - fahr_to_kelvin}fahr_to_kelvin <-function(temp) { kelvin <- ((temp -32) * (5/9)) +273.15return(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](https://swcarpentry.github.io/r-novice-gapminder-es/10-functions.html)## Verificación / Realizar pruebasProbemos la función con valores conocidos:```{r Verfiicar fahr_to_kelvin}fahr_to_kelvin(32) # Punto de congelación del aguafahr_to_kelvin(212) # Punto de ebullición del agua```💡 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.::: {.callout-warning icon="false"}## RemarkA 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::::::: {.callout-note icon="false"}## 💻Ejercicio: Conversión de Kelvin a CelsiusDefine una función que:- reciba una temperatura en Kelvin- la convierta a grados Celsius- devuelva el resultadoAdemás, prueba tu función con algunos valores.[Sofware Carpentry, Funciones](https://swcarpentry.github.io/r-novice-gapminder-es/10-functions.html)::: {.callout-tip collapse="true" icon="false"}## Solución```{r Funcion - kelvin_to_celsius}kelvin_to_celsius <-function(temp) { celsius <- temp -273.15return(celsius)}```**Verificación**```{r Verfiicar kelvin_to_celsius}kelvin_to_celsius(300) kelvin_to_celsius(400) ```:::::::# Combinando funcionesEl 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:```{r}fahr_to_kelvin <-function(temp) { kelvin <- ((temp -32) * (5/9)) +273.15return(kelvin)}kelvin_to_celsius <-function(temp) { celsius <- temp -273.15return(celsius)}```:::: {.callout-note icon="false"}## 📝Desafío: Combinando funcionesDefine 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.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}fahr_to_celsius <-function(temp) { temp_k <-fahr_to_kelvin(temp) result <-kelvin_to_celsius(temp_k)return(result)}```**Verificación**```{r}fahr_to_celsius(68)```:::::::# Estructuras de controlLas 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ónEsto es fundamental para construir programas más complejos y, en particular, para definir funciones más robustas y flexibles.::: callout-importantSin estructuras de control, nuestros programas serían siempre lineales y poco dinámicos.:::[R para principiantes, cap 9](https://bookdown.org/jboscomendoza/r-principiantes4/estructuras-de-control.html)## 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## `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:```{r pseudocode ifelse v1, eval =FALSE}Inicio Recibir datos de entrada Evaluar condiciónif (condición es TRUE) { ejecutar acciones si TRUE } else { ejecutar acciones si FALSE }Fin```::: callout-important- 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:```{r pseudocode ifelse v2, eval=FALSE}IF you are happy THEN smileENDIF```**Ejemplo sencillo:**```{r ejemplo ifelse}if(4>3) {"Verdadero"}```::: callout-note## ¿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:```{r pseudocode ifelse v3, eval=FALSE}SI estás feliz ENTONCES sonríeSINO frunce el ceñoFIN```**Ejemplo sencillo:**```{r}if(4>3) {"Verdadero"} else {"Falso"}```::: callout-note## ¿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"`:::## `for`El ciclo `for` nos permite repetir una operación varias veces, recorriendo los elementos de un objeto (por ejemplo, un vector).**Estructura general**:```{r Estructura For, eval=FALSE}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.```{r ejemplo dado}dado <-1:6for(cara in dado) {print(cara ^2)}```💡 **¿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::: {.callout-important icon="true"}Usar `print()` **no guarda los resultados**, solo los muestra.Si queremos reutilizar los resultados, debemos almacenarlos.:::**Almacenar resultados con `for`**```{r ejemplo dado v2}dado <-1:6mi_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 iteracionesfor(cara in dado) { mi_vector[cara] <- cara ^2}mi_vector```💡 **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`:::: {.callout-note icon="false"}## 💻EjercicioDefine 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::: {.callout-tip collapse="true" icon="false"}## Solución```{r}numeros <-1:10resultado <-NULLfor (n in numeros) { resultado[n] <- n^3}resultado```:::::::## `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**```{r Estructura While, eval=FALSE}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```{r}conteo <-0valor <-0while (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}valorconteo```💡 **¿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::: {.callout-important icon="false"}## ⚠️ Caso importanteSi la condición **no es verdadera desde el inicio**, el ciclo **nunca se ejecuta**:\````{r}conteo <-0while ("dado"=="ficha") { conteo <- conteo +1}conteo```💡 **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`:::::: callout-warningEs importante que la condición eventualmente se vuelva `FALSE`, de lo contrario el ciclo puede ejecutarse indefinidamente (*bucle infinito*).::::::: {.callout-note icon="false"}## 💻EjercicioCrea 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::: {.callout-tip collapse="true" icon="false"}## Solución```{r}valor <-1conteo <-0while (valor <=100) { valor <- valor *2 conteo <- conteo +1}valorconteo```:::::::## `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 actualAmbas 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.```{r}for (i in1:10) {if (i ==3) {break }print(i)}```💡 **¿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````{r}numero <-20while (numero >5) {if (numero ==15) {break } numero <- numero -1}numero```💡 **¿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.```{r}for (i in1:4) {if (i ==3) {next }print(i)}```💡 **¿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`::: {.callout-important icon="false"}## 🧠 Resumen- `break` → detiene el ciclo completamente- `next` → salta solo una iteración::::::: {.callout-note icon="false"}## 💻EjercicioCrea un ciclo `for` del 1 al 10 que:- imprima solo los números **pares**- use `next` para omitir los impares::: {.callout-tip collapse="true" icon="false"}## Solución```{r}for (i in1:10) {if (i %%2!=0) {next }print(i)}```:::::::## `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:```{r esquema general case, eval=FALSE}Entrada: valorSi condición 1 → resultado 1Si condición 2 → resultado 2Si condición 3 → resultado 3En 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`.```{r}library(dplyr)x <-1:20case_when( x %%35==0~"fizz buzz", x %%5==0~"fizz", x %%7==0~"buzz",.default =as.character(x))```💡 **¿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`.```{r}starwars %>%select(name:mass, gender, species) %>%mutate(type =case_when( height >200| mass >200~"large", species =="Droid"~"robot",.default ="other" ) )```💡 **¿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::: {.callout-important icon="false"}## 🧠 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**::::::: {.callout-note icon="false"}## 💻EjercicioCrea 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()`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}x <-1:20case_when( x %%2==0~"par",.default ="impar")```:::::::# Programación defensivaHasta 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`).## ¿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::: {.callout-tip icon="false"}💡 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](https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/stopifnot))[Software Carpentry, Programación defensiva](https://swcarpentry.github.io/r-novice-gapminder-es/10-functions.html)## Ejemplo con `stop()` e `if()````{r}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**```{r, eval=FALSE}my_sum(3, 4) # Caso correctomy_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 ❌::: callout-noteAhora la función no falla en silencio, sino que avisa claramente el problema.:::## Ejemplo con `stopifnot()````{r}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:**```{r, eval=FALSE}my_sum(3, 4) # Caso correctomy_sum("hola", 5) # Caso incorrecto```💡 **¿Qué ocurre?**- `my_sum(3, 4)`: devuelve `7` ✔️- `my_sum("hola", 5)`: lanza un error automáticamente ❌:::: {.callout-note icon="false"}## 💻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()`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}fahr_to_kelvin <-function(temp) {if (!is.numeric(temp)) {stop("temp must be a numeric vector.") } kelvin <- ((temp -32) * (5/9)) +273.15return(kelvin)}```**Verificación:**```{r, eval=FALSE}fahr_to_kelvin(temp =32) # Caso correctofahr_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.::::::::::: {.callout-note icon="false"}## 💻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()`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}fahr_to_kelvin <-function(temp) {stopifnot(is.numeric(temp)) kelvin <- ((temp -32) * (5/9)) +273.15return(kelvin)}```**Verificación:**```{r, eval=FALSE}fahr_to_kelvin(temp =32) # Caso correctofahr_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.:::::::## 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:```{r}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**```{r}squareX("4")``````{r}squareX(4)```### 💡 ¿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`)::: {.callout-important icon="false"}## 🧠 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:::: {.callout-note icon="false"}## 💻EjercicioModifica 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::: {.callout-tip collapse="true" icon="false"}## Solución```{r}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}```:::::::## Usando el paquete `assertthat`El paquete [assertthat]{.underline} nos permite validar condiciones, de manera similar a `stopifnot()`, pero con **mensajes de error más claros y personalizables**.### Instalación y carga del paqueteSi no lo tienes instalado:```{r, eval=FALSE}install.packages("assertthat")```Para usarlo en tu sesión:```{r}library(assertthat)```### 💡 ¿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()`**```{r, eval=FALSE}x <-1:10stopifnot(is.character(x))``````{r, eval=FALSE}assert_that(is.character(x))``````{r, eval=FALSE}assert_that(length(x) ==5)``````{r, eval=FALSE}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**```{r}foo_message <-function(x) {assert_that(x ==1, msg ="x must always be 1")"yay"}```**Verificación / Realizar pruebas**```{r}foo_message(1)``````{r, eval=FALSE}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 ❌::: {.callout-important icon="false"}## 🧠 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::::::: {.callout-note icon="false"}## 💻EjercicioModifica la función `squareX()` para que:- use `assert_that()` para verificar que `x` sea numérico- muestre un mensaje claro si no lo es::: {.callout-tip collapse="true" icon="false"}## Solución```{r}squareX <-function(x) {assert_that(is.numeric(x), msg ="x debe ser numérico") x^2}```:::::::## 🧠 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.# Guardar y reutilizar funcionesHasta 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.## Opción A: Guardar como archivo `.RData`Podemos guardar una función como un objeto en un archivo `.RData`.```{r}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ónsave(squareX, file ="./squareX.RData")# Eliminar la función del entornorm(squareX)```### Cargar la funciónPara volver a usarla:```{r, eval=FALSE}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## 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:```{r, eval=FALSE}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## 🧠 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 |## 🎯 Recomendación> Para desarrollo y proyectos, es mejor usar archivos `.R` y `source()`.:::: {.callout-note icon="false"}## 💻EjercicioCrea un archivo llamado `mis_funciones.R` que contenga al menos:- `my_sum()`- `squareX()`Luego, cárgalo en tu sesión usando `source()`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r, eval=FALSE}my_sum <-function(a, b) { a + b}squareX <-function(x) { x^2}``````{r, eval=FALSE}source("mis_funciones.R")```:::::::# Ejercicios extra:::: {.callout-note icon="false"}## 💻Ejercicio: Función de saludo según la horaEscribe 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.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}library(lubridate)greeting <-function(time =now()) { hora <-hour(time)if (hora <12) {return("Buenos días") } elseif (hora <18) {return("Buenas tardes") } else {return("Buenas noches") }}```**Verificación:**```{r}greeting() # Usa la hora actual``````{r}greeting(ymd_hms("2024-01-01 10:00:00")) # Mañanagreeting(ymd_hms("2024-01-01 15:00:00")) # Tardegreeting(ymd_hms("2024-01-01 21:00:00")) # Noche```::::::::::: {.callout-note icon="false"}## 💻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.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}fizzbuzz <-function(x) {if (x %%15==0) {return("fizzbuzz") } elseif (x %%3==0) {return("fizz") } elseif (x %%5==0) {return("buzz") } else {return(x) }}```**Verificación:**```{r}fizzbuzz(3) # "fizz"fizzbuzz(5) # "buzz"fizzbuzz(15) # "fizzbuzz"fizzbuzz(7) # 7```::::::::::: {.callout-note icon="false"}## 💻Ejercicio: Clasificación de temperaturaEscribe 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`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}clasificar_temp <-function(temp) {if (temp <=0) {return("congelación") } elseif (temp <=10) {return("frío") } elseif (temp <=20) {return("templado") } elseif (temp <=30) {return("cálido") } else {return("caliente") }}```**Verificación**```{r}clasificar_temp(-5)clasificar_temp(15)clasificar_temp(35)```::::::::::: {.callout-note icon="false"}## 💻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`.::: {.callout-tip collapse="true" icon="false"}## Solución```{r}suma_acumulada <-function(n) { total <-0for (i in1:n) { total <- total + i }return(total)}```**Verificación:**```{r}suma_acumulada(5) # 1+2+3+4+5 = 15```:::::::