估计命令如何在 R 的公式中查找变量名称?
How do estimation commands find variable names in formulas in R?
我想在用户定义的函数上使用 R 的 nls()
函数来估计大量模型。由于许多变量在我的规范中是固定的,我想要一种在我的函数中预先设置它们的方法,但我没有正确理解 R 如何在公式中包含的函数中查找变量。
我看过 Hadley Wickham 的高级 R 书中关于元编程的部分,但它并没有启发我。这是我使用 mtcars
数据集尝试实现的简化示例:
我尝试为跨规范固定的变量设置默认值:
expo <- function(x, theta, weight = wt) {
x*weight^theta
}
我也试过只使用固定变量的列名作为函数内部的变量
expo <- function(x, theta) {
x*wt^theta
}
如果我只想计算函数,这两种方法都有效,比如
attach(mtcars)
expo(qsec, 1)
detach()
但是如果我尝试在估算例程中使用我的 expo()
函数,例如
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
失败并显示消息 Error in expo(qsec, theta) : object 'wt' not found
。评论中提出的一种可能性是简单地将数据集 mtcars
作为参数传递给 expo()
。但是由于我只会在对 nls()
的调用内部调用 expo()
,其中数据集已经是一个参数,所以如果我能找到避免这种重复的方法,我会很高兴。
我在适当地定义或调用 expo()
之后的最终目标是能够做这样的事情:
depvars <- c('qsec', 'drat', 'dist')
lapply <- (depvars, function(x) {
formula <- as.formula(paste0('mpg ~ phi + expo(', x, ', theta)'))
nls(formula,
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
}
棘手的是 R 的词法范围搜索在 封闭 环境中,
这在调用过程中可能会造成混淆,因为调用者环境中的每一个都有封闭的环境,事情很快就会变得混乱。
我将使用 rlang
包来调试这个场景。
首先,如果您在全局环境中定义了 expo
,
那么这将是它的封闭环境:
expo <- function(x, theta) {
x*wt^theta
}
rlang::get_env(expo)
# <environment: R_GlobalEnv>
所以当你调用它的时候,R会先在函数的调用中搜索变量
(不是来电者!)
环境,
然后在封闭环境中(这里是全局环境)。
我不知道 nls
到底做了什么,
但我会假设它会根据您提供的 data
创建一个环境并在那里评估公式。
但是,它创建的环境似乎只包含它可以在公式中明确看到的变量,
我发现的东西:
expo <- function(x, theta) {
cat("caller: ")
print(ls(rlang::caller_env()))
cat("enclosing: ")
print(ls(rlang::env_parent(rlang::current_env())))
}
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
# caller: [1] "mpg" "phi" "qsec" "theta"
# enclosing: [1] "expo"
# Error ...
我们可以看到,expo
的caller环境包含了我们可以在公式中识别的变量,
其 enclosing 环境仅包含 expo
的定义
(全球环境)。
不幸的是,这意味着你甚至不能在 expo
中使用像 eval.parent
这样的东西,
因为该环境不会包含来自 data
.
的所有变量
如果您仍想解决它,
您可以在调用 nls
之前用您的数据修改 expo
的封闭环境,
类似于:
expo <- function(x, theta) {
x*wt^theta
}
environment(expo) <- list2env(as.list(mtcars))
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
# Error ... number of iterations exceeded maximum of 50
我接受了 Alexis 的回答,因为它解决了我原来的问题。尽管如此,我还是认为我会分享我采用的解决方案,以防有人觉得它有用。
正如 Alexis 所说,解决方案需要涉及修改 expo()
的封闭环境。我的方法不是每次都手动执行此操作(并且可能在每次调用 expo()
后将其更改回原始环境),而是将 expo()
的环境包含正确变量的要求与 NelsonGon 的建议相结合我在某个时候将数据集作为参数提供。为此,我创建了一个函数工厂 make_expo()
,它设置了所需的变量和 returns expo()
,以便变量自动位于 expo()
的封闭环境中:
make_expo <- function(df, vars = c('wt')) {
wt <- df[[vars[1]]]
function(x, theta) {
x + wt^theta
}
}
expo <- make_expo(mtcars)
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = 1, theta = 1))
# Error ... number of iterations exceeded maximum of 50
我认为这有两个好处。首先,它更健壮,因为你不需要记住设置 expo()
的环境,它会在定义 expo()
时自动设置。然而 make_expo()
是灵活的——我可以设置默认值,或输入不同的数据集。其次,它将参数 expo()
要求降低到我实际期望在对 expo()
的不同调用中变化的参数,提高了可理解性
我很惊讶地得知公式创建了一个环境,在这个环境中查找名称只包含在公式中明确命名的变量,而不是传递给 nls()
的数据集中的其他变量,但是有你去吧。
我想在用户定义的函数上使用 R 的 nls()
函数来估计大量模型。由于许多变量在我的规范中是固定的,我想要一种在我的函数中预先设置它们的方法,但我没有正确理解 R 如何在公式中包含的函数中查找变量。
我看过 Hadley Wickham 的高级 R 书中关于元编程的部分,但它并没有启发我。这是我使用 mtcars
数据集尝试实现的简化示例:
我尝试为跨规范固定的变量设置默认值:
expo <- function(x, theta, weight = wt) {
x*weight^theta
}
我也试过只使用固定变量的列名作为函数内部的变量
expo <- function(x, theta) {
x*wt^theta
}
如果我只想计算函数,这两种方法都有效,比如
attach(mtcars)
expo(qsec, 1)
detach()
但是如果我尝试在估算例程中使用我的 expo()
函数,例如
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
失败并显示消息 Error in expo(qsec, theta) : object 'wt' not found
。评论中提出的一种可能性是简单地将数据集 mtcars
作为参数传递给 expo()
。但是由于我只会在对 nls()
的调用内部调用 expo()
,其中数据集已经是一个参数,所以如果我能找到避免这种重复的方法,我会很高兴。
我在适当地定义或调用 expo()
之后的最终目标是能够做这样的事情:
depvars <- c('qsec', 'drat', 'dist')
lapply <- (depvars, function(x) {
formula <- as.formula(paste0('mpg ~ phi + expo(', x, ', theta)'))
nls(formula,
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
}
棘手的是 R 的词法范围搜索在 封闭 环境中, 这在调用过程中可能会造成混淆,因为调用者环境中的每一个都有封闭的环境,事情很快就会变得混乱。
我将使用 rlang
包来调试这个场景。
首先,如果您在全局环境中定义了 expo
,
那么这将是它的封闭环境:
expo <- function(x, theta) {
x*wt^theta
}
rlang::get_env(expo)
# <environment: R_GlobalEnv>
所以当你调用它的时候,R会先在函数的调用中搜索变量 (不是来电者!) 环境, 然后在封闭环境中(这里是全局环境)。
我不知道 nls
到底做了什么,
但我会假设它会根据您提供的 data
创建一个环境并在那里评估公式。
但是,它创建的环境似乎只包含它可以在公式中明确看到的变量,
我发现的东西:
expo <- function(x, theta) {
cat("caller: ")
print(ls(rlang::caller_env()))
cat("enclosing: ")
print(ls(rlang::env_parent(rlang::current_env())))
}
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
# caller: [1] "mpg" "phi" "qsec" "theta"
# enclosing: [1] "expo"
# Error ...
我们可以看到,expo
的caller环境包含了我们可以在公式中识别的变量,
其 enclosing 环境仅包含 expo
的定义
(全球环境)。
不幸的是,这意味着你甚至不能在 expo
中使用像 eval.parent
这样的东西,
因为该环境不会包含来自 data
.
如果您仍想解决它,
您可以在调用 nls
之前用您的数据修改 expo
的封闭环境,
类似于:
expo <- function(x, theta) {
x*wt^theta
}
environment(expo) <- list2env(as.list(mtcars))
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = -2, 'theta' = 1))
# Error ... number of iterations exceeded maximum of 50
我接受了 Alexis 的回答,因为它解决了我原来的问题。尽管如此,我还是认为我会分享我采用的解决方案,以防有人觉得它有用。
正如 Alexis 所说,解决方案需要涉及修改 expo()
的封闭环境。我的方法不是每次都手动执行此操作(并且可能在每次调用 expo()
后将其更改回原始环境),而是将 expo()
的环境包含正确变量的要求与 NelsonGon 的建议相结合我在某个时候将数据集作为参数提供。为此,我创建了一个函数工厂 make_expo()
,它设置了所需的变量和 returns expo()
,以便变量自动位于 expo()
的封闭环境中:
make_expo <- function(df, vars = c('wt')) {
wt <- df[[vars[1]]]
function(x, theta) {
x + wt^theta
}
}
expo <- make_expo(mtcars)
nls(mpg ~ phi + expo(qsec, theta),
data = mtcars,
start = c('phi' = 1, theta = 1))
# Error ... number of iterations exceeded maximum of 50
我认为这有两个好处。首先,它更健壮,因为你不需要记住设置 expo()
的环境,它会在定义 expo()
时自动设置。然而 make_expo()
是灵活的——我可以设置默认值,或输入不同的数据集。其次,它将参数 expo()
要求降低到我实际期望在对 expo()
的不同调用中变化的参数,提高了可理解性
我很惊讶地得知公式创建了一个环境,在这个环境中查找名称只包含在公式中明确命名的变量,而不是传递给 nls()
的数据集中的其他变量,但是有你去吧。