Áp dụng một hàm cho mỗi hàng của ma trận hoặc khung dữ liệu


129

Giả sử tôi có một ma trận bằng 2 và một hàm lấy một vectơ 2 làm một trong các đối số của nó. Tôi muốn áp dụng hàm cho từng hàng của ma trận và lấy một vectơ n. Làm thế nào để làm điều này trong R?

Ví dụ: tôi muốn tính mật độ của phân phối chuẩn 2D theo 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 chức năng cho từng hàng out?

Làm thế nào để truyề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?

Câu trả lời:


180

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

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> 

Điều 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 vượt qua đối số bổ sung cho các chức năng như thứ tư, thứ năm, ... đối số apply().


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

Đọc trợ giúp cho apply()- nó quét theo hàng (khi đối số 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.
Dirk Eddelbuettel

@Tim: nếu bạn sử dụng hàm R nội bộ và hàng không phải là đối số đầu tiên, hãy làm như Dirk đã làm và tạo chức năng tùy chỉnh của riêng bạn trong đó hàng đối số đầu tiên.
Joris Meys

3
Gói plyr cung cấp một loạt các loại chức năng áp dụng này. Nó cũng cung cấp nhiều chức năng hơn, bao gồm xử lý song song.
Paul Hiemstra

6
@ cryptic0 câu trả lời này là muộn, nhưng đối với các nhân viên của Google, đối số thứ hai được áp dụng là MARGINđối số. Ở đây nó có nghĩa là áp dụng hàm cho các hàng (thứ nguyên đầu tiên trong dim(M)). Nếu là 2, nó sẽ áp dụng hàm cho các cột.
De Novo

17

Trong trường hợp bạn muốn áp dụng các hàm phổ biến như sum hoặc mean, bạn nên sử dụng rowSumshoặc rowMeansvì chúng nhanh hơn apply(data, 1, sum)cách tiếp cận. Nếu không, gắn bó với apply(data, 1, fun). Bạn có thể chuyển các đối số bổ sung sau đối số FUN (như Dirk đã đề xuất):

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

15

Dưới đây là một ví dụ ngắn về việc áp dụng một hàm cho mỗi hàng của ma trận. (Ở đây, hàm được áp dụng bình thường hóa mỗi hàng thành 1.)

Lưu ý: Kết quả từ apply()phải được chuyển đổi bằng cách sử dụng t()để có cùng bố cục với 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

6

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

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 với tham số mặc định của mình thì cuộc gọi sẽ bao gồm các đối số được đặt tên sau hàm:

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

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


4

Áp dụng làm công việc tốt, nhưng khá chậm. Sử dụng sapply và vapply có thể hữu ích. Hàng của dplyr cũng có thể hữu ích. Hãy xem ví dụ về cách thực hiện hàng thông minh 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 / áp dụng là cách thực hành tốt vì nó giúp giảm thời gian rất nhiều. Chúng ta 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 về cách t () đang được sử dụng


Có thể công bằng hơn khi so sánh gia đình áp dụng nếu bạn đã sử dụng b <- t(iris[1:10, 1:3])apply(b, 2 prod).
DaSpeeg

2

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.