2010-11-21 40 views
85

Giả sử tôi có ma trận n và 2 và hàm nhận 2-vectơ là một trong các đối số của nó. Tôi muốn áp dụng hàm cho mỗi hàng của ma trận và nhận được một vectơ. Làm thế nào để làm điều này trong R?Áp dụng một hàm cho mỗi hàng của ma trận hoặc khung dữ liệu

Ví dụ, tôi muốn để tính mật độ phân phối bình thường tiêu chuẩn 2D trên ba điểm:

bivariate.density(x = c(0, 0), mu = c(0, 0), sigma = c(1, 1), rho = 0){ 
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+x[2]^2/sigma[2]^2-2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2)) 
} 

out <- rbind(c(1, 2), c(3, 4), c(5, 6)) 

Làm thế nào để áp dụng các chức năng để mỗi hàng của out?

Làm cách nào để chuyển giá trị cho các đối số khác ngoài các điểm cho hàm theo cách bạn chỉ định?

Trả lời

136

Bạn chỉ cần sử dụng chức năng apply():

R> M <- matrix(1:6, nrow=3, byrow=TRUE) 
R> M 
    [,1] [,2] 
[1,] 1 2 
[2,] 3 4 
[3,] 5 6 
R> apply(M, 1, function(x) 2*x[1]+x[2]) 
[1] 4 10 16 
R> 

này có một ma trận và áp dụng một hàm (ngớ ngẩn) cho mỗi hàng. Bạn chuyển các đối số thừa cho hàm như thứ tư, thứ năm, ... đối số cho apply().

+0

Cảm ơn! Điều gì nếu các hàng của ma trận không phải là arg đầu tiên của hàm? Làm thế nào để xác định mà arg của hàm mỗi hàng của ma trận được gán cho? – Tim

+0

Đọc trợ giúp cho 'apply()' - nó quét theo hàng (khi arg thứ hai là 1, khác theo cột), và hàng hiện tại (hoặc col) luôn là đối số đầu tiên. Đó là cách mọi thứ được định nghĩa. –

+0

@Tim: nếu bạn sử dụng hàm R bên trong và hàng không phải là arg đầu tiên, hãy làm như Dirk đã làm và thực hiện chức năng tùy chỉnh của riêng bạn, nơi hàng ** là ** arg đầu tiên. –

6

Bước đầu tiên sẽ là tạo đối tượng hàm, sau đó áp dụng nó. Nếu bạn muốn có một đối tượng ma trận mà có cùng số hàng, bạn có thể ấn định trước đó và sử dụng đối tượng [] hình thức như minh họa (nếu không thì giá trị trả về sẽ được đơn giản hóa đến một vector):

bvnormdens <- function(x=c(0,0),mu=c(0,0), sigma=c(1,1), rho=0){ 
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+ 
          x[2]^2/sigma[2]^2- 
          2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 
    1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2)) 
    } 
out=rbind(c(1,2),c(3,4),c(5,6)); 

bvout<-matrix(NA, ncol=1, nrow=3) 
bvout[] <-apply(out, 1, bvnormdens) 
bvout 
      [,1] 
[1,] 1.306423e-02 
[2,] 5.931153e-07 
[3,] 9.033134e-15 

Nếu bạn muốn sử dụng khác so với thông số mặc định của bạn sau đó các cuộc gọi nên bao gồm lập luận đặt tên theo chức năng:

bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6) 

apply() cũng có thể được sử dụng trên các mảng chiều cao và lập luận MARGIN có thể là một vector cũng như một số nguyên duy nhất .

14

Trong trường hợp bạn muốn áp dụng các hàm phổ biến như tổng hoặc trung bình, bạn nên sử dụng rowSums hoặc rowMeans vì chúng nhanh hơn phương pháp apply(data, 1, sum). Nếu không, hãy gắn bó với apply(data, 1, fun). Bạn có thể vượt qua đối số bổ sung sau khi tranh luận FUN (như Dirk đã gợi ý):

set.seed(1) 
m <- matrix(round(runif(20, 1, 5)), ncol=4) 
diag(m) <- NA 
m 
    [,1] [,2] [,3] [,4] 
[1,] NA 5 2 3 
[2,] 2 NA 2 4 
[3,] 3 4 NA 5 
[4,] 5 4 3 NA 
[5,] 2 1 4 4 

Sau đó, bạn có thể làm một cái gì đó như thế này:

apply(m, 1, quantile, probs=c(.25,.5, .75), na.rm=TRUE) 
    [,1] [,2] [,3] [,4] [,5] 
25% 2.5 2 3.5 3.5 1.75 
50% 3.0 2 4.0 4.0 3.00 
75% 4.0 3 4.5 4.5 4.00 
2

Một cách tiếp cận nếu bạn muốn sử dụng một phần khác nhau của bộ dữ liệu thay vì một giá trị duy nhất là sử dụng rollapply(data, width, FUN, ...). Sử dụng vectơ có độ rộng cho phép bạn áp dụng một hàm trên cửa sổ thay đổi của tập dữ liệu. Tôi đã sử dụng điều này để xây dựng một thói quen lọc thích ứng, mặc dù nó không phải là rất hiệu quả.

11

Dưới đây là ví dụ ngắn về cách áp dụng hàm cho mỗi hàng của ma trận. (Ở đây, chức năng áp dụng trở lại bình thường mỗi hàng để 1.)

Lưu ý: Kết quả từ apply() phải được hoán sử dụng t() để có được bố trí tương tự như ma trận đầu vào A.

A <- matrix(c(
    0, 1, 1, 2, 
    0, 0, 1, 3, 
    0, 0, 1, 3 
), nrow = 3, byrow = TRUE) 

t(apply(A, 1, function(x) x/sum(x))) 

Kết quả:

 [,1] [,2] [,3] [,4] 
[1,] 0 0.25 0.25 0.50 
[2,] 0 0.00 0.25 0.75 
[3,] 0 0.00 0.25 0.75 
3

Áp dụng không được công việc tốt, nhưng khá chậm. Sử dụng một cách thủ công và vâng lời có thể hữu ích. dplyr's rowwise cũng có thể hữu ích Hãy xem ví dụ về cách thực hiện sản phẩm hàng khôn ngoan của bất kỳ khung dữ liệu nào.

a = data.frame(t(iris[1:10,1:3])) 
vapply(a, prod, 0) 
sapply(a, prod) 

Lưu ý rằng việc gán cho biến trước khi sử dụng vapply/sapply/apply là thực hành tốt vì nó làm giảm thời gian. Hãy xem kết quả microbenchmark

a = data.frame(t(iris[1:10,1:3])) 
b = iris[1:10,1:3] 
microbenchmark::microbenchmark(
    apply(b, 1 , prod), 
    vapply(a, prod, 0), 
    sapply(a, prod) , 
    apply(iris[1:10,1:3], 1 , prod), 
    vapply(data.frame(t(iris[1:10,1:3])), prod, 0), 
    sapply(data.frame(t(iris[1:10,1:3])), prod) , 
    b %>% rowwise() %>% 
     summarise(p = prod(Sepal.Length,Sepal.Width,Petal.Length)) 
) 

Có một cái nhìn cẩn thận như thế nào t() đang được sử dụng

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