Использование `With` внутри функции (оболочки) - PullRequest
5 голосов
/ 05 декабря 2011

Я хотел бы написать обертку вокруг пользовательской функции, которая принимает некоторые векторы в качестве входных данных (например: mtcars$hp, mtcars$am и т. Д.), Чтобы принимать входные данные в качестве имени фрейма данных (как параметр data, например: mtcars) и имена переменных (например: hp и am), как обычно в большинстве стандартных функций.

Но у меня есть некоторые проблемы, моя предложенная демонстрационная функция (обертка вокруг mean не работает.

Код:

f <- function(x, data=NULL) {
    if (!missing(data)) {
        with(data, mean(x))
    } else {
        mean(x)
    }
}

Бег против вектора работает, конечно:

> f(mtcars$hp)
[1] 146.69

Но with, к сожалению, не получается:

> f(hp, mtcars)
Error in with(d, mean(x)) : object 'hp' not found

В то время как в глобальной среде / без моей пользовательской функции работает правильно:

> with(mtcars, mean(hp))
[1] 146.69

Я пытался провести какой-то эксперимент с substitute, deparse и другими, но безуспешно. Любая подсказка будет приветствоваться!

Ответы [ 3 ]

10 голосов
/ 05 декабря 2011

Вот ключевой фрагмент головоломки:

f <- function(x,data=NULL) {
  eval(match.call()$x,data) # this is mtcars$hp, so just take the mean of it or whatever
}

> f(hp,mtcars)
 [1] 110 110  93 110 175 105 245  62  95 123 123 180 180 180 205 215 230  66  52  65  97 150 150 245 175  66
[27]  91 113 264 175 335 109

# it even works without a data.frame specified:
> f(seq(10))
 [1]  1  2  3  4  5  6  7  8  9 10

См. Ссылку @ Андри на документ @ Хэдли для объяснения того, почему это работает. См. Примечание @ Hadley о критическом предупреждении: f () не может быть запущен из другой функции.

В основном R использует ленивую оценку (например, он не оценивает вещи, пока они не используются). Таким образом, вы можете избежать передачи hp, потому что он остается неоцененным символом, пока не появится где-нибудь. Поскольку match.call берет его как символ и ждет, чтобы оценить его, все хорошо.

Затем eval оценивает его в указанной среде. Согласно ?eval, второй аргумент представляет:

Среда, в которой должен быть вычислен expr. Может также быть NULL, list, фрейм данных, pairlist или целое число, как указано в sys.call.

Следовательно, вы в хорошей форме либо с NULL (если вы не передаете data.frame), либо с data.frame.

Доказательством ленивых вычислений является то, что это не возвращает ошибку (поскольку x никогда не используется в функции):

> g <- function(x) {
+   0
+ }
> g(hp)
[1] 0
3 голосов
/ 05 декабря 2011
f <- function(x, data=NULL) {
    if (!missing(data)) { colname=deparse(substitute(x))
         mean(data[[colname]])
    } else {
        mean(x)
    }
}

 f(hp, mtcars)
[1] 146.6875

(По общему признанию, не такой компактный, как у @ gsk, и, думаю, я попытаюсь вспомнить его метод над моим. И спасибо Джошу О'Брайену за то, что он указал на ошибку, которая теперь исправлена.)

0 голосов
/ 05 декабря 2011

попробуйте это:

f <- function(x, data = NULL) {
     if (is.null(data)) {
         mean(x)
     } else { 
         attach(data)
         mean(x)
         detach(data)
     }
 }

Также в вашем примере вы вводите набор данных вместо столбца.Ваш пример должен быть f (hp, mtcars)

...