Подтвердить что ты не робот

Как написать функцию, которая вызывает функцию, которая вызывает data.table?

В пакете data.table имеется специальный синтаксис, который требует использования выражений в качестве аргументов i и j.

Это имеет некоторые последствия для того, как одни функции записи принимают и передают аргументы таблицам данных, как это хорошо объясняется в разделе 1.16 часто задаваемых вопросов.

Но я не могу понять, как сделать этот дополнительный уровень.

Вот пример. Скажем, я хочу написать функцию-обертку foo(), которая делает конкретное резюме моих данных, а затем вторую обертку plotfoo(), которая вызывает foo() и выводит результат:

library(data.table)


foo <- function(data, by){
  by <- substitute(by)
  data[, .N, by=list(eval(by))]
}

DT <- data.table(mtcars)
foo(DT, gear)

ОК, это работает, потому что я получаю результаты с табулированием:

   by  N
1:  4 12
2:  3 15
3:  5  5

Теперь я пытаюсь сделать то же самое при написании plotfoo(), но я терплю неудачу:

plotfoo <- function(data, by){
  by <- substitute(by)
  foo(data, eval(by))
}
plotfoo(DT, gear)

Но на этот раз я получаю сообщение об ошибке:

Error: evaluation nested too deeply: infinite recursion / options(expressions=)?

ОК, поэтому проблема eval(). Удалите его:

plotfoo <- function(data, by){
  by <- substitute(by)
  foo(data, by)
}
plotfoo(DT, gear)

Нет, я получаю новое сообщение об ошибке:

Error in `[.data.table`(data, , .N, by = list(eval(by))) : 
  column or expression 1 of 'by' or 'keyby' is type symbol. Do not quote column names. Useage: DT[,sum(colC),by=list(colA,month(colB))]

И здесь я остаюсь застрявшим.

Вопрос: Как написать функцию, вызывающую функцию, которая вызывает data.table?

4b9b3361

Ответ 1

Это будет работать:

plotfoo <- function(data, by) {
  by <- substitute(by)
  do.call(foo, list(quote(data), by))
}

plotfoo(DT, gear)
#    by  N
# 1:  4 12
# 2:  3 15
# 3:  5  5

Объяснение:

Проблема заключается в том, что ваш вызов foo() в plotfoo() выглядит следующим образом:

foo(data, eval(by))
foo(data, by)

Когда foo обрабатывает эти вызовы, он покорно substitute для второго формального аргумента (by) получает как by значение символы eval(by) или by. Но вы хотите, чтобы значение by было gear, как в случае вызова foo(data, gear).

do.call() решает эту проблему, оценивая элементы своего второго аргумента перед построением вызова, который он затем оценивает. В результате, когда вы передаете его by, он вычисляет его значение (символ gear) перед построением вызова, который выглядит (по существу) следующим образом:

foo(data, gear)

Ответ 2

Я думаю, ты можешь завязать себя в узлах. Это работает:

library(data.table)
foo <- function(data, by){
  by <- by
  data[, .N, by=by]
}

DT <- data.table(mtcars)
foo(DT, 'gear')

plotfoo <- function(data, by){
  foo(data, by)
}
plotfoo(DT, 'gear')

И этот метод поддерживает передачу символьных значений:

> gg <- 'gear'
> plotfoo <- function(data, by){
+   foo(data, by)
+ }
> plotfoo(DT, gg)
   gear  N
1:    4 12
2:    3 15
3:    5  5