Ответ 1
Одним из решений является размещение list(...)
в выводе функции.
Я стараюсь использовать as.quoted
, крадуя путь @hadley реализует .()
в пакете plyr
.
library(data.table)
library(plyr)
dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10)
myfun <- function(name) {
one <- paste0(name, '_one')
two <- paste0(name, '_two')
out <- paste0(name,'_out')
as.quoted(paste('list(',out, '=',one, '-', two,')'))[[1]]
}
dat[, eval(myfun('x')),]
# x_out
# 1: 0
# 2: 0
# 3: 0
# 4: 0
# 5: 0
# 6: 0
# 7: 0
# 8: 0
# 9: 0
#10: 0
Чтобы сделать сразу два столбца, вы можете настроить свой вызов
myfun <- function(name) {
one <- paste0(name, '_one')
two <- paste0(name, '_two')
out <- paste0(name,'_out')
calls <- paste(paste(out, '=', one, '-',two), collapse = ',')
as.quoted(paste('list(', calls,')'))[[1]]
}
dat[, eval(myfun(c('x','y'))),]
# x_out y_out
# 1: 0 0
# 2: 0 0
# 3: 0 0
# 4: 0 0
# 5: 0 0
# 6: 0 0
# 7: 0 0
# 8: 0 0
# 9: 0 0
# 0: 0 0
По причине.....
в этом решении весь вызов 'list(..)
оценивается внутри parent.frame, являющегося таблицей данных.
Соответствующий код в [.data.table
равен
if (missing(j)) stop("logical error, j missing")
jsub = substitute(j)
if (is.null(jsub)) return(NULL)
jsubl = as.list.default(jsub)
if (identical(jsubl[[1L]],quote(eval))) {
jsub = eval(jsubl[[2L]],parent.frame())
if (is.expression(jsub)) jsub = jsub[[1L]]
}
если (в вашем случае)
j = list(xout = eval(myfun('x')))
##then
jsub <- substitute(j)
является
# list(xout = eval(myfun("x")))
и
as.list.default(jsub)
## [[1]]
## list
##
## $xout
## eval(myfun("x"))
поэтому jsubl[[1L]]
есть list
, jsubl[[2L]]
is eval(myfun("x"))
поэтому data.table
не нашел вызов eval
и не будет обрабатывать его соответствующим образом.
Это будет работать, заставляя вторую оценку в правильных data.table
# using OP myfun
dat[,list(xout =eval(myfun('x'), dat))]
Точно так же
eval(parse(text = 'x_one'),dat)
# [1] 1 2 3 4 5 6 7 8 9 10
Работает, но
eval(eval(parse(text = 'x_one')), dat)
Не
Редактировать 10/4/13
Хотя, вероятно, безопаснее (но медленнее) использовать .SD
в качестве среды, так как тогда он будет устойчив к i
или by
, а также, например,
dat[,list(xout =eval(myfun('x'), .SD))]
Отредактируйте от Матфея:
+10 до выше. Я не мог бы лучше объяснить это. Сделав еще один шаг, я иногда создаю весь запрос data.table, а затем eval
. Иногда это может быть немного более устойчивым. Я думаю об этом, как SQL; т.е. мы часто строим динамический оператор SQL, который отправляется на SQL-сервер, который должен быть выполнен. При отладке тоже иногда проще смотреть на построенный запрос и запускать его в приглашении браузера. Но иногда такой запрос будет очень длинным, поэтому передача eval
в i
, j
или by
может быть более эффективной, не перекомпонуя другие компоненты. Как обычно, многие способы скинуть кошку.
Тонкие причины для рассмотрения eval
всего запроса:
-
Одна из причин, по которой быстро группируется, заключается в том, что она сначала проверяет выражение
j
. Если онlist
, он удаляет имена, но запоминает их. Затемeval
неназванный список для каждой группы, затем восстанавливает имена один раз, в конце в конечном результате. Одной из причин, по которой другие методы могут быть медленными, является повторение одного и того же имени столбца для каждой группы, снова и снова. Более сложныйj
определяется, хотя (например, если выражение не начинается точно с помощьюlist
), тем сложнее он будет кодировать внутреннюю проверку контрольной логики. В этой области есть много тестов; например, в сочетании сeval
и подробными отчетами, если удаление имени не работает. Но, построив "простой" запрос (полный запрос) иeval
, который может быть более быстрым и надежным по этой причине. -
В версии v.1.8.2 теперь выполняется оптимизация
j
:options(datatable.optimize=Inf)
. Это проверяетj
и модифицирует его для оптимизацииmean
иlapply(.SD,...)
идиомы. Это делает порядок разницы в величине и означает, что пользователю не нужно знать (например, некоторые из точек вики теперь ушли). Мы могли бы сделать больше этого; например,DT[a==10]
можно оптимизировать доDT[J(10)]
автоматически, еслиkey(DT)[1]=="a"
[Обновление Sep 2014 - теперь реализовано в версии 1.9.3]. Но опять же, внутренняя оптимизация усложняется для внутреннего кода, если вместоDT[,mean(a),by=b]
itDT[,list(x=eval(expr)),by=b]
, гдеexpr
содержит вызовmean
, например. Таким образом,eval
весь запрос может играть лучше сdatatable.optimize
. Поворот многословия в отчетах о том, что он делает и оптимизация может быть отключена, если это необходимо; например, для проверки разницы в скорости, которую он делает.
В соответствии с комментариями добавлен FR # 2183: "Изменить j = list (xout = eval (...)) eval для eval в пределах области DT". Спасибо за выделение. Что тип сложного j
Я имею в виду, где eval
вложен в выражение. Если j
начинается с eval
, тем не менее, это намного проще и уже закодировано (как показано выше) и протестировано и должно быть оптимизировано отлично.
Если один из них извлекается из этого, то это: использовать DT[...,verbose=TRUE]
или options(datatable.verbose=TRUE)
, чтобы проверить data.table
, все еще эффективно работая при использовании для динамических запросов с участием eval
.