2012-07-04 14 views
9

Có thể chuỗi các chức năng trong R?Phương pháp xích với R

mẫu dữ liệu:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2) 

Ví dụ, tôi muốn thay thế những điều khoản sau đây:

step1 <- mean(m) 
step2 <- sum(step1) 
res <- step2 

Hoặc,

res <- sum(mean(m)) 

Với một cái gì đó như thế này:

res <- [email protected]()@sum() 

Trong một số trường hợp, điều đó sẽ làm rõ mã của tôi đáng kể.

EDIT1 Đây là ví dụ giả. Tôi chọn ngẫu nhiên 'sum' và 'mean'.

Ben đã đưa ra một mảnh đầu tiên của câu trả lời bằng% @% tuy nhiên, nó ngăn chặn từ việc sử dụng lập luận thêm trong chức năng:

m %@% function1(arg1, arg2) %@% function2(arg1, arg2) 

Làm thế nào tôi có thể làm việc xung quanh đó?

EDIT2 Thêm một ví dụ

require(xts) 
require(PerformanceAnalytics) 
xts.ts <- xts(rnorm(231),as.Date(13514:13744,origin="1970-01-01")) 
plot(na.omit(lag(rollapply(xts.ts, width=rolling.per-1, FUN= function(x){sqrt(var(x))*sqrt(252)}), k=1)), main = "Dummy Example") 

Ví dụ này dường như làm việc tốt với Charles giải pháp:

`%@%` <- function(x, f) eval.parent(as.call(append(as.list(substitute(f)), list(x), 1))) 
xts.ts %@% rollapply(width = rolling.per-1, FUN= function(x) x%@%var%@%sqrt * sqrt(252)) %@% lag(k=1) %@% na.omit %@% plot(main = "Dummy Example") 

Ít quan trọng đối với trường hợp của tôi, nhưng woth nhắc đến, các Statment sau không thành công với giải pháp của Charles :

xts.ts %@% names <- 'ts name' 
+2

Có gì sai với 'res <- sum (mean (m))'? –

+0

Không có gì, nhưng nó không có ý nghĩa nhiều để lấy 'tổng hợp' của một chiều dài 1 vector (đó là những gì được trả về bởi' mean' trên một ma trận). – Henrik

+0

Mặc dù nó sẽ biến mất "sớm", vẫn còn một hàm 'mean.data.frame' trả về một vectơ. –

Trả lời

4

Trong một tĩnh mạch tương tự như câu trả lời của Ben, nhưng cho phép lập luận:

`%@%` <- function(x, f) eval.parent(as.call(append(as.list(substitute(f)), list(x), 1))) 

x %@% mean %@% sqr # => 6.25 
c(1, 2, NA, 3, 4) %@% mean(na.rm=T) %@% sqr # => 6.25 
m %@% colMeans() %@% sum() # => 21 
+0

Để người cuối cùng hoạt động, bạn cần sử dụng 'tên <-' hoặc' setNames', ví dụ 'xts.ts% @% setNames ('ts name')', vì '<-' có xử lý đặc biệt cho cuộc gọi hàm lhs sẽ không hoạt động ở đây. – Charles

+0

Tôi chưa từng thấy điều này trước đây. Đó là siêu thông minh. Tôi rất ngại sử dụng nó trong mã sản xuất vì cơ hội nó sẽ trở nên mong manh ... –

+0

Tôi sẽ không muốn sử dụng nó trong mã sản xuất hay cách khác! Bất kể đây có phải là một ý hay hay không, những gì tôi thích về R là loại điều này là có thể. Tôi có thể thấy nó trông quen thuộc hơn với một người quen với đối tượng OO 'object.verb()' phổ biến thay vì 'động từ' của R (đối tượng) '. – Charles

9

Sắp xếp, nhưng lần thứ I mực nó không thành ngữ và có thể mong manh/không phải là một ý tưởng hay. (Điều này được ngụ ý, tôi nghĩ rằng, bằng cách bình luận @ RichieCotton ở trên.)

Từ http://cran.r-project.org/doc/manuals/R-lang.html:

10.3.4 khai thác đặc biệt

R cho phép các nhà khai thác ghi vào người dùng định nghĩa. Các biểu tượng này có dạng chuỗi ký tự được giới hạn bởi ký tự ‘%’. Chuỗi có thể chứa bất kỳ ký tự in nào ngoại trừ ‘%’. Trình tự thoát cho các chuỗi không áp dụng tại đây.

Lưu ý rằng các nhà khai thác sau đây được xác định trước

%% %*% %/% %in% %o% %x% 
"%@%" <- function(x,f) { 
    f(x) 
} 

sqr <- function(x) x^2 
x <- 1:4 

x %@% mean ## 2.5 
x %@% mean %@% sqr ## 6.25 
x %@% (mean %@% sqr) ## fails 

Với m như định nghĩa ở trên - có thể những gì bạn có trong tâm trí?

m %@% colMeans %@% sum ## 21 

Ghi chú:

  • ví dụ của bạn là một chút buồn cười, vì mean(x) luôn trả về một đại lượng vô hướng (tức là chiều dài-1 vector), do đó sum(mean(x)) sẽ luôn luôn được giống như mean(x)
  • các toán tử infix phải được bao quanh bởi %, vì vậy bạn không thể có bất kỳ thứ gì nhỏ gọn như một biểu tượng duy nhất (và %% được lấy).
  • loại chuỗi này không liên kết, điều này làm tôi lo lắng - có vẻ như các ví dụ ở trên hoạt động, vì vậy R (dường như) đánh giá từ trái sang phải, nhưng tôi không biết điều đó được đảm bảo ...

chỉnh sửa: câu hỏi giờ đây sẽ hỏi cách thêm đối số bổ sung. Tôi không nghĩ cú pháp được đề xuất (x %@% fun1(arg1) %@% fun2(arg2)) sẽ hoạt động mà không có một số phép thuật nghiêm trọng. Đây là gần nhất tôi có thể nhận được tại thời điểm này - tạo ra một hàm bao bọc mà tạo ra một phiên bản sửa đổi của chức năng ban đầu.

F <- function(f,...) { 
    function(x) { 
     f(x,...) 
    } 
} 

Thử nghiệm:

pow <- function(x,b=2) { x^b } 
sqr <- function(x) x^2 
x <- 1:4 

x %@% F(mean,na.rm=TRUE) ## 2.5 
x %@% F(mean,na.rm=TRUE) %@% F(pow,3) ## 16.25 

(Lưu ý rằng tôi đã sử dụng F như một chức năng ở đây, có thể dicey trong một số trường hợp vì nó ghi đè các phím tắt F==FALSE)

+0

Dường như là một công việc xung quanh nhưng nó chỉ có thể làm các trick tốt! Bạn đề nghị sửa đổi toán tử% @% để tôi có thể thêm đối số như thế nào? Ví dụ: m% @% function1 (arg1, arg2)% function2 (arg1, arg2) – Sam

+1

Có gì với '"% @% "<- function (f, ...) f (...)'? Với tất cả các đối số này nên được chuyển tới 'f', ngay cả đối số được đặt tên. (lưu ý: chưa được kiểm tra) – Henrik

+0

@Sam, bạn có thể cho tôi một ví dụ cụ thể về những gì bạn muốn làm và kết quả cần tạo ra không? Cú pháp của bạn sẽ không hoạt động chính xác như được viết bởi vì 'function1 (arg1, arg2)' không phải là một hàm tổng quát. Tôi nghĩ rằng ý tưởng của Henrik có thể không hoạt động, bởi vì chúng ta phải cẩn thận để giữ cho các đối số theo đúng thứ tự ... 'f' phải là đối số * giây * để có được phép toán từ trái sang phải thứ tự ... –

11

Hãy thử các gói chức năng :

library(functional) 
squared <- function(x)x*x 
Compose(sum, squared)(m) 
## [1] 44100 
squared(sum(m)) 
## [1] 44100 

EDIT:

Về câu hỏi trong phần bình luận của một phản hồi khác về các đối số ở đây là một ví dụ về việc soạn thảo với các đối số. Curry cũng là từ các gói chức năng:

addn <- function(n, x) x + n 
Compose(Curry(addn, 1), squared)(10) 
## [1] 121 
squared(addn(1, 10)) 
## [1] 121 

EDIT 2:

Về câu hỏi về gỡ lỗi, debug công trình nếu chức năng là cà ri. Nếu nó vẫn chưa cà ri sau đó quấn nó trong Curry:

# this works since addn is curried 
debug(addn) 
Compose(Curry(addn, 1), squared)(10) 

# to debug squared put it in a Curry -- so this works: 
debug(squared) 
Compose(Curry(addn, 1), Curry(squared))(10) 
+0

Tôi nghĩ rằng gsubfn-package có thể làm một cái gì đó như thế, ít nhất là cú pháp postfix của env @ fn? –

+0

@DWin, Tốt. Trong gsubfn chúng ta có thể làm điều này: 'Soạn (fn $ identity (~ addn (1, x)), bình phương) (10)'. Tất nhiên ngay cả khi không có gsubfn chúng ta có thể làm điều này: 'Soạn (hàm (x) addn (1, x), bình phương) (10)' –

+0

@ G.Gothendieck, nó hoạt động nhưng tôi không nghĩ nó mang lại cho bạn những lợi thế thông thường giao diện thông thạo (http://en.wikipedia.org/wiki/Fluent_interface) như một số ngôn ngữ hướng đối tượng khác. Hơn nữa, tôi tin rằng nó không làm cho dễ dàng để gỡ rối một phần của một statment. – Sam

2

Tôi sẽ sử dụng gói magrittr. Nó có một toán tử "đường ống" lấy kết quả của một hàm và chuyển nó vào làm đối số đầu tiên cho hàm kế tiếp:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2) 

m %>% mean %>% sum 

Ceci n'est pas un pipe!