2011-11-15 40 views
7

Tôi muốn tổng hợp các hàng của ma trận bằng cách thêm các giá trị vào các hàng có cùng tên. Cách tiếp cận hiện tại của tôi là như sau:Các hàng tổng hợp trong một ma trận lớn bằng cách gọi lại

> M 
    a b c d 
1 1 1 2 0 
1 2 3 4 2 
2 3 0 1 2 
3 4 2 5 2 
> index <- as.numeric(rownames(M)) 
> M <- cbind(M,index) 
> Dfmat <- data.frame(M) 
> Dfmat <- aggregate(. ~ index, data = Dfmat, sum) 
> M <- as.matrix(Dfmat) 
> rownames(M) <- M[,"index"] 
> M <- subset(M, select= -index) 
> M 
    a b c d 
1 3 4 6 2 
2 3 0 1 2 
3 4 2 5 2 

Vấn đề appraoch này là tôi cần phải áp dụng nó cho một số ma trận rất lớn (lên đến 1.000 hàng và 30.000 cột). Trong những trường hợp này, thời gian tính toán rất cao (cùng một vấn đề khi sử dụng ddply). Có một eficcient hơn để đến với các giải pháp? Nó có giúp các ma trận đầu vào ban đầu là DocumentTermMatrix từ gói tm không? Theo như tôi biết chúng được lưu trữ trong một định dạng ma trận thưa thớt.

+0

Không hoàn toàn rõ ràng những gì bạn cần làm, nhưng tôi muốn chơi xung quanh với gói 'reshape' (melt() và cast()) một chút. Nhưng quan trọng hơn: làm cách nào bạn cho phép tên hàng trùng lặp ở địa điểm đầu tiên? đó thường là một ý tưởng tồi. –

+0

Trong dữ liệu của tôi, tên gọi là ngày tháng. Chúng trùng lặp bất cứ khi nào tôi có nhiều quan sát trong cùng một ngày. – Christian

+0

@Christian Đó là tốt. Tôi nghĩ Carl đang nghĩ đến các khung dữ liệu, nơi mà các bản sao hoàn toàn không được phép. –

Trả lời

6

Đây là giải pháp sử dụng bycolSums, nhưng yêu cầu một số không phù hợp do đầu ra mặc định là by.

M <- matrix(1:9,3) 
rownames(M) <- c(1,1,2) 
t(sapply(by(M,rownames(M),colSums),identity)) 
    V1 V2 V3 
1 3 9 15 
2 3 6 9 
1

Câu trả lời của James hoạt động như mong đợi nhưng khá chậm đối với các ma trận lớn. Đây là một phiên bản đó avoids creating of new objects:

combineByRow <- function(m) { 
    m <- m[ order(rownames(m)), ] 

    ## keep track of previous row name 
    prev <- rownames(m)[1] 
    i.start <- 1 
    i.end <- 1 

    ## cache the rownames -- profiling shows that it takes 
    ## forever to look at them 
    m.rownames <- rownames(m) 
    stopifnot(all(!is.na(m.rownames))) 


    ## go through matrix in a loop, as we need to combine some unknown 
    ## set of rows 
    for (i in 2:(1+nrow(m))) { 

     curr <- m.rownames[i] 

     ## if we found a new row name (or are at the end of the matrix), 
     ## combine all rows and mark invalid rows 
     if (prev != curr || is.na(curr)) { 

      if (i.start < i.end) { 
       m[i.start,] <- apply(m[i.start:i.end,], 2, max) 
       m.rownames[(1+i.start):i.end] <- NA 
      } 

      prev <- curr 
      i.start <- i 
     } else { 
      i.end <- i 
     } 
    } 

    m[ which(!is.na(m.rownames)),]  
} 

Kiểm tra nó cho thấy đó là khoảng 10x nhanh hơn so với câu trả lời bằng by (2 so với 20 giây trong ví dụ này):

N <- 10000 

m <- matrix(runif(N*100), nrow=N) 
rownames(m) <- sample(1:(N/2),N,replace=T) 

start <- proc.time() 
m1 <- combineByRow(m) 
print(proc.time()-start) 

start <- proc.time() 
m2 <- t(sapply(by(m,rownames(m),function(x) apply(x, 2, max)),identity)) 
print(proc.time()-start) 

all(m1 == m2) 
1

Có được bây giờ là một tổng hợp chức năng trong Matrix.utils. Điều này có thể thực hiện những gì bạn muốn với một dòng mã và khoảng 10x nhanh hơn so với giải pháp combineByRow và 100x nhanh hơn so với giải pháp by:

N <- 10000 

m <- matrix(runif(N*100), nrow=N) 
rownames(m) <- sample(1:(N/2),N,replace=T) 

> microbenchmark(a<-t(sapply(by(m,rownames(m),colSums),identity)),b<-combineByRow(m),c<-aggregate.Matrix(m,row.names(m)),times = 10) 
Unit: milliseconds 
                expr  min   lq  mean  median   uq  max neval 
a <- t(sapply(by(m, rownames(m), colSums), identity)) 6000.26552 6173.70391 6660.19820 6419.07778 7093.25002 7723.61642 10 
            b <- combineByRow(m) 634.96542 689.54724 759.87833 732.37424 866.22673 923.15491 10 
       c <- aggregate.Matrix(m, row.names(m)) 42.26674 44.60195 53.62292 48.59943 67.40071 70.40842 10 

> identical(as.vector(a),as.vector(c)) 
[1] TRUE 

EDIT: Frank là đúng, rowsum là hơi nhanh hơn so với bất kỳ những các giải pháp. Bạn sẽ muốn cân nhắc sử dụng một trong các chức năng khác này chỉ khi bạn đang sử dụng một Matrix, đặc biệt là một phần thưa thớt, hoặc nếu bạn đang thực hiện một tập hợp bên cạnh sum.

+2

Có lẽ bạn có thể thêm 'rowsum (m, rownames (m))', đó là giải pháp cơ bản (lạ không xuất hiện trong số các câu trả lời ở đây). – Frank

+0

Không sử dụng chức năng này. Nó không âm thầm trên hầu như tất cả các khả năng cho đối số "FUN". https://github.com/cran/Matrix.utils/issues/1 –

+0

@eric_kernfeld 'aggregate.Matrix' chấp nhận đối số chuỗi là 'count', 'mean' hoặc 'sum'. Đây là tài liệu tốt hơn trong phiên bản hiện tại. – Craig

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