2011-09-12 83 views
44

Tôi tự hỏi nếu có hàm dựng sẵn trong R áp dụng hàm cho mỗi phần tử của ma trận (tất nhiên, hàm sẽ được tính dựa trên chỉ số ma trận). Tương đương sẽ là một cái gì đó như thế này:Làm thế nào để áp dụng hàm trên mỗi chỉ số của phần tử ma trận

matrix_apply <- function(m, f) { 
    m2 <- m 
    for (r in seq(nrow(m2))) 
    for (c in seq(ncol(m2))) 
     m2[[r, c]] <- f(r, c) 
    return(m2) 
} 

Nếu không có tích hợp chức năng như vậy, cách tốt nhất để khởi tạo một ma trận để chứa các giá trị thu được bằng cách tính toán một chức năng tùy ý trong đó có chỉ số ma trận như thông số là bao nhiêu?

+2

bạn có quen thuộc với họ có chức năng 'apply()' không? Tham số MARGIN chấp nhận các giá trị cho hàng, cột và hàng & cột. Chưa kể rằng một vài hàm R được vectorized và có thể tránh được loại lập trình này. – Chase

+3

@leden bạn có thể đưa ra một ví dụ về 'f()'? Theo như tôi có thể nói, bất kỳ hàm vectơ nào cũng sẽ hoạt động trên một ma trận vì nó chỉ là một vec tơ có thuộc tính mờ. Bạn không cần phải chia nhỏ nó thành các chỉ mục hàng và cột. Tại thời điểm này có một số lượng mơ hồ trong Q của bạn; nó có vẻ như bạn muốn có một giải pháp chung nhưng hãy đăng ký rằng nó nên b dựa trên các chỉ số, đó là phụ tối ưu. –

+0

Ý tôi là, tại sao không thể viết 'f()' như vậy mà tất cả những gì bạn thực sự cần là 'm [] <- f (m)'? Tôi sẽ thêm một ví dụ ... –

Trả lời

27

tôi nghi ngờ bạn muốn outer:

> mat <- matrix(NA, nrow=5, ncol=3) 

> outer(1:nrow(mat), 1:ncol(mat) , FUN="*") 
    [,1] [,2] [,3] 
[1,] 1 2 3 
[2,] 2 4 6 
[3,] 3 6 9 
[4,] 4 8 12 
[5,] 5 10 15 

> outer(1:nrow(mat), 1:ncol(mat) , FUN=function(r,c) log(r+c)) 
      [,1]  [,2]  [,3] 
[1,] 0.6931472 1.098612 1.386294 
[2,] 1.0986123 1.386294 1.609438 
[3,] 1.3862944 1.609438 1.791759 
[4,] 1.6094379 1.791759 1.945910 
[5,] 1.7917595 1.945910 2.079442 

Đó mang lại một lượng nhỏ gọn đẹp. nhưng có thể là mapply sẽ hữu ích trong các tình huống khác. Việc nghĩ về mapply là một cách khác để thực hiện thao tác tương tự mà những người khác trên trang này đang sử dụng Vectorize cho. mapply là tổng quát hơn vì không có khả năng Vectorize để sử dụng các hàm "nguyên thủy".

data.frame(mrow=c(row(mat)), # straightens out the arguments 
      mcol=c(col(mat)), 
      m.f.res= mapply(function(r,c) log(r+c), row(mat), col(mat) )) 
# mrow mcol m.f.res 
1  1 1 0.6931472 
2  2 1 1.0986123 
3  3 1 1.3862944 
4  4 1 1.6094379 
5  5 1 1.7917595 
6  1 2 1.0986123 
7  2 2 1.3862944 
8  3 2 1.6094379 
9  4 2 1.7917595 
10 5 2 1.9459101 
11 1 3 1.3862944 
12 2 3 1.6094379 
13 3 3 1.7917595 
14 4 3 1.9459101 
15 5 3 2.0794415 

Bạn có lẽ không thực sự có nghĩa là để cung cấp cho các chức năng gì hàng() và col() chức năng sẽ trở lại: Điều này tạo ra một mảng của 15 (hơi thừa) 3 x 5 ma trận:

> outer(row(mat), col(mat) , FUN=function(r,c) log(r+c)) 
8

Bạn có thể nghĩ đến việc outer:

rows <- 1:10 
cols <- 1:10 

outer(rows,cols,"+") 

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] 
[1,] 2 3 4 5 6 7 8 9 10 11 
[2,] 3 4 5 6 7 8 9 10 11 12 
[3,] 4 5 6 7 8 9 10 11 12 13 
[4,] 5 6 7 8 9 10 11 12 13 14 
[5,] 6 7 8 9 10 11 12 13 14 15 
[6,] 7 8 9 10 11 12 13 14 15 16 
[7,] 8 9 10 11 12 13 14 15 16 17 
[8,] 9 10 11 12 13 14 15 16 17 18 
[9,] 10 11 12 13 14 15 16 17 18 19 
[10,] 11 12 13 14 15 16 17 18 19 20 

Đó rõ ràng là một chức năng ví dụ khá tầm thường, nhưng bạn có thể cung cấp tùy chỉnh của riêng bạn một là tốt. Xem ?outer.

Sửa

Trái với những nhận xét dưới đây, bạn cũng có thể sử dụng outer với các chức năng không vectorized bởi .... vectorizing họ!

m <- matrix(1:16,4,4) 

#A non-vectorized function 
myFun <- function(x,y,M){ 
    M[x,y] + (x*y) 
} 

#Oh noes! 
outer(1:4,1:4,myFun,m) 
Error in dim(robj) <- c(dX, dY) : 
    dims [product 16] do not match the length of object [256] 

#Oh ho! Vectorize()! 
myVecFun <- Vectorize(myFun,vectorize.args = c('x','y')) 

#Voila! 
outer(1:4,1:4,myVecFun,m) 
    [,1] [,2] [,3] [,4] 
[1,] 2 7 12 17 
[2,] 4 10 16 22 
[3,] 6 13 20 27 
[4,] 8 16 24 32 
+0

Tôi không nghĩ rằng 'bên ngoài' là những gì anh ta đang tìm kiếm. Chỉ vì ví dụ của anh ta với vòng lặp kép giống như một cuộc gọi đến 'bên ngoài' không có nghĩa là' bên ngoài' sẽ hoạt động. Bạn có thể đưa ra một ví dụ sử dụng một hàm không được vector hóa không? – adamleerich

+0

@adamleerich Xem chỉnh sửa của tôi. Các hàm không được vector hóa có thể được vector hóa. – joran

10

Bạn không cho chúng tôi biết loại hàm bạn muốn áp dụng cho từng phần tử, nhưng tôi cho rằng lý do duy nhất các ví dụ trong các câu trả lời khác là do các hàm đã được vector hóa. Nếu bạn thực sự muốn áp dụng một chức năng cho từng phần tử, outer sẽ không cung cấp cho bạn bất kỳ điều gì đặc biệt mà chức năng chưa cung cấp cho bạn. Bạn sẽ nhận thấy rằng các câu trả lời thậm chí không vượt qua một ma trận để outer!

Cách theo dõi nhận xét và sử dụng của @ Chase apply.

Ví dụ, tôi có ma trận

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2) 

Nếu tôi muốn biến nó thành một ma trận nhân vật, phần tử bằng phần tử (chỉ là một ví dụ) tôi có thể làm điều này

apply(m, c(1,2), as.character) 

Trong số tất nhiên, as.character đã được vector hóa, nhưng chức năng đặc biệt của tôi my.special.function thì không. Nó chỉ mất một đối số, một phần tử. Không có cách nào tốt để có được outer để làm việc với nó.Nhưng, công trình này

apply(m, c(1,2), my.special.function) 
+0

Bạn có thể làm cho công việc 'bên ngoài 'với một hàm không được vector hóa, nhưng chỉ bằng cách giả mạo vectơ trong cùng một cách mà bạn đang giả mạo nó với' apply() '. –

+0

Hơn nữa, tôi trình bày trong câu trả lời cách bạn có thể thực hiện 'as.character()' mà không dùng đến 'apply()', và như vậy, có lẽ chúng ta nên vận động đúng cách để làm việc thay vì giả mạo kết quả mong muốn - 'apply()' * của bạn * chỉ ẩn một vòng lặp đơn dài hơn nơi mà OP có hai vòng lặp lồng nhau. –

15

Phương pháp đơn giản nhất là chỉ để sử dụng một f() có thể được áp dụng trực tiếp đến các yếu tố của ma trận. Ví dụ, sử dụng ma trận m từ @ adamleerich của trả lời

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2) 

Không có lý do để sử dụng apply() trong trường hợp ví dụ as.character(). Thay vào đó chúng ta có thể hoạt động trên các yếu tố của m như thể nó là một vector (nó thực sự là một) và thay thế tại chỗ:

> m[] <- as.character(m) 
> m 
    [,1] [,2] [,3] [,4] 
[1,] "1" "3" "5" "7" 
[2,] "2" "4" "6" "8" 

Phần đầu tiên của khối đó là chìa khóa ở đây. m[] buộc các thành phần của m được thay thế bằng đầu ra từ as.character(), thay vì ghi đè m bằng vectơ ký tự.

Vì vậy, giải pháp chung để áp dụng hàm cho từng phần tử của ma trận.

Nếu ai thực sự cần phải sử dụng một f() hoạt động trên hàng và cột chỉ số sau đó tôi muốn viết một f() sử dụng row()col():

> m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2) 
> row(m) 
    [,1] [,2] [,3] [,4] 
[1,] 1 1 1 1 
[2,] 2 2 2 2 
> col(m) 
    [,1] [,2] [,3] [,4] 
[1,] 1 2 3 4 
[2,] 1 2 3 4 
> row(m) * col(m) ## `*`(row(m), col(m)) to see this is just f() 
    [,1] [,2] [,3] [,4] 
[1,] 1 2 3 4 
[2,] 2 4 6 8 

hoặc một trong đó sử dụng outer() như của người khác đã chỉ ra. Nếu f() không được vector hóa, thì tôi sẽ suy nghĩ lại về chiến lược của mình càng nhiều càng tốt khi có i) có lẽ là một cách để viết một phiên bản thực sự được vector hóa, và ii) một hàm không được vectơ hóa sẽ không mở rộng tốt.

+0

+1 Ví dụ hay về kiểu 'm []'. – Iterator

0

Điều này không trả lời chính xác câu hỏi của bạn, nhưng tôi đã tìm thấy câu hỏi này trong khi cố gắng tìm ra một câu hỏi tương tự để tôi sẽ cho bạn thấy điều gì đó.

Giả sử bạn có một hàm mà bạn muốn áp dụng cho từng phần tử của ma trận chỉ yêu cầu một phần.

mydouble <- function(x) { 
    return(x+x) 
} 

Và nói rằng bạn có một ma trận X,

> x=c(1,-2,-3,4) 
> X=matrix(x,2,2) 
> X 
    [,1] [,2] 
[1,] 1 -3 
[2,] -2 4 

thì bạn làm như sau:

res=mydouble(X) 

Sau đó, nó sẽ làm một đôi yếu tố khôn ngoan của mỗi giá trị.

Tuy nhiên, nếu bạn thực hiện logic trong hàm như dưới đây, bạn sẽ nhận được cảnh báo rằng nó không được tham số hóa và không hoạt động như bạn mong đợi.

myabs <- function(x) { 
    if (x<0) { 
     return (-x) 
    } else { 
     return (x) 
    } 
} 

> myabs(X) 
    [,1] [,2] 
[1,] 1 -3 
[2,] -2 4 
Warning message: 
In if (x < 0) { : 
    the condition has length > 1 and only the first element will be used 

Nhưng nếu bạn sử dụng hàm apply(), bạn có thể sử dụng nó.

Ví dụ:

> apply(X,c(1,2),myabs) 
    [,1] [,2] 
[1,] 1 3 
[2,] 2 4 

Vì vậy, đó là rất tốt, phải không? Vâng, nó bị hỏng nếu bạn có một hàm với hai hoặc nhiều parm.Ví dụ: bạn có điều này:

mymath <- function(x,y) { 
    if(x<0) { 
     return(-x*y) 
    } else { 
     return(x*y) 
    } 
} 

Trong trường hợp này, bạn sử dụng hàm apply(). Tuy nhiên, nó sẽ mất ma trận nhưng kết quả được tính toán chính xác. Họ có thể được cải cách nếu bạn nghiêng.

> mapply(mymath,X,X) 
[1] 1 -4 -9 16 
> mapply(mymath,X,2) 
[1] 2 4 6 8 
> matrix(mapply(mymath,X,2),c(2,2)) 
    [,1] [,2] 
[1,] 2 6 
[2,] 4 8 
Các vấn đề liên quan