Programación funcional en R: Reduce

Siguiendo con la serie de artículos sobre programación funcional que comencé hablando de Filter() hace un tiempo, trataré hoy la función Reduce(). El contenido de cuanto sigue debería ser familiar de quienes asistieron al Taller Avanzado de R en las II Jornadas de Usuarios de R.

Reduce es el segundo de los tiempos de una abstracción popularizado por Google y otros pero que tiene sus raíces en los lenguajes funcionales (Lisp y otros): map-reduce. En resumen, map es la transformación

(v_1, \dots, v_n ) \longmapsto (f(v_1), \dots, f(v_n) )

mientras que reduce consiste en (algo parecido a)

(f(v_1), \dots, f(v_n) )  \longmapsto F( f(v_1), \dots, f(v_n) ),

que devuelve un único valor a partir del vector transformado. Dado un vector v=(v_1, \dots, v_n ) y una función f, sabemos cómo construir (f(), \dots, f() ) y (f(v_1), \dots, f(v_n) ) usando las funciones replicate() y sapply(). Lo que permite Reduce() es hacer

f(f( \dots f( f( v_1, v_2), v_3 ), \dots ), v_n )

y, como consecuencia,

f(f( \dots f( x ) \dots ) ).

En particular, Reduce() acepta como argumento una función (que, a su vez, acepta dos argumentos) y un vector (además de, posiblemente, argumentos auxiliares adicionales que el interesado encontrará en la ayuda).

El primer ejemplo es simple: vectorizar una función. Tambén es impráctico porque la función que queremos vectorizar ya está vectorizada: max().

f <- function( x, y ) ifelse( x > y, x, y )     # máximo de dos valores
v <- rnorm( 100 )
Reduce( f, v )
max( v )

El segundo tiene que ver con fracciones continuas:

f <- function( x, y ) y + 1 / x
v <- rep( 1, 12 )
Reduce( f, v, accumulate = T )

El interesado puede también probar a definir

f(x, y) = x + 1 / y

y jugar con las opciones right = T/F de Reduce().

El tercer ejemplo puede resultar más interesante. Hay algo de markoviano en la función Reduce() que hace que pueda aplicarse con (discutible, pues es una aproximación) éxito a la simulación de colas. En este caso, simulamos una cola en la que tanto el número de clientes que llegan como el de los que salen en cada intervalo de tiempo (pequeño y fijo) sigue una ley de Poisson. Esto es una aproximación de la situación tal vez más realista en la que ambos fenómenos se rigen por un proceso de Poisson (que puede simularse tal como nos enseña Olivier Núñez en la lista de ayuda de R).

El código es así:

n <- 10000      # n periodos de 1 minuto

lambda1 <- .3   # intensidad de las llegadas
lambda2 <- .4   # intensidad del tiempo de servicio

events <- data.frame(
    entrada = rpois( n, lambda1 ),
    salida  = rpois( n, lambda2 )
    )

# Convertimos el df en una lista por minutos

events <- by( events, 1:n, I )

calcular.longitud.cola <- function( long.cola, delta ){
    max( 0, long.cola + delta$entrada - delta$salida )
}

# hour.events <- Reduce( estado.cola, events, init = 0 )

longitud.cola <- Reduce( calcular.longitud.cola, events, init = 0, accumulate = T )

plot( longitud.cola, type = "l" )

El interesado en profundizar en el estudio de esta función puede ejercitarse con los siguientes ejercicios:

  1. Vectorizar las funciones cbind() y rbind()
  2. Reescribir el código para reescribir el código que ofrecí en la entrada A vueltas con los fractales usando la función Reduce()

3 comentarios sobre “Programación funcional en R: Reduce

  1. Emilio 12 marzo, 2011 9:45

    La anotación matemática señalada ha sido muy útil para entender con claridad el comportamiento de la función Reduce y complementa muy bien la descripción que aparece en ?Reduce ( a left reduce computes l_1 = f(v_1, v_2), l_2 = f(l_1, v_3), etc., and returns l_{n-1} =
    f(l_{n-2}, v_n) )

    ¡Mil gracias!
    Emilio

  2. datanalytics 12 marzo, 2011 9:57

    @Emilio
    Es que ya nadie usa la notación matemática para nada porque nadie sabe matemáticas. Escribo un blog incomprendido para incomprendidos, me temo.

  3. Emilio 20 marzo, 2011 8:32

    Por si puede interesarante, y complementando la explicación de Carlos, aquí hay unos sencillos ejemplos que permiten evitar el bucle for mediante el uso de la función Reduce. Un saludo

    Emilio

    f1 <- function(x, y) x <- x + y
    f2 <- function(x, y) x <- paste(x, y)
    f3 <- function(x, y) x <- c(x, y)

    x <- 1:10
    result <- 0
    for( i in x ) result <- result + i
    result # 55

    result <- 0
    for( i in x ) result <- f1(result, i)
    result # 55

    buclefor <- function( f, x, result = 0) {
    for( i in x ) result <- f( result, i )
    result
    }

    buclefor( f1, x) # 55
    buclefor( f2, x) # "0 1 2 3 4 5 6 7 8 9 10"
    buclefor( f3, x) # 0 1 2 3 4 5 6 7 8 9 10

    Reduce(f1, x) # 55
    Reduce(f2, x) # "1 2 3 4 5 6 7 8 9 10"
    Reduce(f3, x) # 1 2 3 4 5 6 7 8 9 10

Los comentarios están desabilitados.