R
Tutorial R: Funciones anónimas
Aprende a usar funciones anónimas, la familia apply y funciones que retornan funciones en R para análisis y transformación de datos eficiente.
Aprende R y certifícateSintaxis de funciones sin nombre (lambda)
En R, las funciones anónimas (también conocidas como funciones lambda) son funciones que se crean sin asignarles un nombre específico. Estas construcciones son especialmente útiles cuando necesitamos una función para una operación puntual y no queremos definirla formalmente con la sintaxis tradicional.
Estructura básica
La sintaxis para crear una función anónima en R es muy similar a la de una función normal, pero omitiendo el nombre:
function(argumentos) {
# cuerpo de la función
}
A diferencia de las funciones regulares que definimos con un nombre mediante asignación, las funciones anónimas se utilizan directamente donde se necesitan. Veamos un ejemplo sencillo:
# Función anónima que calcula el cuadrado de un número
(function(x) x^2)(5)
Este código crea una función que eleva al cuadrado su argumento y la ejecuta inmediatamente con el valor 5, devolviendo 25. Observa que:
- La función se define con
function(x) x^2
- Los paréntesis externos permiten ejecutarla inmediatamente
- El valor
(5)
al final es el argumento que se pasa a la función
Sintaxis simplificada para expresiones sencillas
Cuando el cuerpo de la función es una expresión simple, podemos omitir las llaves:
# Con llaves
(function(x) { x + 10 })(5) # Devuelve 15
# Sin llaves (más conciso)
(function(x) x + 10)(5) # También devuelve 15
Para expresiones más complejas, las llaves son necesarias:
(function(x, y) {
resultado <- x * y
return(resultado + 5)
})(3, 4) # Devuelve 17
Múltiples argumentos
Las funciones anónimas pueden aceptar múltiples argumentos, igual que las funciones normales:
# Función anónima con dos argumentos
(function(x, y) x * y + x)(3, 4) # Devuelve 15
También podemos definir valores predeterminados para los argumentos:
# Función anónima con argumento predeterminado
(function(x, potencia = 2) x^potencia)(3) # Devuelve 9
(function(x, potencia = 2) x^potencia)(3, 3) # Devuelve 27
Uso práctico de funciones anónimas
Las funciones anónimas rara vez se utilizan de forma aislada como en los ejemplos anteriores. Su verdadero potencial se manifiesta cuando se pasan como argumentos a otras funciones. Veamos algunos casos de uso comunes:
- Filtrado de datos:
numeros <- 1:10
filter(numeros, function(x) x %% 2 == 0) # Filtra números pares
- Transformación de vectores:
# Convertir temperaturas de Celsius a Fahrenheit
temperaturas_c <- c(0, 10, 20, 30)
sapply(temperaturas_c, function(temp) (temp * 9/5) + 32)
# Devuelve: 32 50 68 86
- Operaciones personalizadas en dataframes:
# Dataframe de ejemplo
df <- data.frame(
nombre = c("Ana", "Carlos", "Elena"),
edad = c(25, 30, 28)
)
# Aplicar una transformación personalizada
transform(df, categoria_edad = sapply(edad, function(e) {
if(e < 26) "Joven" else "Adulto"
}))
Captura de variables del entorno
Una característica importante de las funciones anónimas es que pueden capturar variables del entorno donde fueron creadas:
multiplicador <- 10
numeros <- 1:5
# La función anónima captura la variable 'multiplicador'
sapply(numeros, function(x) x * multiplicador)
# Devuelve: 10 20 30 40 50
En este ejemplo, la función anónima utiliza la variable multiplicador
que está definida en el entorno exterior.
Ventajas y limitaciones
Las ventajas principales de las funciones anónimas son:
- Código más conciso para operaciones simples
- Evitan "contaminar" el espacio de nombres con funciones de un solo uso
- Permiten definir la lógica exactamente donde se necesita
Sin embargo, también tienen limitaciones:
- Pueden hacer el código menos legible si son complejas
- No son reutilizables (a menos que se asignen a una variable)
- Dificultan la depuración en comparación con funciones nombradas
Asignación de funciones anónimas
Aunque parezca contradictorio, podemos asignar una función anónima a una variable, convirtiéndola efectivamente en una función con nombre:
# Asignar una función anónima a una variable
calcular_area <- function(radio) pi * radio^2
# Ahora podemos usarla como cualquier otra función
calcular_area(5) # Devuelve aproximadamente 78.54
Esta técnica es común en R y representa la forma estándar de definir funciones, aunque técnicamente estamos asignando una función anónima a un nombre.
Funciones anónimas en una línea
Para funciones muy simples, podemos escribir todo en una sola línea, lo que resulta útil especialmente cuando se pasan como argumentos:
# Filtrar valores mayores que 5
Filter(function(x) x > 5, 1:10) # Devuelve 6 7 8 9 10
# Calcular la raíz cuadrada de cada número
sapply(1:5, function(x) sqrt(x)) # Devuelve 1.000 1.414 1.732 2.000 2.236
Esta sintaxis compacta es particularmente útil cuando trabajamos con las funciones de la familia apply
, que veremos en la siguiente sección.
Apply y funciones de orden superior
En R, las funciones de orden superior son aquellas que pueden recibir otras funciones como argumentos o devolver funciones como resultado. La familia de funciones apply
representa uno de los ejemplos más útiles y potentes de este concepto, permitiéndonos aplicar operaciones a conjuntos de datos de forma elegante y eficiente.
La familia apply
La familia apply
proporciona alternativas vectorizadas a los bucles tradicionales, haciendo el código más conciso y generalmente más rápido. Estas funciones toman una colección de datos y una función (que puede ser anónima) para aplicarla a cada elemento.
Las principales funciones de esta familia son:
- apply: Opera sobre matrices o arrays por filas o columnas
- lapply: Opera sobre listas y devuelve una lista
- sapply: Similar a lapply pero intenta simplificar el resultado
- vapply: Versión más segura de sapply con tipo de retorno especificado
- mapply: Versión multivariable de sapply/lapply
Veamos cómo funcionan con ejemplos prácticos:
apply()
La función apply()
trabaja con matrices o arrays y aplica una función a lo largo de un margen específico (filas o columnas):
# Crear una matriz de ejemplo
matriz <- matrix(1:9, nrow = 3)
matriz
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9
# Sumar cada fila (margen = 1)
apply(matriz, 1, function(fila) sum(fila))
# [1] 12 15 18
# Calcular la media de cada columna (margen = 2)
apply(matriz, 2, function(columna) mean(columna))
# [1] 2 5 8
El segundo argumento (1
o 2
) indica si queremos operar por filas o columnas respectivamente.
lapply()
La función lapply()
aplica una función a cada elemento de una lista y devuelve siempre una lista:
# Lista de vectores
lista_numeros <- list(a = 1:3, b = 4:6, c = 7:9)
# Calcular la suma de cada vector en la lista
lapply(lista_numeros, function(x) sum(x))
# $a
# [1] 6
# $b
# [1] 15
# $c
# [1] 24
# También funciona con vectores, convirtiéndolos temporalmente en listas
lapply(1:5, function(x) x^2)
# [[1]]
# [1] 1
# [[2]]
# [1] 4
# [[3]]
# [1] 9
# [[4]]
# [1] 16
# [[5]]
# [1] 25
sapply()
La función sapply()
es similar a lapply()
, pero intenta simplificar el resultado cuando es posible:
# El mismo ejemplo anterior con sapply
sapply(lista_numeros, function(x) sum(x))
# a b c
# 6 15 24
# Con vectores, devuelve un vector en lugar de una lista
sapply(1:5, function(x) x^2)
# [1] 1 4 9 16 25
# Si la función devuelve múltiples valores, sapply crea una matriz
sapply(1:3, function(x) c(x, x^2))
# [,1] [,2] [,3]
# [1,] 1 2 3
# [2,] 1 4 9
vapply()
La función vapply()
es una versión más segura de sapply()
que requiere especificar el tipo de retorno esperado:
# Especificamos que esperamos un único valor numérico para cada elemento
vapply(1:5, function(x) x^2, numeric(1))
# [1] 1 4 9 16 25
# Si intentamos devolver algo diferente, obtendremos un error
# vapply(1:3, function(x) if(x==2) "dos" else x, numeric(1))
# Error: valores no coinciden con el tipo especificado
mapply()
La función mapply()
aplica una función a múltiples argumentos en paralelo:
# Vectores de entrada
nombres <- c("Ana", "Carlos", "Elena")
edades <- c(25, 30, 28)
# Crear un mensaje personalizado para cada persona
mapply(function(nombre, edad) {
paste(nombre, "tiene", edad, "años")
}, nombres, edades)
# [1] "Ana tiene 25 años" "Carlos tiene 30 años" "Elena tiene 28 años"
Combinando apply con funciones anónimas
El verdadero poder de estas funciones se aprecia cuando las combinamos con funciones anónimas para operaciones específicas:
# Dataframe de ejemplo
df <- data.frame(
producto = c("A", "B", "C", "D"),
precio = c(100, 200, 150, 300),
descuento = c(0.1, 0.05, 0.15, 0)
)
# Calcular precio final con descuento
df$precio_final <- sapply(1:nrow(df), function(i) {
df$precio[i] * (1 - df$descuento[i])
})
df
# producto precio descuento precio_final
# 1 A 100 0.10 90.0
# 2 B 200 0.05 190.0
# 3 C 150 0.15 127.5
# 4 D 300 0.00 300.0
Uso de funciones anónimas con condiciones
Podemos crear lógica más compleja dentro de nuestras funciones anónimas:
# Categorizar números según diferentes criterios
numeros <- 1:10
sapply(numeros, function(x) {
if (x %% 2 == 0) {
return("Par")
} else if (x %% 3 == 0) {
return("Múltiplo de 3")
} else {
return("Otro")
}
})
# [1] "Otro" "Par" "Múltiplo de 3" "Par"
# [5] "Otro" "Par" "Otro" "Par"
# [9] "Múltiplo de 3" "Par"
Aplicando transformaciones a listas de datos
Las funciones de la familia apply
son especialmente útiles para procesar listas de datos:
# Lista de vectores de temperaturas diarias (en Celsius)
temperaturas_semana <- list(
lunes = c(15, 18, 20, 19),
martes = c(14, 16, 18, 17),
miercoles = c(16, 19, 21, 20)
)
# Calcular estadísticas para cada día
estadisticas <- lapply(temperaturas_semana, function(dia) {
list(
media = mean(dia),
min = min(dia),
max = max(dia)
)
})
# Ver resultados para el lunes
estadisticas$lunes
# $media
# [1] 18
#
# $min
# [1] 15
#
# $max
# [1] 20
Ventajas de usar apply con funciones anónimas
Utilizar la familia apply
con funciones anónimas ofrece varias ventajas:
- Código más limpio y menos propenso a errores que los bucles tradicionales
- Mayor legibilidad al expresar claramente la intención del código
- Mejor rendimiento en muchos casos gracias a la vectorización interna
- Flexibilidad para crear operaciones personalizadas sin definir funciones separadas
Ejemplo práctico: análisis de datos
Veamos un ejemplo más completo de análisis de datos usando estas funciones:
# Datos de ventas mensuales por producto
ventas <- list(
producto_A = c(120, 135, 160, 145),
producto_B = c(85, 90, 95, 100),
producto_C = c(200, 180, 210, 190)
)
# Análisis de ventas
resultados <- lapply(ventas, function(producto) {
# Calcular varias métricas
promedio <- mean(producto)
crecimiento <- (producto[length(producto)] - producto[1]) / producto[1] * 100
# Determinar tendencia
tendencia <- if(crecimiento > 10) {
"Crecimiento fuerte"
} else if(crecimiento > 0) {
"Crecimiento moderado"
} else if(crecimiento > -10) {
"Decrecimiento moderado"
} else {
"Decrecimiento fuerte"
}
# Devolver resultados como lista
list(
promedio_ventas = promedio,
porcentaje_crecimiento = crecimiento,
tendencia = tendencia
)
})
# Simplificar resultados para mejor visualización
data.frame(
producto = names(ventas),
promedio = sapply(resultados, function(x) x$promedio_ventas),
crecimiento = sapply(resultados, function(x) x$porcentaje_crecimiento),
tendencia = sapply(resultados, function(x) x$tendencia)
)
# producto promedio crecimiento tendencia
# 1 producto_A 140.0 20.83333 Crecimiento fuerte
# 2 producto_B 92.5 17.64706 Crecimiento fuerte
# 3 producto_C 195.0 -5.00000 Decrecimiento moderado
Este ejemplo muestra cómo podemos encadenar diferentes funciones de la familia apply
con funciones anónimas para realizar un análisis completo de datos en pocas líneas de código.
Funciones que retornan funciones
En R, una característica poderosa pero a menudo subestimada es la capacidad de crear funciones que devuelven otras funciones. Este concepto permite construir abstracciones elegantes y reutilizables que adaptan su comportamiento según las necesidades específicas.
Una función que retorna otra función es simplemente una función cuyo valor de retorno es una nueva función (generalmente anónima). Esta técnica nos permite crear "fábricas de funciones" personalizadas.
Estructura básica
La estructura general de una función que retorna otra función sigue este patrón:
crear_funcion <- function(parametros_externos) {
function(parametros_internos) {
# Código que usa tanto parametros_externos como parametros_internos
}
}
Veamos un ejemplo sencillo:
# Función que crea multiplicadores personalizados
crear_multiplicador <- function(factor) {
function(x) {
x * factor
}
}
# Crear funciones específicas
duplicar <- crear_multiplicador(2)
triplicar <- crear_multiplicador(3)
# Usar las funciones generadas
duplicar(5) # Devuelve 10
triplicar(5) # Devuelve 15
En este ejemplo, crear_multiplicador
es una función que retorna otra función. El parámetro factor
se incorpora en la definición de la función retornada, creando así funciones especializadas.
Personalización de comportamientos
Una aplicación práctica es crear funciones con comportamientos personalizados según ciertos parámetros:
# Crear una función de saludo personalizada
crear_saludo <- function(idioma) {
if (idioma == "español") {
function(nombre) paste("¡Hola,", nombre, "!")
} else if (idioma == "inglés") {
function(nombre) paste("Hello,", nombre, "!")
} else if (idioma == "francés") {
function(nombre) paste("Bonjour,", nombre, "!")
} else {
function(nombre) paste("Hola,", nombre, "!") # Valor predeterminado
}
}
# Crear funciones de saludo en diferentes idiomas
saludar_es <- crear_saludo("español")
saludar_en <- crear_saludo("inglés")
saludar_fr <- crear_saludo("francés")
# Usar las funciones
saludar_es("María") # Devuelve "¡Hola, María !"
saludar_en("John") # Devuelve "Hello, John !"
saludar_fr("Pierre") # Devuelve "Bonjour, Pierre !"
Creación de funciones con parámetros preestablecidos
Podemos usar este enfoque para crear versiones especializadas de funciones existentes:
# Crear funciones de filtrado personalizadas
crear_filtro <- function(condicion) {
function(datos) {
subset(datos, eval(parse(text = condicion)))
}
}
# Datos de ejemplo
empleados <- data.frame(
nombre = c("Ana", "Carlos", "Elena", "David"),
edad = c(25, 42, 31, 38),
departamento = c("Ventas", "IT", "Ventas", "Marketing")
)
# Crear filtros específicos
filtrar_ventas <- crear_filtro("departamento == 'Ventas'")
filtrar_mayores_30 <- crear_filtro("edad > 30")
# Aplicar los filtros
filtrar_ventas(empleados)
# nombre edad departamento
# 1 Ana 25 Ventas
# 3 Elena 31 Ventas
filtrar_mayores_30(empleados)
# nombre edad departamento
# 2 Carlos 42 IT
# 3 Elena 31 Ventas
# 4 David 38 Marketing
Funciones matemáticas personalizadas
Las funciones que retornan funciones son especialmente útiles para crear transformaciones matemáticas:
# Crear una función de potencia personalizada
crear_potencia <- function(exponente) {
function(base) {
base ^ exponente
}
}
# Crear funciones específicas
cuadrado <- crear_potencia(2)
cubo <- crear_potencia(3)
raiz_cuadrada <- crear_potencia(0.5)
# Aplicar las funciones
cuadrado(4) # Devuelve 16
cubo(3) # Devuelve 27
raiz_cuadrada(16) # Devuelve 4
Creación de funciones de transformación de datos
Podemos crear funciones que apliquen transformaciones específicas a conjuntos de datos:
# Crear transformador de temperatura
crear_conversor_temperatura <- function(escala_destino) {
if (escala_destino == "F") {
function(celsius) (celsius * 9/5) + 32
} else if (escala_destino == "K") {
function(celsius) celsius + 273.15
} else {
function(celsius) celsius # Devuelve la misma temperatura si la escala no es reconocida
}
}
# Crear conversores específicos
a_fahrenheit <- crear_conversor_temperatura("F")
a_kelvin <- crear_conversor_temperatura("K")
# Convertir temperaturas
temperaturas_c <- c(0, 25, 100)
sapply(temperaturas_c, a_fahrenheit) # Devuelve 32.0 77.0 212.0
sapply(temperaturas_c, a_kelvin) # Devuelve 273.15 298.15 373.15
Funciones para procesamiento de texto
También podemos crear funciones especializadas para manipular texto:
# Crear función de formato de texto
crear_formateador <- function(prefijo, sufijo = "") {
function(texto) {
paste0(prefijo, texto, sufijo)
}
}
# Crear formateadores específicos
formato_html_negrita <- crear_formateador("<b>", "</b>")
formato_html_italica <- crear_formateador("<i>", "</i>")
formato_lista <- crear_formateador("- ")
# Aplicar formatos
formato_html_negrita("Importante") # Devuelve "<b>Importante</b>"
formato_html_italica("Énfasis") # Devuelve "<i>Énfasis</i>"
formato_lista("Primer elemento") # Devuelve "- Primer elemento"
Creación de funciones de validación
Podemos generar validadores personalizados para diferentes tipos de datos:
# Crear validador personalizado
crear_validador <- function(tipo, mensaje = NULL) {
if (tipo == "numerico") {
function(x) {
if (!is.numeric(x)) {
stop(mensaje %||% "El valor debe ser numérico")
}
return(TRUE)
}
} else if (tipo == "positivo") {
function(x) {
if (!is.numeric(x) || x <= 0) {
stop(mensaje %||% "El valor debe ser un número positivo")
}
return(TRUE)
}
} else if (tipo == "rango") {
function(x, min, max) {
if (!is.numeric(x) || x < min || x > max) {
stop(sprintf(mensaje %||% "El valor debe estar entre %s y %s", min, max))
}
return(TRUE)
}
}
}
# Definir el operador %||% (valor por defecto si NULL)
`%||%` <- function(x, y) if (is.null(x)) y else x
# Crear validadores específicos
validar_numero <- crear_validador("numerico")
validar_positivo <- crear_validador("positivo")
validar_rango <- crear_validador("rango")
# Usar los validadores
tryCatch({
validar_positivo(-5)
}, error = function(e) {
print(e$message) # Imprime "El valor debe ser un número positivo"
})
tryCatch({
validar_rango(15, 1, 10)
}, error = function(e) {
print(e$message) # Imprime "El valor debe estar entre 1 y 10"
})
Aplicaciones en análisis de datos
Las funciones que retornan funciones son particularmente útiles en análisis de datos:
# Crear función para generar estadísticas personalizadas
crear_estadistica <- function(tipo) {
if (tipo == "centro") {
function(x, ...) {
c(media = mean(x, ...),
mediana = median(x, ...),
moda = as.numeric(names(sort(table(x), decreasing = TRUE)[1])))
}
} else if (tipo == "dispersion") {
function(x, ...) {
c(desviacion = sd(x, ...),
varianza = var(x, ...),
rango = max(x) - min(x))
}
} else if (tipo == "resumen") {
function(x, ...) {
c(minimo = min(x, ...),
maximo = max(x, ...),
media = mean(x, ...),
mediana = median(x, ...))
}
}
}
# Crear funciones estadísticas específicas
estadistica_centro <- crear_estadistica("centro")
estadistica_dispersion <- crear_estadistica("dispersion")
estadistica_resumen <- crear_estadistica("resumen")
# Datos de ejemplo
datos <- c(12, 15, 18, 15, 21, 23, 15, 18)
# Aplicar las funciones
estadistica_centro(datos)
# media mediana moda
# 17.125 16.500 15.000
estadistica_dispersion(datos)
# desviacion varianza rango
# 3.603102 12.982143 11.000000
estadistica_resumen(datos)
# minimo maximo media mediana
# 12.0000 23.0000 17.1250 16.5000
Ventajas de las funciones que retornan funciones
Esta técnica ofrece varias ventajas:
- Encapsulación: Los parámetros externos quedan "encapsulados" en la función retornada
- Reutilización: Permite crear familias de funciones relacionadas sin duplicar código
- Abstracción: Ofrece interfaces más simples para operaciones complejas
- Flexibilidad: Facilita la creación de comportamientos personalizados
Consideraciones prácticas
Al trabajar con funciones que retornan funciones, ten en cuenta:
- Las funciones retornadas pueden acceder a las variables del entorno donde fueron creadas
- Es recomendable asignar las funciones generadas a variables con nombres descriptivos
- Para depuración, considera incluir mensajes o comentarios que indiquen qué función se está utilizando
Las funciones que retornan funciones representan una herramienta fundamental en la programación funcional en R, permitiendo crear abstracciones elegantes y reutilizables que adaptan su comportamiento según necesidades específicas.
Otros ejercicios de programación de R
Evalúa tus conocimientos de esta lección Funciones anónimas 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
Introducción Y Entorno
Introducción A R
Introducción Y Entorno
Operadores
Sintaxis
Estructuras De Datos
Sintaxis
Funciones
Sintaxis
Estructuras De Control Iterativo
Sintaxis
Scopes Y Closures
Sintaxis
Estructuras De Control Condicional
Sintaxis
Funciones Anónimas
Sintaxis
Tipos De Datos Y Variables
Sintaxis
Sistema R6: Clases Referenciales Y Encapsulamiento
Programación Orientada A Objetos
Sistema S4: Clases Formales Y Validación
Programación Orientada A Objetos
Herencia Y Polimorfismo En R
Programación Orientada A Objetos
Sistemas De Oop En R
Programación Orientada A Objetos
Sistema S3: Clases Implícitas Y Métodos Genéricos
Programación Orientada A Objetos
Tidyverse Para Transformación De Datos
Manipulación De Datos
Lubridate Para Fechas Y Tiempo
Manipulación De Datos
Group_by Y Summarize Para Agrupación Y Resumen
Manipulación De Datos
Stringr Para Expresiones Regulares
Manipulación De Datos
Tidyr Para Limpieza De Valores Faltantes
Manipulación De Datos
Joins En R Para Combinación Y Relaciones De Tablas
Manipulación De Datos
Pivot_longer Y Pivot_wider Para Reestructuración
Manipulación De Datos
Mutate Y Transmute Para Transformación
Manipulación De Datos
Dplyr Para Filtrado Y Selección
Manipulación De Datos
Readr Y Read.csv Para Importar Datos
Manipulación De Datos
Gráficos Bivariantes En R
Visualización De Datos
Gráficos Univariantes En R
Visualización De Datos
Facetas En Ggplot2
Visualización De Datos
Personalización Y Temas
Visualización De Datos
Ggplot2 Para Visualización De Datos
Visualización De Datos
Gráficos Multivariantes En R
Visualización De Datos
Correlación En R
Estadística
Regresión Lineal En R
Estadística
Pruebas De Hipótesis En R
Estadística
Anova En R
Estadística
Estadística Descriptiva En R
Estadística
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la sintaxis y uso de funciones anónimas (lambda) en R.
- Aplicar funciones anónimas con la familia apply para manipulación eficiente de datos.
- Crear y utilizar funciones que retornan otras funciones para personalización y reutilización.
- Entender la captura de variables del entorno en funciones anónimas.
- Reconocer ventajas y limitaciones de estas técnicas en programación funcional en R.