在R中调用环境计算是否昂贵?

时间:2016-07-22 18:05:18

标签: r user-defined-functions

我有一个主用户定义的函数,有很多输入。主函数调用一个用户定义的函数,该函数调用另一个函数,然后每次调用另一个函数,依此类推,每次使用较小的输入子集。我想出了两种将输入传递给低级函数的方法:

  1. 手动,
  2. 让每个较低级别的功能从主功能获取输入。
  3. 解决方案1)正在打字密集,我怀疑不是一个更有经验的程序员会做什么。解决方案2)似乎更整洁,但运行需要更长的时间。所以我有两个问题:A)为什么解决方案2)需要更多时间? B)是否有比这些更好的解决方案减少程序员的手工工作并且计算效率高?在我的生物学研究以及编码统计方法方面,这种编程方案已经出现,所以我认为这是其他人已经解决的常见问题。

    我在下面列出了两个解决方案的简单示例(添加5个数字)以及时间。

    # Solution 1)
    f0 <- function(a0,a1,a2,a3,a4){
      val <- a0 + f1(a1=a1,a2=a2,a3=a3,a4=a4)
      return(val)
    }
    
    f1 <- function(a1,a2,a3,a4){
      val <- a1 + f2(a2=a2,a3=a3,a4=a4)
      return(val)
    }
    
    f2 <- function(a2,a3,a4){
      val <- a2 + f3(a3=a3,a4=a4)
      return(val)
    }
    
    f3 <- function(a3,a4){
      val <- a3 + f4(a4=a4)
      return(val)
    }
    
    f4 <- function(a4){
      val <- a4
      return(val)
    }
    
    # Solution 2)
    
    g0 <- function(a0,a1,a2,a3,a4){
      vars <- list('a0','a1','a2','a3','a4')
      env <<- environment()
      val <- a0 + g1()
      return(val)
    }
    
    g1 <- function(){
      for (i in get('vars',env)){assign(i,get(i,env),environment())}
      val <- a1 + g2()
      return(val)
    }
    
    g2 <- function(){
      for (i in get('vars',env)){assign(i,get(i,env),environment())}
      val <- a2 + g3()
      return(val)
    }
    
    g3 <- function(){
      for (i in get('vars',env)){assign(i,get(i,env),environment())}
      val <- a3 + g4()
      return(val)
    }
    
    g4 <- function(){
      for (i in get('vars',env)){assign(i,get(i,env),environment())}
      val <- a4
      return(val)
    }
    
    # Timing
    t0 <- Sys.time()
    replicate(1e4, f0(1,2,3,4,5))
    t1 <- Sys.time()
    
    tt0 <- Sys.time()
    replicate(1e4, g0(1,2,3,4,5))
    tt1 <- Sys.time()
    
    # Time: Solution 1)
    > t1-t0
    Time difference of 0.2921922 secs
    
    # Time: Solution 2)
    > tt1-tt0
    Time difference of 0.953675 secs
    

3 个答案:

答案 0 :(得分:2)

您可以传递一个名为list(),甚至可以根据列表创建自己的类。这或多或少是大多数模型在R中的工作方式:lm对象是一个很大的列表,并且有很多函数(predictsummarycoefAICplot等)使用对象的任何部分。

# Solution 4)
h0 <- function(arg_list){
 arg_list$a0 + h1(arg_list)
}

h1 <- function(arg_list){
  arg_list$a1 + h2(arg_list)
}

h2 <- function(arg_list){
  arg_list$a2 + h3(arg_list)
}

h3 <- function(arg_list) {
  arg_list$a3 + h4(arg_list)
}

h4 <- function(arg_list) {
  arg_list$a4
}


h0(list(a0 = 1, a1 = 2, a2 = 3, a3 = 4, a4 = 5))
# [1] 15

这样做的好处是您不必过多担心确切的依赖关系。如果h2调用h3并且您编辑h3,请使用列表的另一部分,您不必编辑h2来传递正确的参数,因为您是传递整个物体。

想象一下,如果你必须使用摘要使用的模型片段而不是其他任何内容来调用summary.lm,而不是summary(my_model)你有summary(rank = my_model$rank, resid = my_model$residuals, df_resid = my_model$df.residuals, w = my_mod$weights, ...),那会有多烦人并且为模型的一半或更多元素打开和继续!

答案 1 :(得分:2)

使用...将参数传递给后续函数:

f0 <- function(a0, ...){
  val <- a0 + f1(...)
  return(val)
}

f1 <- function(a1, ...){
  val <- a1 + f2(...)
  return(val)
}

f2 <- function(a2, ...){
  val <- a2 + f3(...)
  return(val)
}

f3 <- function(a3, ...){
  val <- a3 + f4(...)
  return(val)
}

f4 <- function(a4){
  val <- a4
  return(val)
}

f0(1,2,3,4,5)
#[1] 15

关于A):每个函数调用都需要时间。我认为assign尤其不是很快。

答案 2 :(得分:0)

通常,解决方案1可能更容易维护,因为每个功能都清楚它应该做什么。将来理解您的代码所需的时间会更少。

广泛的“最佳”解决方案很难;在这种情况下,它只是a1 + a2 + a3 + a4 + a5,但实际功能是什么以及它们如何相互作用将极大地影响您的解决方案。

至于为什么解决方案2需要这么长时间,它不仅仅是在环境中查找变量,而是在进行大量的获取和分配。

我也不认为该功能正在按照您的想法进行,因为vars未存储在环境env中。

您可以考虑将变量存储在全局环境中,如下所示:

a1 <- 1
a2 <- 2
a3 <- 4
a4 <- 8


h1 <- function(){
  a1 + h2()
}

h2 <- function(){
  a2 + h3()
}

h3 <- function(){
  a3 + h4()
}

h4 <- function(){
  a4
}

h1()