Pregunta Cómo escribir una función R que evalúa una expresión dentro de un marco de datos


Puzzle para los conocedores: digamos que tenemos un marco de datos:

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

Sé que podemos hacer cosas como

with(df, a)

para obtener un vector de resultados.

Pero ¿cómo escribo una función que toma una expresión (como a o a > 3) y hace lo mismo dentro. Es decir. Quiero escribir una función fn que toma un marco de datos y una expresión como argumentos y devuelve el resultado de evaluar la expresión "dentro" del marco de datos como un entorno.

No importa que esto suene artificial (podría usar with como arriba), pero esta es solo una versión simplificada de una función más compleja que estoy escribiendo. Intenté varias variantes (utilizando eval, with, envir, substitute, local, etc.) pero ninguno de ellos funciona. Por ejemplo, si defino fn al igual que:

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

Me sale este error:

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

Claramente, me falta algo sutil sobre los entornos y la evaluación. ¿Hay alguna manera de definir tal función?


9
2018-01-13 16:50


origen


Respuestas:


El paquete de celosía hace este tipo de cosas de una manera diferente. Ver, por ejemplo, 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
2018-01-14 15:16



Eso es porque no estás pasando una expresión.

Tratar:

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

Un vistazo rápido al código fuente de las funciones que usan esto (ej. lm) puede revelar muchas cosas más interesantes al respecto.


9
2018-01-13 17:01



Una entrada tardía, pero el data.table el enfoque y la sintaxis parecen ser lo que buscas. Esto es exactamente como [.data.table trabaja con el j, i y by argumentos.

Si lo necesita en la forma fn(x,expr), entonces puedes usar lo siguiente

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

Creo que es más fácil de usar en forma más nativa

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

y así. En el fondo esto está usando substitute y eval


2
2017-10-16 05:14



? dentro también podría ser de interés.

 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

-1
2018-01-14 00:45