2011-01-13 43 views
9

Puzzle cho cognoscenti R: Giả sử chúng ta có một dữ liệu khung:Làm thế nào để viết một chức năng R mà đánh giá một biểu thức trong một dữ liệu-frame

df <- data.frame(a = 1:5, b = 1:5) 

Tôi biết chúng ta có thể làm những việc như

with(df, a) 

để nhận vectơ kết quả.

Nhưng làm cách nào để viết hàm có biểu thức (chẳng hạn như a hoặc a > 3) và thực hiện điều tương tự bên trong. I E. Tôi muốn viết một hàm fn lấy một khung dữ liệu và một biểu thức làm đối số và trả về kết quả của việc đánh giá biểu thức "bên trong" khung dữ liệu dưới dạng môi trường.

Đừng bận tâm rằng điều này nghe có vẻ khó hiểu (tôi chỉ có thể sử dụng with như trên), nhưng đây chỉ là một phiên bản đơn giản của một hàm phức tạp hơn tôi đang viết. Tôi đã thử một số biến thể (sử dụng eval, with, envir, substitute, local, v.v.) nhưng không có biến thể nào hoạt động. Ví dụ nếu tôi xác định fn như vậy:

fn <- function(dat, expr) { 
    eval(expr, envir = dat) 
} 

tôi nhận được lỗi này:

> fn(df, a) 
Error in eval(expr, envir = dat) : object 'a' not found 

Rõ ràng tôi đang thiếu một cái gì đó tinh tế về môi trường và đánh giá. Có cách nào để xác định một chức năng như vậy?

+1

Có phần trên [Wiki của Hadley về chủ đề này] (https: // github.com/hadley/devtools/wiki/Evaluation) – Marek

+0

@Marek đó là một tài liệu tham khảo tuyệt vời để đọc, cảm ơn! –

+0

Trang này có còn truy cập được không? –

Trả lời

10

Gói mạng thực hiện loại điều này theo một cách khác. Xem, ví dụ: lattice:::xyplot.formula.

fn <- function(dat, expr) { 
    eval(substitute(expr), dat) 
} 
fn(df, a)    # 1 2 3 4 5 
fn(df, 2 * a + b)  # 3 6 9 12 15 
+0

+1 đây là cách đơn giản nhất, nhờ –

+0

+1, rất hay (không nghĩ về thay thế). Lợi thế của match.call là bạn có tất cả các đối số của bạn trong một danh sách tiện lợi, đó là lý do tại sao tôi sử dụng nó một cách thường xuyên hơn. Nhưng nếu bạn không cần phần còn lại, thay thế thực sự là một cách rất tốt đẹp và dễ dàng. –

+0

Có cách nào để truyền nhiều biểu thức trong một danh sách() hoặc c() và đánh giá từng biểu thức trong một vòng lặp for cho các khung dữ liệu khác nhau cũng được lưu trữ trong một danh sách không? Tôi muốn các chức năng tương tự tôi chỉ có thể không làm cho nó làm việc cho dataframes và biểu thức được lưu trữ trong danh sách. – Blind0ne

9

Đó là vì bạn không chuyển biểu thức.

Hãy thử:

fn <- function(dat, expr) { 
    mf <- match.call() # makes expr an expression that can be evaluated 
eval(mf$expr, envir = dat) 
} 

> df <- data.frame(a = 1:5, b = 1:5) 
> fn(df, a) 
[1] 1 2 3 4 5 
> fn(df, a+b) 
[1] 2 4 6 8 10 

Nhìn lướt qua mã nguồn của các chức năng sử dụng này (ví dụ lm) có thể tiết lộ rất nhiều điều thú vị hơn về nó.

+0

cảm ơn, đó là những gì tôi bỏ lỡ! Và vâng, tôi đã thử xem xét các chức năng như 'tập hợp con 'và một số thứ khác để xem chúng hoạt động như thế nào, nhưng chúng là nội bộ. Không nghĩ về 'lm', điểm tốt để tham khảo trong tương lai. –

+1

Tôi nghĩ việc sử dụng thay thế trong trường hợp này là kinh điển hơn. Và tôi không chắc chắn lm là một mô hình vai trò tốt - ít nhất hãy chắc chắn để đọc các quy tắc đánh giá tiêu chuẩn phi tiêu chuẩn. – hadley

+0

@hadley: đúng. Tôi chỉ nghĩ về 'match.call()' và 'lm()' vì đối số 'data'. –

-1

? Bên trong cũng có thể được quan tâm.

df <- data.frame(a = 1:5, b = 1:5) 
within(df, cx <- a > 3) 
    a b cx 
1 1 1 FALSE 
2 2 2 FALSE 
3 3 3 FALSE 
4 4 4 TRUE 
5 5 5 TRUE 
+0

@mdsummer: Tôi e rằng bạn không hoàn toàn nắm bắt được câu hỏi ... –

2

Cách tiếp cận muộn, nhưng cú pháp và phương pháp data.table sẽ xuất hiện như sau. Đây chính xác là cách [.data.table hoạt động với các đối số j, iby.

Nếu bạn cần nó trong các hình thức fn(x,expr), sau đó bạn có thể sử dụng sau

library(data.table) 

DT <- data.table(a = 1:5, b = 2:6) 

`[`(x=DT, j=a) 

## [1] 1 2 3 4 5 

`[`(x=DT, j=a * b) 
## [1] 2 6 12 20 30 

Tôi nghĩ rằng đó là dễ dàng hơn để sử dụng ở dạng tự nhiên hơn

DT[,a] 
## [1] 1 2 3 4 5 

và vân vân. Trong nền, điều này đang sử dụng substituteeval

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