Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

r - apply a function on data n number of times

I would like to apply the same function certain number of times on a vector using the output from the function every time.

A simplified example with a simple function just to demonstrate:

# sample vector
a <- c(1,2,3)

# function to be applied n times
f1 <- function(x) {
  x^2 + x^3
 }

I would like to apply f1 on a, n number of times, for example here lets say 3 times.

I heard purrr::reduce or purrr::map() might be a good idea for this but couldn't make it work.

The desired output if n = 3 would be equal to f1(f1(f1(a))).

like image 305
adl Avatar asked Dec 17 '22 20:12

adl


2 Answers

Let's use Reduce (no external library requirements, generally good performance). I'll modify the function slightly to accept a second (ignored) argument:

f1 <- function(x, ign) x^2 + x^3

Reduce(f1, 1:3, init = a)
# [1] 1.872000e+03 6.563711e+09 1.102629e+14

Here's what's happening. Reduce:

uses a binary function to successively combine the elements of a given vector and a possibly given initial value.

The first argument is the function to use, and it should accept two arguments. The first is the value from the previous execution of the function in this reduction. On the first call of the function, it uses the init= value provided.

  • First call:

    f1(c(1,2,3), 1)
    # [1]  2 12 36
    
  • Second call:

    f1(c(2,12,36), 2)
    # [1]    12  1872 47952
    
  • Third call:

    f1(c(12,1872,47952), 3)
    # [1] 1.872000e+03 6.563711e+09 1.102629e+14
    

The second argument 1:3 is used just for its length. Anything of the proper length will work.

If you don't want to redefine f1 just for this reduction, you can always do

Reduce(function(a,ign) f1(a), ...)

Benchmark:

library(microbenchmark)
r <- Reduce(function(a,b) call("f1", a), 1:3, init=quote(a))
triple_f1 <- function(a) f1(f1(f1(a)))
microbenchmark::microbenchmark(
  base = Reduce(function(a,ign) f1(a), 1:3, a),
  accum = a %>% accumulate(~ .x %>% f1, .init = f1(a)) %>% extract2(3),
  reduc = purrr::reduce(1:3, function(a,ign) f1(a), .init=a),
  whil = { 
    i <- 1
    a <- c(1,2,3)
      while (i < 10) {
        i <- i + 1
        a <- f1(a)
      }
    },
  forloop = {
    out <- a
    for(i in seq_len(3)) out <- f1(out)
  },
  evaluated = {
    r <- Reduce(function(a,b) call("f1", a), 1:3, init=quote(a))
    eval(r)
  },
  precompiled = eval(r),
  anotherfun = triple_f1(a)
)
# Unit: microseconds
#         expr      min        lq       mean    median        uq      max neval
#         base    5.101    7.3015   18.28691    9.3010   10.8510  848.302   100
#        accum  294.201  328.4015  381.21204  356.1520  402.6510  823.602   100
#        reduc   27.000   38.1005   57.55694   45.2510   54.2005  747.401   100
#         whil 1717.300 1814.3510 1949.03100 1861.8510 1948.9510 2931.001   100
#      forloop 1110.001 1167.1010 1369.87696 1205.5010 1292.6500 9935.501   100
#    evaluated    6.702   10.2505   22.18598   13.3015   15.5510  715.301   100
#  precompiled    2.300    3.2005    4.69090    4.0005    4.5010   26.800   100
#   anotherfun    1.400    2.0515   12.85201    2.5010    3.3505 1017.801   100
like image 63
r2evans Avatar answered Jan 01 '23 17:01

r2evans


i <- 1

while (i < 10) {
  i <- i + 1
  x <- f(x)
}
like image 31
12b345b6b78 Avatar answered Jan 01 '23 18:01

12b345b6b78