将公式传递给np包中的npcdensbw()函数的奇怪行为

时间:2018-03-16 12:38:39

标签: r formula

我试图在我写的另一个函数中包含对np::npcdensbw()的调用。因此,我希望能够将条件密度的公式作为变量传递给npcdensbw()。但是,当我这样做时,我得到一个错误,我无法弄清楚为什么。最小的例子:

####### Testing data
N <- 100 # sample size

x_cont <- rnorm(N)
x_dis <- rbinom(N, 1, .5)

# errors
e <- rnorm(N)

# depvar
y <- as.numeric((x_cont + .5 * x_dis + e) > 0)

df <- data.frame(y, x_cont, x_dis)

当我编写公式并直接调用函数时,它执行没有问题:

# Estimate conditional density of y
ycdens <- np::npcdensbw(as.factor(y) ~ x_cont + as.factor(x_dis), df)

但是因为我想从另一个函数中调用这个函数,我需要将公式作为参数传递,当我这样做时它不起作用:

# create  formula object
f <- formula(as.factor(y) ~ x_cont + as.factor(x_dis))

ycdens2 <- np::npcdensbw(f, df) # doesnt work
#> Error in names(res) <- c("response", "terms"): 'names' attribute [2] must be the same length as the vector [1]

ycdens2 <- np::npcdensbw(deparse(f), df) # doesnt work
#> Error in npcdensbw.conbandwidth(xdat = xdat, ydat = ydat, bws = tbw): number of rows of 'ydat' does not match 'xdat'\

func <- function(form, data) {
           np::npcdensbw(form, data)
  }
func(as.factor(y) ~ x_cont + as.factor(x_dis), df) # doesnt work
# Error in names(res) <- c("response", "terms") : 'names' attribute [2] must be the same length as the vector [1]

我似乎无法弄清楚为什么会发生这种情况或我如何规避它。在函数中使用时出现的错误似乎来自我在explodeFormula() github page上找到的代码函数np,并且只是:

explodeFormula <- function(formula){
  res <- strsplit(strsplit(paste(deparse(formula), collapse=""),
                           " *[~] *")[[1]], " *[+] *")
  stopifnot(all(sapply(res,length) > 0))
  names(res) <- c("response","terms")
  res
}

当我在任意公式上调用它时,此代码不会抛出错误,因此问题必须在其他地方。

1 个答案:

答案 0 :(得分:1)

遗憾的是,此功能的编码方式无法轻易传递变量。

查看npcdensbw.formula(),它会在调用match.call之前执行explodeFormula(),因为它已经崩溃了(正如您已正确指出的那样)。

这意味着deparse(formula)只是您在通话中传递的变量(字符串),例如上面例子中的"f""form"

如果explodeFormula()的代码改为调用deparse(eval(formula)),则可能会有效。

简单示例:

explodeFormula <- function(formula) {
  res <- strsplit(strsplit(paste(deparse(formula), collapse=""),
                           " *[~] *")[[1]], " *[+] *")
  stopifnot(all(sapply(res,length) > 0))
  names(res) <- c("response","terms")
  res
}

myFunc <- function(x, ...) {
  mf <- match.call(expand.dots = FALSE)
  explodeFormula(mf[["x"]])
}

ff <- as.factor(y) ~ x_cont + as.factor(x_dis)

myFunc(ff)
myFunc(as.factor(y) ~ x_cont + as.factor(x_dis))

explodeFormula <- function(formula) {
     res <- strsplit(strsplit(paste(deparse(eval(formula)), collapse=""), " *[~] *")[[1]], " *[+] *")
     stopifnot(all(sapply(res,length) > 0))
     names(res) <- c("response","terms")
     res
}

myFunc(ff)
$response
[1] "as.factor(y)"

$terms
[1] "x_cont"           "as.factor(x_dis)"