Почему «замена» работает в нескольких строках, а не в одной? - PullRequest
7 голосов
/ 31 октября 2019

Я пытался ответить на этот хороший вопрос о создании нестандартной оценочной функции для объекта data.table, сгруппированной суммы. Акрун придумал прекрасный ответ, который я здесь упростил:

akrun <- function(data, var, group){
 var <- substitute(var)
 group <- substitute(group)
 data[, sum(eval(var)), by = group]
}

library(data.table)
mt = as.data.table(mtcars)
akrun(mt, cyl, mpg)
#    group    V1
# 1:     6 138.2
# 2:     4 293.3
# 3:     8 211.4

Я также работал над ответом и имел близкий к тому же ответу, но с substitute s, встроенным в остальных,Мой результат приводит к ошибке:

gregor = function(data, var, group) {
  data[, sum(eval(substitute(var))), by = substitute(group)]
} 

gregor(mt, mpg, cyl)
# Error in `[.data.table`(data, , sum(eval(substitute(var))), by = substitute(group)) : 
#  'by' or 'keyby' must evaluate to vector or list of vectors 
#  (where 'list' includes data.table and data.frame which are lists, too) 

На первый взгляд, моя функция - это простая замена функции Акруна. Почему это не работает?


Обратите внимание, что обе замены вызывают проблемы, как показано здесь:

gregor_1 = function(data, var, group) {
  var = substitute(var)
  data[,sum(eval(var)), 
       by = substitute(group)]
} 
gregor_1(mt, mpg, cyl)
# Same error as above


gregor_2 = function(data, var, group) {
  group = substitute(group)
  data[,sum(eval(substitute(var))), 
       by = group]
} 
gregor_2(mt, mpg, cyl)
# Error in eval(substitute(var)) : object 'mpg' not found 

Ответы [ 2 ]

5 голосов
/ 01 ноября 2019

В документации substitute вы можете прочитать, как он решает, что заменить, и тот факт, что по умолчанию он ищет среду, в которой он вызывается. Если вы вызовете substitute внутри фрейма data.table (то есть внутри []), он не сможет найти символы, потому что они отсутствуют в среде оценки data.table, они находятся в среде, где [ был вызван.

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

library(data.table)

foo <- function(dt, group, var) {
    eval(substitute(dt[, sum(var), by = group]))
}

foo(as.data.table(mtcars), cyl, mpg)
   cyl    V1
1:   6 138.2
2:   4 293.3
3:   8 211.4
1 голос
/ 31 октября 2019

Кажется, что substitute не работает в таблице данных так, как можно ожидать от его работы в других контекстах, но вы можете использовать enexpr из пакета rlang вместо substitute:

library(data.table)
library(rlang)

gregor_rlang = function(data, var, group) {
  data[, sum(eval(enexpr(var))), by = .(group = eval(enexpr(group)))]
} 

gregor_rlang(mt, mpg, cyl)
##    group    V1
## 1:     6 138.2
## 2:     4 293.3
## 3:     8 211.4

окружения

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

gregor_pf = function(data, val, group) {
  data[, sum(eval(substitute(val, parent.env(environment())))), 
    by = c(deparse(substitute(group)))]
} 
gregor_pf(mt, mpg, cyl)
##      cyl    V1
## 1:     6 138.2
## 2:     4 293.3
## 3:     8 211.4
...