r - 对数据应用函数 n 次

Posted

技术标签:

【中文标题】r - 对数据应用函数 n 次【英文标题】:r - apply a function on data n number of times 【发布时间】:2019-03-28 00:14:22 【问题描述】:

我想每次使用函数的输出在向量上应用相同的函数一定次数。

一个简单的例子,一个简单的功能只是为了演示:

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

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

我想在an 上申请f1 的次数,例如这里说3 次。

我听说purrr::reducepurrr::map() 可能是一个好主意,但无法实现。

如果n = 3 的期望输出将等于f1(f1(f1(a)))

【问题讨论】:

你应该让你的函数return 一些东西。 @apitsch,它确实是隐含的。也就是说,如果功能块中的最后一行代码是x^2 + x^3,这相当于return(x^2 + x^3)tmp &lt;- x^2 + x^3 ; return(tmp) @r2evans 感谢您告诉我。很高兴学到新东西。 BTW:@apitsch,这个可以咬你:如果函数做了部分赋值比如mtcars$cyl &lt;- mycars$cyl + 1,那么返回的值不是mtcars,而是@ 987654335@,一个常见的错误。您可以看到分配是不可见返回通过将调用包装在括号中分配的值,如(在您的控制台上)(mtcars$cyl &lt;- mycars$cyl + 1)。出于这个原因,许多人(正如您所建议的那样)认为明确的 return(...) 语句可能是一件好事。 @r2evans return 仅在特殊情况下才需要...在一般情况下,返回函数内计算的最后一个表达式。仅当您想跳出函数并在此之前返回一个值时,您才需要显式的 return 语句。 return 不影响返回值的“可见性/不可见性”:例如,赋值返回已赋值但这样做不可见的值。试试fun &lt;- function(x) return(a&lt;-x) -- 现在fun(3)==3 等于TRUEfun(3) 不会在屏幕上显示任何值。但是fun &lt;- function(x) (a&lt;-x) 的行为会有所不同。 【参考方案1】:

我们使用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:

使用二元函数连续组合给定向量的元素和可能给定的初始值。

第一个参数是要使用的函数,它应该接受两个参数。第一个是在此归约中previous 函数执行的值。在第一次调用该函数时,它使用提供的 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

【讨论】:

绝对是我要找的东西【参考方案2】:
i <- 1

while (i < 10) 
  i <- i + 1
  x <- f(x)

【讨论】:

【参考方案3】:

这是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 选项 Reduceaccumulate = 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

【讨论】:

可以在不保留中间结果的情况下做到这一点。 purrr::map 可能吗? @adl map 将函数分别应用于每个元素,而不是递归地将函数应用于每个元素的输出 它位于magrittr。谢谢你的回答 你不能用accumulate %&gt;% extract2代替purrr::reduce吗? @r2evans 我知道reduce 在这种情况下更好。我正在考虑获取vectors 的`list`,然后OP可以决定是选择一个元素还是多个【参考方案4】:

这是使用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

【讨论】:

您正在利用向量长度和重复次数相同的事实,我没有从 OP 中假设。 这是另一种方式Reduce(function(a,b) get("f1")(a), a, init = a)

以上是关于r - 对数据应用函数 n 次的主要内容,如果未能解决你的问题,请参考以下文章

如何在 R 中的单个数据帧上迭代地应用函数?

Pyspark - 一次聚合数据框的所有列[重复]

变长df二次采样函数r

R进阶:缺失值的处理、拟合关系

数据结构(主席树):HZOI 2016 采花

是否有用于对数据帧的每一列执行基本操作的 R 函数? [复制]