参考for循环
的对象列表说我有两个对象,a
和b
,和R中的函数f1
参考for循环
a<- 5
b<- 10
f1<-function(){
out<- a+b
return(out)
我想写一个for循环,评估此功能的灵敏度到a
和b
各改变它们,并再次运行函数的值。我想创建对象的载体中,然后运行一些像这样的代码:
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)
}
这听起来像你有一个函数f1
依靠对象a
和b
(未在函数定义),并且你想要测试其输出的灵敏度值为a
和b
。接近这会通过你想要的敏感性分析的值来循环和操作的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
在这里,我们进行计算的函数值一格的a
和b
值,无论是在原始a
和b
值和在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
这听起来像是一个完美的封闭用例。
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;博士:闭包是真棒
读你的一些意见,我觉得这其实是你想要什么: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
清晰度改变lapply
到sapply
。
这将给出最后的示例:
> sensitivity(f2, list(x=1, y=2, z=3))
x y z
1.011053 1.094737 1.094737
PPS:任何理由,你为什么不计算f的梯度,而不是做你在做什么?
你能解释更多关于为什么我的函数写得“不正确”,以及为什么你指定它是正确的? – colin
如果你的函数使用a和b的方式和你的例子一样简单,我不明白不传递a和b作为f的参数有什么好处,特别是考虑到使用R进行作用域可能是非常不直观的。我必须说,我发现,在不同的时间以相同的论点调用f可能会产生不同的结果,这一事实尤其没有吸引力。没有很好的理由,这很容易出错。这使得很难简单的事情,如你想做的事情。这不是不正确的,但我认为它是次等的。 –
如果你想执行;对'x'和'y',你现在必须做'a
这将工作,但我真的想要在当前对象值的上下文中定义值的范围。我希望变量a和变量b增加10%>这是因为在我的实际函数中,我有很多不同的变量,它们的值的数量级差别很大,所以我无法为所有的变量指定一组公用的值他们承担。 – colin
@colin好吧,我已经更新了,当我传递给'sensitivity'函数来处理基值和10%以上的基值。 – josliber
这是如此接近。有没有办法给这个命令提供一个对象向量,而不是每次添加/删除参数都改变函数?我希望能够将此代码插入到我运行的其他模型中并将其播放,并为它提供一个我想估计其灵敏度的参数矢量,并且这些参数将始终设置为R对象。 – colin