3  Programación funcional

3.1 Familia apply

Una función de orden superior es una función que como argumento usa otra función. Son una alternativa para los bucles y trabajan vectorialemente con los datos, así que además de buscar una mayor belleza matemática en tu código, conseguiremos mayor eficiencia.

Tu puedes implementar funciones de orden superior, pero por ahora vamos a aprender a usar la familia de funciones apply:

  • lapply
  • sapply
  • tapply
  • apply
  • etc.

Cogen una función como entrada y devuelven un vector.

3.1.1 lapply

Va tomando cada elemento de una lista, evalúa la función que aparece al final de lapply a cada elemento seleccionado de la lista y devuelve una lista con todos los resultados.

Ejecuta:

Notas <- list(alumno1=c(2.5,3.7,6), alumno2=c(5,6.7,10))
Notas
medias <- lapply(Notas,mean)
medias
class(medias)

Como ves tenemos como resultado una lista con la media de cada una de los vectores que contiene la lista de entrada.

Simplemente otro ejemplo:

alumnos <- c("JUAN","PEDRO","ISABEL","LUISA")
alumnos.minus <-lapply(alumnos, tolower)
alumnos.minus
[[1]]
[1] "juan"

[[2]]
[1] "pedro"

[[3]]
[1] "isabel"

[[4]]
[1] "luisa"
class(alumnos.minus)
[1] "list"

3.1.2 sapply

Toma como entrada una lista y:

  • Itera sobre cada elemento de la lista.
  • Aplica una función \(f\) a cada elemento \(x\) de la lista.
  • Devuelve un vector si cada \(f(x)\) tiene longitud 1.
  • Devuelve una matriz si cada \(f(x)\) tiene longitud mayor que 1.

Veamos un par de ejemplos para entender cómo funciona.

Notas <- list(alumno1=c(2.5,3.7,6), alumno2=c(5,6.7,10))
Notas
medias <- sapply(Notas,mean)
medias
class(medias)

En este caso, ves que devuelve un vector.

Usaremos sapply con un dataset que se almacenará en R en un data.frame.

Ejecuta:

data("airquality")
View(airquality)
sapply(airquality, mean)

Calculamos con sapply la media para cada columna.

Pregunta: ¿Porque me ha devuelto para las dos primeras columnas el valor NA? Arréglalo.

3.1.3 tapply

Aplica una función a subconjuntos de un vector. Es útil cuando tienes que romper un vector en grupos y a cada grupo aplicarle una función.

En el dataset anterior airquality una columna tiene los meses. Calcularemos la media de la temperatura para cada uno de los meses:

tapply(airquality$Temp,airquality$Month,  mean)
       5        6        7        8        9 
65.54839 79.10000 83.90323 83.96774 76.90000 

3.1.4 apply

Aplica una función \(f\) a cada elemento sobre una dimensión de una matriz.

M <- matrix(rnorm(100),10,10)
apply(M,1,mean) ## Calcula la media de cada fila de la matriz
apply(M,2,mean) ## Calcula la media de cada columna de la matriz
apply(M,1,sum) ## Calcula la suma de cada fila de la matriz
apply(M,2,sum) ## Calcula la suma de cada columna de la matriz

3.1.5 split

  • split coge un objeto (vector, data frame, etc. ) y lo divide en grupos determinados por una variable de tipo factor.
  • Usado en combinación con sapply, tapply (paradigma map-reduce ).
v <-1:100
f <- gl(10,10) ## consulta ayuda de gl    (  >?gl )
split(v,f)
lapply(split(v,f), mean)
#con data frames
library(datasets)
head(airquality)
s <- split(airquality, airquality$Month)
lapply(s,function(x) mean(x[,"Wind"]) )

3.1.6 Funciones anónimas

Se usan funciones anónimas (sin nombre) dentro de otras funciones, cuando su importancia no es muy grande o cuando solo se va a usar dentro de la función en la que está encerrada.

Ejemplo:

sin_cos <- function(parametro) {
  if (parametro==0)
      function(x) sin(x)
  else
      function(x) cos(x)
}#end function

sin1 <- sin_cos(0)
cos1 <- sin_cos(1)

Ahora podemos usar sin1 y cos1 como funciones.

sin1(pi)
[1] 1.224647e-16
cos1(pi)
[1] -1

Cuando ejecutamos sin1 sin argumentos aparece:

sin1
function(x) sin(x)
<environment: 0x10a586e00>

que indica que sin1 es la función sin(x).

Más adelante veremos la utilidad de estas funciones. Por ahora otro ejemplo.

Un ejemplo más usado, una función anónima dentro de lapply.

x <- 1:10
funs <- list(
  sum = sum,
  mean = mean,
  median = median
)
lapply(funs, function(f) f(x))
$sum
[1] 55

$mean
[1] 5.5

$median
[1] 5.5

f va tomando como valores las distintas funciones que hay almacenadas en la lista funs. Es decir, aplicamos la suma, la media y la mediana al vector x.

3.1.7 map

Es una función del paquete purr. Debes instalarlo la primera vez que lo uses.

La función map transforma su entrada aplicándole una función a cada elemento y devolviendo un vector con la misma longitud que la entrada.

Ejecuta:

library(purrr)# Instalar la primera vez
data("airquality")
View(airquality)
map(airquality,sum)

El código anterior con la filosofia tidyverse (tuberías) aparecerá como:

library(magrittr);library(purrr)

Attaching package: 'purrr'
The following object is masked from 'package:magrittr':

    set_names
airquality %>% map(sum)
$Ozone
[1] NA

$Solar.R
[1] NA

$Wind
[1] 1523.5

$Temp
[1] 11916

$Month
[1] 1070

$Day
[1] 2418

3.2 Ejercicios

3.2.1 1. Funciones apply con listas

Dada la siguiente lista con nombre lista1 que debes introducir en R:

La lista nombrada lista1 es:
$notas_grupo1
 [1] 1 2 3 4 5 7 6 5 4 3

$notas_grupo2_3
     [,1] [,2] [,3]
[1,]    2    2    8
[2,]    3    9    8
La estructura de la lista es:
List of 2
 $ notas_grupo1  : int [1:10] 1 2 3 4 5 7 6 5 4 3
 $ notas_grupo2_3: num [1:2, 1:3] 2 3 2 9 8 8

Resuelve los siguientes ejercicios:

  1. Usa lapply() para encontrar la longitud de las notas de todos los grupos que están en lista1.

  2. Usa sapply() para encontrar la media de las notas de todos los grupos que están en lista1.

  3. Usa lapply() para encontrar los quantiles de las notas de todos los grupos que están en lista1.

  4. Dada la siguiente función \(f_1(x)=(3x)/10\), aplica \(f_1(x)\) a todas las notas para cambiar las puntuaciones a la escala de 1 a 3.

  5. Repite el ejercicio 4 pero haciendo que \(f_1(x)\) sea una función anónima dentro de lapply().

  6. Encuentra los valores no repetidos de las notas

  7. Convierte todas las notas de la lista a un vector (aplana los valores).