do.call基于前一个sapply(seq_along())迭代的输出

时间:2017-04-12 11:37:45

标签: r sapply do.call

我有一些算法的命名列表,可能看起来像这样

> algorithm
$rBinarize
$rBinarize$x
[1] 40

并且可以包含任意数量的附加算法。每个算法对类栅格的空间对象(spObj)执行操作,并返回修改后的栅格。然后我想使用do.call将父函数中的这个(以及所有其他)算法应用于原始输入。然而,我还想要实现的是定义算法的顺序应用,即在先前算法的输出上。我提出了以下代码,但我很好奇有关提高性能的其他建议,因为它是可能的。

if(sequential){
  for(k in seq_along(algorithm)){
    if(length(algorithm[[k]])==0){
      args <- c(spObj = spObj)
    } else{
      args <- c(algorithm[[k]], spObj = spObj)
    }
    spObj <- do.call(what = names(algorithm)[k], args = args)
  }
} else{
  algorithm2 <- lapply(algorithm, function(x) x <- c(x, spObj = spObj))
  modified <- sapply(seq_along(algorithm2), function(j) do.call(what = names(algorithm2)[[j]], args = algorithm2[[j]]))
}

是否可以使用某种apply()构造而不是for - 循环?我不确定我是否完全不理解apply / do.call的逻辑,或者在R中实际上是不可能的。

我修改为for循环以使其与Davids建议相当,并在其上运行微基准测试:

microbenchmark(a = for(k in seq_along(alg)){
  if(length(alg[[k]][-1])==0){
    args <- c(spObj = spObj)
  } else{
    args <- c(alg[[k]][-1], spObj = spObj)
  }
  spObj <- do.call(what = alg[[k]]$algorithm, args = args)
},
b = Reduce(f = function(x, y) do.call(what = y$algorithm, args = c(list(x), y[-1])),
       x = alg,
       init = spObj))

导致了

Unit: milliseconds
 expr      min       lq     mean   median       uq      max neval
    a 33.36777 35.22067 39.60699 36.79661 40.75072 152.0171   100
    b 33.35236 35.39173 40.32860 37.51993 40.25102 154.0441   100

这是其中一个例子,其中for循环实际上并不比任何其他解决方案慢吗?

1 个答案:

答案 0 :(得分:1)

您可以使用内置的Reduce功能。

举个简单的例子,假设你的链看起来像这样:

algorithms <- list(list(func = "sin"),
                   list(func = "cos"),
                   list(func = "log", base = 2))

您希望将此等效应用于log(cos(sin(x)), 2)。请注意,我已将您的输入结构更改为在每个列表中都有一个名为func的项目,而不是名称。

然后您可以将这些应用于:

Reduce(function(x, y) do.call(y$func, c(list(x), y[-1])),
       algorithms,
       init = spObj)

例如,如果spObj10开头,则会产生-0.2249337,您会注意到它与log(cos(sin(spObj)), 2)相同。

根据您使用的功能,您可能需要根据您的使用情况稍微调整一下,但Reduce通常是您正在寻找的。