【问题标题】:How to apply succesively the same change to each argument of a function?如何对函数的每个参数连续应用相同的更改?
【发布时间】:2021-04-30 15:21:00
【问题描述】:

我试图弄清楚如何对我开发的模型执行弹性分析。 为此,我打算对模型的每个参数应用 10% 并测量输出的变化。 我写了一个简单的函数,包括所有参数作为参数。

我的挑战是我不知道如何使用 apply 函数或循环将这个过程自动化到所有参数。

我们以一个伪函数为例:

model <- function(a=1, b=1, c=1){
   result <- a+(10*b)+(100*c)
   return(result)
   }

我想对每个参数应用相同的更改(+10% 的值)并为每次迭代生成一次运行。 例如,如果我必须手动完成,它会是这样的:

output_a <- model(a=1.1)
> 111.1

output_b <- model(b=1.1)
> 111

output_c <- model(c=1.1)
> 121

理想情况下,所有输出都将位于同一个数据帧中,以便将来轻松处理它们。 对此有什么想法吗?也许我找不到现有的帖子?

【问题讨论】:

    标签: r function arguments iteration apply


    【解决方案1】:

    更改参数名称有点麻烦,但您可以使用do.call() 语法来构建命名列表。您还可以使用formalArgs() 来获取函数参数的名称

    所以你可以做类似的事情

    lapply(formalArgs(model), function(arg) {
      do.call("model", setNames(list(1.1), arg))
    })
    

    您还可以使用更简洁的方法来处理 rlangpurrr

    library(rlang)
    library(purrr)
    formalArgs(model) %>% 
      map(~exec(model, !!.x := 1.1))
    

    【讨论】:

    • 对不起,我可能不是很清楚。我不需要更改参数名称,而是使用不同的输入值运行函数。
    【解决方案2】:

    创建一个包含所需值的网格,将其子集化为所需值,然后在其上或在 (3) 中直接生成 Mapmapply

    v <- c(1, 1.1)
    
    # either of these would be ok
    g <- expand.grid(a = v, b = v,  c = v)
    # g <- do.call("expand.grid", Map(function(x) v, c("a", "b", "c")))
    
    g <- g[rowSums(g != 1) == 1, ]
    
    # either of these would be ok
    transform(g, result = mapply("model", g$a, g$b, g$c))
    # transform(g, result = do.call("mapply", c("model", g)))
    

    给予:

        a   b   c result
    2 1.1 1.0 1.0  111.1
    3 1.0 1.1 1.0  112.0
    5 1.0 1.0 1.1  121.0
    

    2) 还有一些列表理解包(comprehenr、eList、listcompr)可以使用:

    library(listcompr)
    gen.data.frame(data.frame(a, b, c, result = model(a, b, c)), 
      a = v, b = v, c = v, (a != 1) + (b != 1) + (c != 1) == 1)
    

    给予:

        a   b   c result
    1 1.1 1.0 1.0  111.1
    2 1.0 1.1 1.0  112.0
    3 1.0 1.0 1.1  121.0
    

    3) diag 我们可以直接使用diag创建g矩阵:

    g <- diag(.1, 3) + 1
    colnames(g) <- c("a", "b", "c")
    cbind(g, result = apply(g, 1, function(x) model(x[1], x[2], x[3])))
    

    给出这个矩阵:

           a   b   c result
    [1,] 1.1 1.0 1.0  111.1
    [2,] 1.0 1.1 1.0  112.0
    [3,] 1.0 1.0 1.1  121.0
    

    【讨论】:

    • 感谢您的智能解决方案!我想知道是否有一种方法可以仅从单个更改中获取结果,而不是一次将更改应用于多个参数。在您的输出中,这将仅对应于第 2、3 和 5 行。
    • 已将约束 (a != 1) + (b != 1) + (c != 1) == 1 添加到前两个解决方案中,以将每行中的更改数量限制为 1,并且添加了第三种解决方案,直接生成网格矩阵。
    • 非常感谢!为网格子集添加约束确实是限制更改数量的一种非常聪明的方法!
    • 现在,更进一步,有一个很长的参数列表,它们都有非常不同的数值(默认不是 1,但是对于 a=2,b=7590 , c=42 等)。当它们不相等时,有没有办法将更改应用于每个参数?我会尝试自己找到解决方案...?
    • 将 g 的第一列乘以 2,第二列乘以 7590,第三列乘以 42:g2 &lt;- transform(g, a = 2 * a, b = 7590 * b, c = 42 * c)g2 &lt;- replace(g, TRUE, Map("*", g, c(2, 7590, 42))),现在使用:g2[rowSums(g != 1) == 1, ]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多