2013-02-12 34 views
18

Gói data.table có một số cú pháp đặc biệt yêu cầu một cú pháp sử dụng các biểu thức như các đối số ij.Làm thế nào để viết một hàm gọi một hàm gọi data.table?

Điều này có một số hàm ý về cách một hàm viết chấp nhận và chuyển đối số cho các bảng dữ liệu, như được giải thích thực sự tốt trong section 1.16 of the FAQs.

Nhưng tôi không thể tìm hiểu cách thực hiện thêm một cấp độ này.

Đây là một ví dụ. Nói rằng tôi muốn viết một hàm wrapper foo() mà làm cho một bản tóm tắt dữ liệu cụ thể của tôi, và sau đó một wrapper thứ hai plotfoo() mà các cuộc gọi foo() và âm mưu kết quả:

library(data.table) 


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

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

OK, công trình này, bởi vì tôi nhận được kết quả lập bảng của tôi :

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

Bây giờ, tôi cố gắng chỉ là tương tự khi viết plotfoo() nhưng tôi thất bại thảm hại:

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

Nhưng lần này tôi nhận được một thông báo lỗi:

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

OK, vì vậy eval() đang gây ra một vấn đề. Hãy loại bỏ nó:

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

Ồ không, tôi nhận được một thông báo lỗi mới:

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

Và đây là nơi mà tôi vẫn bị mắc kẹt.

Câu hỏi: Cách viết hàm gọi hàm data.table?

+0

Không phải là một giải pháp, nhưng nếu bạn loại bỏ 'thay thế (bởi) 'và' eval' và truyền 'bánh' thành một biến ký tự như' foo (DT, "gear") 'thì cả hai đều hoạt động. – Arun

Trả lời

13

này sẽ làm việc:

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 

Giải thích:

Vấn đề là bạn hãy gọi tới số foo() trong plotfoo() trông giống như một trong những cách sau:

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

Khi foo quá trình những cuộc gọi, nó ngoan ngoãn substitute s cho số thứ hai chính thức (by) nhận như giá trị by 's những biểu tượng eval(by) hoặc by. Nhưng bạn muốn giá trị của bygear, như trong cuộc gọi foo(data, gear).

do.call() giải quyết vấn đề này bằng cách đánh giá các yếu tố của đối số thứ hai của nó trước khi xây dựng cuộc gọi mà nó sau đó đánh giá. Kết quả là, khi bạn vượt qua nó by, nó đánh giá nó để giá trị của nó (biểu tượng gear) trước khi xây dựng một cuộc gọi mà trông (chủ yếu) như thế này:

foo(data, gear) 
+0

giải thích rất hay! cảm ơn bạn. – Arun

+0

+1 Điều này hoạt động rất tốt, cảm ơn bạn, ngay cả đối với yêu cầu ngụ ý nhưng không được đưa ra của tôi để truyền các đối số 'i' và' j'. – Andrie

5

Tôi nghĩ rằng bạn có thể đang buộc mình lên bằng nút thắt.Đây hoạt động:

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

Và phương pháp hỗ trợ thông qua các giá trị nhân vật:

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

Xin lỗi vì đã làm phiền bạn nhưng tôi tự hỏi ý nghĩa của 'by <- by' bên trong foo là gì. – vodka

+0

Ah, vâng, bạn hoàn toàn đúng. Tôi xin lỗi, trong nỗ lực của tôi để đơn giản hóa ví dụ của tôi, tôi loại bỏ các vấn đề ban đầu của đi qua một đối số để 'i' hoặc' j'. Tôi xin lỗi - tôi sẽ chỉnh sửa câu hỏi của mình. – Andrie

+0

@vodka: Không có ý nghĩa cụ thể. Chỉ còn lại từ việc chỉnh sửa bản gốc. –

Các vấn đề liên quan