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
我想在a
、n
上申请f1
的次数,例如这里说3 次。
我听说purrr::reduce
或purrr::map()
可能是一个好主意,但无法实现。
如果n = 3
的期望输出将等于f1(f1(f1(a)))
。
【问题讨论】:
你应该让你的函数return
一些东西。
@apitsch,它确实是隐含的。也就是说,如果功能块中的最后一行代码是x^2 + x^3
,这相当于return(x^2 + x^3)
和tmp <- x^2 + x^3 ; return(tmp)
。
@r2evans 感谢您告诉我。很高兴学到新东西。
BTW:@apitsch,这个可以咬你:如果函数做了部分赋值比如mtcars$cyl <- mycars$cyl + 1
,那么返回的值不是mtcars
,而是@ 987654335@,一个常见的错误。您可以看到分配是不可见返回通过将调用包装在括号中分配的值,如(在您的控制台上)(mtcars$cyl <- mycars$cyl + 1)
。出于这个原因,许多人(正如您所建议的那样)认为明确的 return(...)
语句可能是一件好事。
@r2evans return
仅在特殊情况下才需要...在一般情况下,返回函数内计算的最后一个表达式。仅当您想跳出函数并在此之前返回一个值时,您才需要显式的 return
语句。 return
不影响返回值的“可见性/不可见性”:例如,赋值返回已赋值但这样做不可见的值。试试fun <- function(x) return(a<-x)
-- 现在fun(3)==3
等于TRUE
但fun(3)
不会在屏幕上显示任何值。但是fun <- function(x) (a<-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
选项 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
【讨论】:
可以在不保留中间结果的情况下做到这一点。purrr::map
可能吗?
@adl map
将函数分别应用于每个元素,而不是递归地将函数应用于每个元素的输出
它位于magrittr
。谢谢你的回答
你不能用accumulate %>% extract2
代替purrr::reduce
吗?
@r2evans 我知道reduce
在这种情况下更好。我正在考虑获取vector
s 的`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 次的主要内容,如果未能解决你的问题,请参考以下文章