Как написать функцию R, которая оценивает выражение в кадре данных - PullRequest
9 голосов
/ 13 января 2011

Головоломка для R cognoscenti: скажем, у нас есть фрейм данных:

df <- data.frame( a = 1:5, b = 1:5 )

Я знаю, что мы можем сделать что-то вроде

with(df, a)

, чтобы получить вектор результатов.

Но как мне написать функцию, которая принимает выражение (например, a или a > 3) и делает то же самое внутри.Т.е. я хочу написать функцию fn, которая принимает фрейм данных и выражение в качестве аргументов и возвращает результат оценки выражения «внутри» фрейма данных как среды.

Не берите в голову, что это звучит надуманно (я мог бы просто использовать with, как указано выше), но это просто упрощенная версия более сложной функции, которую я пишу.Я пробовал несколько вариантов (используя eval, with, envir, substitute, local и т. Д.), Но ни один из них не работает.Например, если я определяю fn следующим образом:

fn <- function(dat, expr) {
  eval(expr, envir = dat)
}

Я получаю эту ошибку:

> fn( df, a )
Error in eval(expr, envir = dat) : object 'a' not found

Очевидно, что мне не хватает чего-то тонкого в окружении и оценке.Есть ли способ определить такую ​​функцию?

Ответы [ 4 ]

11 голосов
/ 14 января 2011

Пакет решетки делает это по-другому. См., Например, lattice:::xyplot.formula.

fn <- function(dat, expr) {
  eval(substitute(expr), dat)
}
fn(df, a)             # 1 2 3 4 5
fn(df, 2 * a + b)     # 3 6 9 12 15
10 голосов
/ 13 января 2011

Это потому, что вы не передаете выражение.

Попытка:

fn <- function(dat, expr) {
  mf <- match.call() # makes expr an expression that can be evaluated
 eval(mf$expr, envir = dat)
}

> df <- data.frame( a = 1:5, b = 1:5 )
> fn( df, a )
[1] 1 2 3 4 5
> fn( df, a+b )
[1]  2  4  6  8 10

Быстрый взгляд на исходный код функций, использующих это (например, lm), может раскрыть намного больше интересных вещей об этом.

2 голосов
/ 16 октября 2012

Поздняя запись, но подход и синтаксис data.table могут оказаться именно тем, что вам нужно.Именно так [.data.table работает с аргументами j, i и by.

Если вам нужно это в форме fn(x,expr), то вы можете использовать следующее

library(data.table)

DT <- data.table(a = 1:5, b = 2:6)

`[`(x=DT, j=a)

## [1] 1 2 3 4 5

 `[`(x=DT, j=a * b)
## [1]  2  6 12 20 30

Я думаю, что проще использовать в более родной форме

DT[,a]
## [1] 1 2 3 4 5

и так далее.В фоновом режиме это использует substitute и eval

0 голосов
/ 14 января 2011

? Внутри может также представлять интерес.

 df <- data.frame( a = 1:5, b = 1:5 ) 
 within(df, cx <- a > 3)
   a b    cx
 1 1 1 FALSE
 2 2 2 FALSE
 3 3 3 FALSE
 4 4 4  TRUE
 5 5 5  TRUE
...