Как вы кодируете функцию R так, чтобы она "знала", чтобы искать "данные" для переменных в других аргументах?

Если вы запустите:

mod <- lm(mpg ~ factor(cyl), data=mtcars)

Он работает, потому что lm знает, как искать mtcars, чтобы найти mpg и cyl.

Тем не менее mean(mpg) выходит из строя, так как он не может найти mpg, поэтому вы делаете mean(mtcars$mpg).

Как вы кодируете функцию, чтобы она знала, что нужно искать в "данных" для переменных?

myfun <- function (a,b,data){
    return(a+b)
}

Это будет работать с:

myfun(mtcars$mpg, mtcars$hp)

но сбой:

myfun(mpg,hp, data=mtcars )

Приветствия

Ответы

Ответ 1

Вот как я бы закодировал myfun():

myfun <- function(a, b, data) {
    eval(substitute(a + b), envir=data, enclos=parent.frame())
}

myfun(mpg, hp, mtcars)
#  [1] 131.0 131.0 115.8 131.4 193.7 123.1 259.3  86.4 117.8 142.2 140.8 196.4
# [13] 197.3 195.2 215.4 225.4 244.7  98.4  82.4  98.9 118.5 165.5 165.2 258.3
# [25] 194.2  93.3 117.0 143.4 279.8 194.7 350.0 130.4

Если вы знакомы с with(), интересно видеть, что он работает почти точно так же:

> with.default
# function (data, expr, ...) 
# eval(substitute(expr), data, enclos = parent.frame())
# <bytecode: 0x016c3914>
# <environment: namespace:base>

В обоих случаях ключевая идея состоит в том, чтобы сначала создать выражение из символов, переданных в качестве аргументов, и затем оценить это выражение, используя data в качестве "среды" оценки.

Первая часть (например, поворот a + b в выражение mpg + hp) возможен благодаря substitute(). Вторая часть возможна, потому что eval() был красиво спроектирован таким образом, что в качестве среды оценки может быть data.frame.

Ответ 2

lm "знает", чтобы посмотреть в свой аргумент data, потому что он фактически создает вызов model.frame, используя свой собственный вызов в качестве базы. Если вы посмотрите на код для lm, вы увидите необходимое оборудование в первой десятке строк или около того.

Вы можете реплицировать это для своих целей, но если ваши потребности проще, вам не нужно идти в такой же степени. Например:

myfun <- function(..., data)
eval(match.call(expand.dots=FALSE)$...[[1]], data)

Или просто посмотрите evalq.

Ответ 3

Это не совсем так, как вы просили, но если вы не знаете о with(), это может быть вариант:

 myfun <- function (a,b){
    return(a+b)
 }
 with(mtcars, myfun(mpg, hp))

Вы можете удалить аргумент data для myfun для этого.