2013-02-12 41 views
18

Pakiet data.table ma specjalną składnię, która wymaga użycia wyrażeń jako argumentów i i j.Jak napisać funkcję, która wywołuje funkcję, która wywołuje data.table?

Ma to pewien wpływ na to, w jaki sposób jedna funkcja zapisu przyjmuje i przekazuje argumenty do tabel danych, co jest bardzo dobrze wyjaśnione w section 1.16 of the FAQs.

Ale nie mogę wymyślić, jak wziąć ten dodatkowy poziom.

Oto przykład. Że chcę napisać funkcję otoki foo() sprawia, że ​​konkretne podsumowanie moich danych, a następnie drugie owinięcie plotfoo() który wywołuje foo() i działki wynik:

library(data.table) 


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

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

OK, to działa, bo dostać moje tabelaryczne wyników :

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

teraz staram się tak samo podczas pisania plotfoo() ale nie zdało egzaminu:

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

Ale tym razem pojawia się komunikat o błędzie:

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

OK, więc eval() jest przyczyną problemu. Załóżmy, usuń go:

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

Och nie, mam nowy komunikat o błędzie:

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))] 

A oto gdzie pozostanie zatrzymany.

Pytanie: Jak napisać funkcję, która wywołuje funkcję, która wywołuje data.table?

+0

nie jest rozwiązaniem, ale jeśli usunąć 'substytut (by) 'i' eval' i przekazują 'gear' jako zmienną znakową, taką jak' foo (DT, "gear") 'następnie oba działają. – Arun

Odpowiedz

13

to będzie działać:

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 

Objaśnienie:

Problemem jest to, że zadzwoń pod numer foo() w plotfoo() wygląda jak jeden z następujących powodów:

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

Kiedy foo Procesy te rozmowy, IT sumiennie substitute s dla drugiego formalnego argumentu (by) uzyskanie jak by „s wartości symboli eval(by) lub by. Ale chcesz mieć wartość by jako gear, tak jak w wywołaniu foo(data, gear).

do.call() rozwiązuje ten problem, oceniając elementy drugiego argumentu przed skonstruowaniem wywołania, które następnie ocenia. W rezultacie, kiedy przekazać go by, ocenia go do jej wartości (symbol gear) przed wykonaniem połączenia, który wygląda (zasadniczo) tak:

foo(data, gear) 
+0

bardzo ładne wyjaśnienie! Dziękuję Ci. – Arun

+0

+1 To działa bardzo dobrze, dziękuję, nawet za mój domniemany, ale nieokreślony wymóg przekazywania argumentów "i" i "j". – Andrie

5

Myślę, że możesz się związać w węzłach.To działa:

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') 

I to metoda wspiera przekazując wartości znakowych:

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

Przepraszam, że przeszkadzam, ale zastanawiam się, co oznacza "by <- by" wewnątrz foo. – vodka

+0

Ach, tak, masz rację. Przepraszam, próbując uprościć mój przykład, usunąłem pierwotny problem przekazania argumentu do 'i' lub' j'. Przykro mi - będę edytować moje pytanie. – Andrie

+0

@vodka: Bez szczególnego znaczenia. Zostało tylko po edycji oryginału. –

Powiązane problemy