【问题标题】:R: sequentially applying an arbitrarty list of functions with arguments to a matrixR:顺序将带有参数的任意函数列表应用于矩阵
【发布时间】:2014-06-22 12:48:37
【问题描述】:

我有一个过滤函数列表f1,f2,f3,f4,....,它采用矩阵m 和许多选项作为输入,并返回矩阵行的子集作为输出。现在我希望能够以有序的方式定义一些元过滤功能设置metaf1, metaf2, metaf3,...,这将指定指定 nr 过滤功能的顺序应用,例如首先是f2,然后是f3,分别使用给定的选项。我想将这些过滤设置存储在说类"metafiltering" 的列表中,然后让另一个函数应用给定metafiltering 对象中指定的过滤步骤。我的想法将能够以这种方式允许以有序的方式存储和应用过滤设置。我将如何以 R 中最优雅的方式实现这一目标?或者是否有其他方便的方法来实现这样的目标?

编辑:举个例子,说我有矩阵

m=replicate(10, rnorm(20)) 

和过滤功能(这些只是示例,显然我的更复杂:-))

f1=function(m,opt1,opt2) { 
return(m[(m[,2]>opt1)&(m[,1]>opt2),])
}
f2=function(m,opt1) { 
return(m[(m[,3]>opt1),])
}

我已经定义了特定类的以下metafiltering 设置,这些设置将指定两个必须按顺序应用于矩阵 m 的函数

metafilterfuncs=list(fun1=f1(opt1=0.1,opt2=0.2),fun2=f2(opt1=0.5))
class("metafilterfuncs")="metafiltering"

然后我的问题是如何使用指定的函数和设置将任意metafiltering 函数对象的过滤步骤应用于给定矩阵 m?

【问题讨论】:

  • 一个可重现的例子会改善你的问题。
  • @Roland 我刚刚说得更具体了 - 希望这有助于理解我的问题?

标签: r lapply


【解决方案1】:

你可以这样做:

您定义了一种功能流水线,您可以在其中为每个功能指定优先级。

pipelines <- c(f1=100,f2=300,f3=200)

我在这里定义了 3 个虚拟函数进行测试:

f1 <- function(m,a) m + a
f2 <- function(m,b) m + b
f3 <- function(m,c) m + c

对于每个函数,您将参数存储在另一个列表中:

args <- list(f1=c(a=1),f2=c(b=2),f3=c(c=3))

然后你应用你的功能:

m <- matrix(1:2,ncol=2)
for (func in names(pipelines[order(pipelines)]))
{
  m <- do.call(func,list(m,args[[func]]))
}

【讨论】:

  • 非常感谢这个——太棒了!这是我不知道的 do.call 位 - 感谢您的帮助!
  • 是否也有可能直接在管道向量中传递函数的参数,如管道
【解决方案2】:

pryr 有一个函数,compose,就像你需要的一样,但它并不能完全满足它。 compose 函数要求函数一个一个地给出,而不是在一个列表中,并且它不能接受参数。它也奇怪地放在那个包里。在plyr中可以找到类似的函数,即each。但是这个函数不是顺序应用函数,而是单独应用函数并输出一个命名向量(列表?)。

agstudy 在上面提供了一个解决方案,但它遇到了一个问题:它只能接受标量参数,因为它在命名向量中给出参数。解决方案是改用命名列表。所以,这里有一个改进的函数来替换 pryr 中的那个。

compose2 = function(x, funcs, args, msg_intermediate = F) {
  if (length(funcs) != length(args)) stop("length of functions and arguments must match")
  for (i in seq_along(funcs)) {
    x = do.call(what = funcs[[i]], args = c(x, args[[i]]))
    if ((i != length(funcs)) && msg_intermediate) message(x)
  }
  x
}

msg_intermediate 是一个很好的调试参数,它可以向中间结果发送消息,因此可以更容易地理解发生了什么。

测试一下:

adder = function(x, n) x + n
compose2(0,
         funcs = list(adder, adder, adder),
         args = list(list(n = 1), list(n = 2), list(n = 3)),
         msg_intermediate = T
         )

输出:

1
3
[1] 6

这就是当你取 0,然后加 1 (=1),然后加 2 (=3),然后加 3 (=6) 时得到的结果。

compose2 的 args 参数采用列表列表,因此可以提供非标量函数参数。这是一个例子:

add_div = function(x, n, d) (x + n) / d
compose2(0,
         funcs = list(add_div, add_div, add_div),
         args = list(list(n = 1, d = 1), list(n = 2, d = 2), list(n = 3, d = 3)),
         msg_intermediate = T
)

输出:

1
1.5
[1] 1.5

当你取 0,加 1,除以 1 (=1),然后取 1,加 2 然后除以 2 (=1.5),然后取 1.5,加 3,然后除以 3,你会得到什么(=1.5)。

【讨论】:

  • 我注意到这个解决方案相对于pryr::compose 的一个缺点。此函数将函数应用于对象,而不是返回组合函数。这在某些情况下是一个问题。它可以修复,但我现在没有时间。也许有人可以提供改进的解决方案。 :)
猜你喜欢
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 2018-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多