2015-09-28 103 views
0

的对象列表说我有两个对象,ab,和R中的函数f1参考for循环

a<- 5 
b<- 10 

f1<-function(){ 
    out<- a+b 
    return(out) 

我想写一个for循环,评估此功能的灵敏度到ab各改变它们,并再次运行函数的值。我想创建对象的载体中,然后运行一些像这样的代码:

params<- c(a,b) 
for(i in params){ 
    store<- i   #save the initial value of the object so I can restore it later. 
    base<-f1()   #save function output with original object value 
    i<- i*1.1   #increase object value by 10% 
    base.10<- f1()  #recalculate and save function output with new object value 
    calc<- base.10/base #generate a response metric 
    i<- store   #reset the object value to its original value 
    return(calc)  
} 

回答

1

这听起来像你有一个函数f1依靠对象ab(未在函数定义),并且你想要测试其输出的灵敏度值为ab。接近这会通过你想要的敏感性分析的值来循环和操作的f1父环境的一种方式,因此使用这些值:

f1 <- function() a + b 
sensitivity <- function(params) { 
    old.f1.env <- environment(f1) 
    grid <- expand.grid(lapply(params, function(x) x * c(1, 1.1))) 
    grid$outcome <- apply(grid, 1, function(x) { 
    for (n in names(x)) { 
     assign(n, x[n]) 
    } 
    environment(f1) <- environment() 
    ret <- f1() 
    environment(f1) <- old.f1.env 
    ret 
    }) 
    grid 
} 
sensitivity(list(a=5, b=10)) 
#  a b outcome 
# 1 5.0 10 15.0 
# 2 5.5 10 15.5 
# 3 5.0 11 16.0 
# 4 5.5 11 16.5 

在这里,我们进行计算的函数值一格的ab值,无论是在原始ab值和在10%的增加的值。

请注意,我们的很多工作都来自于f1父环境指定的变量。我鼓励你重构你的代码,所以你的函数f1将相关参数作为输入。那么你可以使用:

f1 <- function(a, b) a + b 
sensitivity <- function(params) { 
    grid <- expand.grid(lapply(params, function(x) x * c(1, 1.1))) 
    grid$outcome <- apply(grid, 1, function(x) do.call(f1, as.list(x))) 
    grid 
} 
sensitivity(list(a=5, b=10)) 
#  a b outcome 
# 1 5.0 10 15.0 
# 2 5.5 10 15.5 
# 3 5.0 11 16.0 
# 4 5.5 11 16.5 
+0

这将工作,但我真的想要在当前对象值的上下文中定义值的范围。我希望变量a和变量b增加10%>这是因为在我的实际函数中,我有很多不同的变量,它们的值的数量级差别很大,所以我无法为所有的变量指定一组公用的值他们承担。 – colin

+1

@colin好吧,我已经更新了,当我传递给'sensitivity'函数来处理基值和10%以上的基值。 – josliber

+0

这是如此接近。有没有办法给这个命令提供一个对象向量,而不是每次添加/删除参数都改变函数?我希望能够将此代码插入到我运行的其他模型中并将其播放,并为它提供一个我想估计其灵敏度的参数矢量,并且这些参数将始终设置为R对象。 – colin

1

这听起来像是一个完美的封闭用例。

get_f1 <- function(a, b) { 
    f1<-function(){ 
     out<- a+b 
     return(out) 
    } 
    return(f1) 
} 

然后:

my_f1 <- get_f1(a=5, b=10) 
my_f1() #uses a=5 and b=10 because they are defined in the envir associated with my_f1 

所以在你的循环,你可以简单地做:

base <- (get_f1(a, b))() 
base.10 <- (get_f1(a*1.1, b*1.1))()  

很明显,你可以带参数i=c(a, b)定义get_f1

使用闭包(连接到环境功能),而不是与环境摆弄!

TL;博士:闭包是真棒

1

读你的一些意见,我觉得这其实是你想要什么:sensitivity需要的功能和参数列表和返回功能,它的参数的敏感性。(顺便说一句,你叫什么敏感性,已经意味着something else

sensitivity <- function(fun, args) { 
    out <- lapply(names(args), function(cur) { 
     base10 <- do.call(fun, `[[<-`(args, cur, `[[`(args,cur)*1.1)) 
     base10/do.call(fun, args) 
    }) 
    names(out) <- names(args) 
    return(out) 
} 

例子:

f1 <- function(a,b) a+b 
a1 <- list(a=5, b=2) 
sensitivity(f1, a1) 

这给

$a 
[1] 1.03 

$b 
[1] 1.07 

例2:

f2 <- function(x, y, z) x^2 +3*y*z 
sensitivity(f2, list(x=1, y=2, z=3)) 


$x 
[1] 1.011053 

$y 
[1] 1.094737 

$z 
[1] 1.094737 

它的工作原理“插件并且玩“wi任何函数,但它需要你以不同的方式定义f(人们会说,正确)。我写可能写一些可以与你的函数一起工作的东西,因为它是写的,但它会有很多工作和不好的口味。如果你想要的代码模块,你就不能使用副作用...

PS:如果你希望有一个向量返回而不是一个列表,只需在sensitivity清晰度改变lapplysapply

这将给出最后的示例:

> sensitivity(f2, list(x=1, y=2, z=3)) 
     x  y  z 
1.011053 1.094737 1.094737 

PPS:任何理由,你为什么不计算f的梯度,而不是做你在做什么?

+0

你能解释更多关于为什么我的函数写得“不正确”,以及为什么你指定它是正确的? – colin

+0

如果你的函数使用a和b的方式和你的例子一样简单,我不明白不传递a和b作为f的参数有什么好处,特别是考虑到使用R进行作用域可能是非常不直观的。我必须说,我发现,在不同的时间以相同的论点调用f可能会产生不同的结果,这一事实尤其没有吸引力。没有很好的理由,这很容易出错。这使得很难简单的事情,如你想做的事情。这不是不正确的,但我认为它是次等的。 –

+0

如果你想执行;对'x'和'y',你现在必须做'a <-x'和'b <-y',然后'f()',而不是'f(x,y)'。你不觉得它很丑吗?公平地说,我经常在我的函数中使用外部变量,但仅仅因为它们实际上是固定的值或我很少改变的值,因此几乎没有“参数”。鉴于您正在研究f对a和b的敏感性,我只能假设a和b是f的重要参数,因此应该为了方便和模块化而明确传递。 –