在R中使用数据掩码计算最大似然表达式
我试图使用数据掩码计算最大似然表达式。其思想是允许在函数中按名称调用参数和变量,同时避免多次调用在R中使用数据掩码计算最大似然表达式,r,eval,tidyeval,R,Eval,Tidyeval,我试图使用数据掩码计算最大似然表达式。其思想是允许在函数中按名称调用参数和变量,同时避免多次调用attach()和detach()。这是一个非常简单的小示例,实际函数非常大和复杂 set.seed(1) # Data db <- data.frame( x = runif(10), y = runif(10), z = sample(c(0, 1), 10, replace = TRUE) ) # Log likelihood function ll_lik <- f
attach()
和detach()
。这是一个非常简单的小示例,实际函数非常大和复杂
set.seed(1)
# Data
db <- data.frame(
x = runif(10),
y = runif(10),
z = sample(c(0, 1), 10, replace = TRUE)
)
# Log likelihood function
ll_lik <- function(param) {
pr_1 <- 1 / (1 + exp(-(param[1]*x - param[2]*y)))
pr_2 <- 1 - pr_1
lik <- z * pr_1 + (1 - z) * pr_2
log(lik)
}
# Parameters
param <- c(p1 = 0.1, p2 = 0.2)
# Run the model with attach()/detach()
attach(db)
model <- maxLik::maxLik(ll_lik, start = param)
detach(db)
summary(model)
它无法访问数据掩码中的对象(fnOrig(θ,…)中的错误:未找到对象“x”
)。也许问题出在maxLik
,但我甚至无法评估ll_-lik()
,这会产生相同的错误:
eval_tidy(quo(ll_lik(param)), mask)
但这是可行的:
eval_tidy(quo(x*3), mask)
因此,我开始怀疑ll_lik()
具有“错误”的父级,这就是为什么我的数据掩码可能不在函数的搜索路径中,因此它无法找到变量。现在,as_data_mask()的帮助部分提供了一些示例,说明如何通过创建顶级、中级和底层环境来“嵌套”环境。好的,让我们看看我是否可以创建我的函数作为数据掩码结构的一部分:
call_stack <- function() {lobstr::cst()}
# Create a new environment (child of empty) that takes a list of objects to populate it
top <- new_environment(list(ll_lik = ll_lik, call_stack = call_stack))
# Create a child of the "top" environment"
middle <- env(top)
# Create a child of the "middle environment and add the data object to it
bottom <- env(middle, db=db)
# Create a data_mask where the bottom contains the masking elements and the top
# the last element of the data_mask.
new_mask <- new_data_mask(bottom, top = top)
事实上,如果我读对了,函数的父函数是全局环境
█
1. ├─rlang::eval_tidy(call_stack(), data = new_mask)
2. └─global::call_stack()
3. └─lobstr::cst()
然而,我对如何使这项工作起作用感到困惑。非常感谢您的帮助
BONOUS:如果我能够在
maxLik
中按名称调用参数,而不调用attach()
/detach()
,那就太棒了 一个选项是创建一个包装器,以db
作为上下文,将ll_lik
的主体作为表达式进行计算:
llwrap <- function(param) {
eval( body(ll_lik), db )
}
model <- maxLik::maxLik(llwrap, start=param) # Works
你好我不是
datamasks
方面的专家,因此我无法评论如何使用这种方法访问db
中的对象,但我只是想知道一个(可能是愚蠢的)问题。我在maxLik
函数的帮助页面上读到以下句子:“…:[…]优化器未使用的参数被转发到logLik
、grad
和hess
”。我认为您可以使用参数x
、y
和z
定义ll_lik
函数,然后使用db$x
或使用with
函数传递相应的值。如果你愿意,我可以用这种方法提供更详细的答案。是的,attach()
的使用是为了避免将db$
放在所有变量前面(在某些情况下有很多变量)。我想在遇到类似问题之前,我已经尝试过使用with()
函数。如果我正确理解with()
,它的功能确实类似于数据掩码,但数据掩码可以包含数据、函数和您可能希望在log函数中访问的其他对象。我可能也误解了这一点。很有趣。我不知道body()
。这种方法是否也可以扩展以允许按名称调用param
的元素,而不是param[1]
、param[2]
等?非常感谢您提供最新的答案。这无疑是朝着正确方向迈出的一步。我将在更大的代码上下文中稍微研究一下您的解决方案,看看它是如何工作的。干杯。@user63230:从技术上讲,这是在创造一个环境eval()
将把作为第二个参数提供的列表转换为一个环境,在该环境中计算第一个参数(表达式)。这个答案中的eval模式基本上只是一个复杂的with()
。考虑<代码>(MTCAS,MPG*CYL)< /代码>。通过将表达式存储在变量e中,所以它只是用于变量屏蔽,类似于mutate
so。。。。我关于创建环境的意思是,您不必显式地创建一个环境。例如,在表达式求值的上下文中,您可以认为列表、数据帧和环境是可互换的。所以,是的,这是真的:我们没有显式地调用new.env()
。但是,我们显式地使用c(as.list(db)、as.list(param))
创建一个列表,然后通过eval()
隐式地将其转换为一个环境。
█
1. ├─rlang::eval_tidy(call_stack(), data = new_mask)
2. └─global::call_stack()
3. └─lobstr::cst()
llwrap <- function(param) {
eval( body(ll_lik), db )
}
model <- maxLik::maxLik(llwrap, start=param) # Works
ll_expr <- rlang::expr({ # An expression, not a function
pr_1 <- 1 / (1 + exp(-(p1*x - p2*y))) # <-- now using p1, p2
pr_2 <- 1 - pr_1
lik <- z * pr_1 + (1 - z) * pr_2
log(lik)
})
llwrap2 <- function(param) {
ctx <- c( as.list(db), as.list(param) ) # Combine param and db into one context
eval( ll_expr, ctx ) # No longer need body()
}
model <- maxLik::maxLik(llwrap2, start=param) # Works