【问题标题】:How do you construct a flexible function input when creating a function in R?在 R 中创建函数时如何构造灵活的函数输入?
【发布时间】:2018-01-24 23:48:19
【问题描述】:

我的问题不是专门针对 R 包(我不这么认为。如果是这样我道歉),而是在创建函数时如何构造灵活的函数输入。我将使用一个示例,使用 R 包 rootSolve 中名为 multiroot() 的函数(它执行 Newton Raphson 方法来查找根)。

要使 multiroot() 工作,您必须首先创建一个函数,该函数是某个方程组的向量。然后将此函数作为输入传递给函数 multiroot()。

例如:

require(rootSolve)
model <- function(x) c(F1 = x[1]^2+ x[2]^2 -1, F2 = x[1]^2- x[2]^2 +0.5)
ss <- multiroot(f = model, start = c(1, 1))

一切都很好。但是,假设 F1 是动态的,这意味着我的方程式可能会根据我在其他地方收集的数据点的数量而改变。我有代码可以吐出一个方程式。目前,等式是一个字符串。例如eq1 &lt;- "x[1]^2 + x[2]^2 - 1"

所以现在如果我尝试替换其他地方的方程式,它会变成(注意 F1):

 model <- function(x) c(F1 = eq1, F2 = x[1]^2- x[2]^2 +0.5)
 ss <- multiroot(f = model, start = c(1, 1))

我收到错误:stode(y, times, func, parms = parms, ...) 中的错误: REAL() 只能应用于“数字”,不能应用于“字符”

我可以从错误中看到它想要数字,但是eq1 &lt;- as.numeric(eq1) 返回“NA”,所以这显然行不通。 R 不会将所有字符都理解为数字

如果我尝试eq1 &lt;- as.symbol(eq1),我会得到与“列表”而不是“字符”相同的错误。

如果我尝试eq1 &lt;- expression(eq1),我会得到与“表达式”而不是“列表”相同的错误。

也许我的最终问题变成了“我如何使这个等式变成数字?”,但我不这么认为。我不明白为什么 R 认为原始输入是数字。

我绝望地尝试过的其他事情包括只制作一个简单的函数来在包之外进行测试。

#Test with generic function
model2 <- function(x) 2*x[1]- x[2]^2+5
model2(c(2,3))

效果很好!

现在进行替换:

eq2 <- noquote("2*x[1]- x[2]^2+5")
eq2 <- substitute(2*x[1]- x[2]^2+5)
eq2 <- quote(2*x[1]- x[2]^2+5)
eq2 <- expression(2*x[1]- x[2]^2+5)
eq2 <- as.symbol(2*x[1]- x[2]^2+5)

#Test with generic function
model2 <- function(x) eq2
model2(c(2,3))

虽然没有错误,但输出未提供预期结果。相反,它大部分时间都返回2*x[1]- x[2]^2+5。我知道每个都有不同的结构、类等。

所以,我的问题是如何通过输入一个可以在代码中其他地方更改的变量来使函数输入动态化?我希望它与我在示例中使用的包一起使用,但希望我的问题不依赖于包。我的具体问题可能是如何将作为字符串的变量转换为数字,但这对我来说不太有意义。

感谢您提供的任何帮助。

【问题讨论】:

  • 你也可以从组件中构造一个函数,但我不确定它是否是你想要的:f &lt;- function() {}; body(f) &lt;- expression(x[1]^2 + x[2]^2 - 1); formals(f) &lt;- alist(x=) 或一次性完成:as.function(alist(x=, x[1]^2 + x[2]^2 - 1))
  • 我认为这可能是重复的:stackoverflow.com/questions/29421816/…
  • @42 是的,看起来很相似。接得好!我看到你是那个回答的人。在发帖之前我确实搜索了很多。

标签: r


【解决方案1】:

为了说明代码中的问题,我将使用myfunc() 一个用户定义的函数而不是multiroot(),这需要我安装一个新包。

简单数学

2*2-3^2+5
# [1] 0

函数内的数学运算

model2 <- function(x) 2*x[1]- x[2]^2+5  
model2(c(2,3))
# [1] 0

使用方程式的函数内的数学运算

eq2 <- '2*x[1]- x[2]^2+5'      
model2 <- function(x) eval(parse(text = eq2))
model2(c(2,3))    
# [1] 0

函数内的数学 - 另一个使用 myfunc() 而不是 multiroot() 的示例

myfunc <- function(f , start )
{
  do.call(f, args = list(start))
}
model <- function(x) c(F1 = x[1]^2+ x[2]^2 -1, F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
# F1  F2 
# 1.0 0.5

使用方程和myfunc()在函数内部进行数学运算

  • 返回方程
eq1 <- 'x[1]^2+ x[2]^2 -1'
model <- function(x) c(F1 = eq1, F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
#                  F1                  F2 
# "x[1]^2+ x[2]^2 -1"               "0.5"
  • 计算方程后返回值
eq1 <- 'x[1]^2+ x[2]^2 -1'
model <- function(x) c(F1 = eval(parse(text = eq1)), F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
#  F1  F2 
# 1.0 0.5

【讨论】:

  • 我不确定所有 eval 和 parse 的作用,或者我为什么需要 text =,但我会尝试阅读。再次感谢!
【解决方案2】:

您可以将输入视为一个函数,而不是将您的输入视为要转换的字符串:

eq1 <- "x[1]^2 + x[2]^2 - 1" # change this line to:
eq1 <- function(x) { x[1]^2 + x[2]^2 - 1 }

现在eq1 是一个您可以传递给model 的函数,在同一个参数处进行评估:

model <- function(x) c(F1 = eq1(x), F2 = x[1]^2- x[2]^2 +0.5)
ss <- multiroot(f = model, start = c(1, 1))

> ss
$root
[1] 0.5000000 0.8660254

$f.root
          F1           F2 
2.323138e-08 2.323308e-08 

$iter
[1] 5

$estim.precis
[1] 2.323223e-08

【讨论】:

  • @JT 感谢您的快速回复。这几乎是我需要的。稍后我会尝试搞砸。但是在您说“将此行更改为:”的答案中,您是说手动更改吗?这是我想要避免的。自动带我从第 1 行到第 2 行,我是金子。
  • 对,我的意思是从将它写成一个函数开始(即,根本不要写第 1 行)。我想这取决于你的这段代码是如何工作的:“我有代码可以输出一个等式。”如果你能让它吐出一个函数,那么你就完成了。如果不是...?(但其他答案对此有好处!)
  • 是的,下面的答案更符合我的想法。但是,我非常感谢您的努力。
【解决方案3】:

我认为eval 是您所需要的。特别是,对于您的示例,您可以使用:

eq2 <- substitute(2*x[1]- x[2]^2+5)

#Test with generic function
model2 <- function(x) eval(eq2)
model2(c(2,3))

在更一般的意义上,如果你想将表达式作为字符串传递,你可以使用

expre = "2*x[1]- x[2]^2+5"

model2 <- function(expr,x) eval(parse(text=expr)) 
model2(expre,c(2,3)) 

诀窍是在函数内部进行评估,以便从适当的命名空间获得结果。

【讨论】:

    【解决方案4】:

    您不需要使用 eval(parse(.)),而只需使用 parse。它返回一个适合传递body&lt;- 函数的未评估语言对象:

    f1 <- function(x){}
    body(f1) <- parse( text=eq1)
     require(rootSolve)
    Loading required package: rootSolve
    
    model <- function(x) c(F1 = f1(x), F2 = x[1]^2- x[2]^2 +0.5)
    ss <- multiroot(f = model, start = c(1, 1))
    ss
    #--------------
    $root
    [1] 0.5000000 0.8660254
    
    $f.root
              F1           F2 
    2.323138e-08 2.323308e-08 
    
    $iter
    [1] 5
    
    $estim.precis
    [1] 2.323223e-08
    

    parse-step 创建了与“完整”函数构造期间相同的树结构,但将其保留为开放状态,以便稍后提供参数进行进一步评估。

    【讨论】:

    • 这对我不起作用。我仍然收到错误“REAL() 只能应用于‘数字’,而不是‘表达式’”
    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-17
    • 2013-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多