r - применить функцию к данным n раз - PullRequest
0 голосов
/ 23 октября 2018

Я хотел бы применить одну и ту же функцию определенное количество раз к вектору, используя каждый раз выходные данные функции.

Упрощенный пример с простой функцией, просто для демонстрации:

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

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

Я хотел бы применить f1 к a, n количество раз, например, здесь, скажем, 3 раза.

Я слышал, purrr::reduce или purrr::map() может быть хорошимидея для этого, но не может заставить его работать.

Желаемый результат, если n = 3 будет равен f1(f1(f1(a))).

Ответы [ 4 ]

0 голосов
/ 23 октября 2018

Вот еще один способ сделать это с Reduce:

установкой этапа

a <- 1:3
f1 <- function(x) x^2 + x^3

построением вызова и его оценкой

N <- 3   # how many times?
r <- Reduce(function(a,b) call("f1", a), rep(NA, N), init=a)
# f1(f1(f1(1:3)))
eval(r)
# [1] 1.872000e+03 6.563711e+09 1.102629e+14

альтернатива 2

# N defined as above
Reduce(function(x,y) y(x), replicate(N,f1), init=a)
# [1] 1.872000e+03 6.563711e+09 1.102629e+14

вариант 3 (рекурсивный с глобальной переменной)

doit <- function(N) {
  i <- 0
  function(fun, x){
    i <<- i +1
    if(i < N) Recall(fun, fun(x)) else fun(x)
  }
}
doit(3)(f1, a)
# [1] 1.872000e+03 6.563711e+09 1.102629e+14

... или даже

doit <- function(N, fun, x) (function(fun, x) 
    if((N <<- N - 1) > 0) 
      Recall(fun, fun(x)) else 
        fun(x))(fun, x)
doit(3, f1, a)
# [1] 1.872000e+03 6.563711e+09 1.102629e+14
0 голосов
/ 23 октября 2018

Вот опция с accumulate

library(tidyverse)
n <- 3
a %>% 
  accumulate(~ .x %>%
                 f1, .init = f1(a)) %>%
  extract2(n)
#[1] 1.872000e+03 6.563711e+09 1.102629e+14

ПРИМЕЧАНИЕ: accumulate аналогична опции base R Reduce с accumulate = TRUE

проверкой с помощью OPвывод

f1(f1(f1(a)))
#[1] 1.872000e+03 6.563711e+09 1.102629e+14

Или используйте цикл for (внешние библиотеки не используются)

out <- a
for(i in seq_len(n)) out <- f1(out)
out
#[1] 1.872000e+03 6.563711e+09 1.102629e+14
0 голосов
/ 23 октября 2018

Давайте использовать Reduce (нет требований к внешней библиотеке, как правило, хорошая производительность).Я немного изменю функцию, чтобы принять второй (игнорируемый) аргумент:

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

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

Вот что происходит.Reduce:

использует двоичную функцию для последовательного объединения элементов данного вектора и, возможно, заданного начального значения.

Первый аргумент - это функция, которую нужно использоватьи он должен принять два аргумента.Первым является значение из предыдущего выполнения функции в этом сокращении.При первом вызове функции используется предоставленное значение init=.

  • Первый вызов:

    f1(c(1,2,3), 1)
    # [1]  2 12 36
    
  • Второй вызов:

    f1(c(2,12,36), 2)
    # [1]    12  1872 47952
    
  • Третий вызов:

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

Второй аргумент 1:3 используется только для его длины.Все, что имеет правильную длину, будет работать.

Если вы не хотите переопределять f1 только для этого сокращения, вы всегда можете выполнить

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

Тест:

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
0 голосов
/ 23 октября 2018
i <- 1

while (i < 10) {
  i <- i + 1
  x <- f(x)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...