2013-02-12 3 views
18

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

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

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

Вот пример. Скажем, я хочу, чтобы написать функцию обертки 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?

+0

не решение, но если вы удалите 'заменителя (by) 'и' eval' и передать 'gear' в качестве символьной переменной, такой как' foo (DT, «gear») ', тогда оба работают. – Arun

ответ

13

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

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 «s символов eval(by) или by. Но вы хотите, чтобы значение by было gear, как в случае вызова foo(data, gear).

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

foo(data, gear) 
+0

очень симпатичный информация! Спасибо. – Arun

+0

+1 Это работает очень хорошо, спасибо, даже за мое подразумеваемое, но неустановленное требование для передачи аргументов 'i' и' j'. – Andrie

5

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

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 
+1

Извините, что беспокою вас, но мне интересно, что значит 'by <- by' внутри foo. – vodka

+0

А, да, вы совершенно правы. Я прошу прощения, пытаясь упростить мой пример, я удалил исходную проблему передачи аргумента в 'i' или' j'. Извините, я отредактирую свой вопрос. – Andrie

+0

@vodka: Нет особого значения. Просто осталось от редактирования оригинала. –

Смежные вопросы