2010-08-24 77 views
3

我充滿TRUE/FALSE值的矩陣,我試圖找到每一行的第一TRUE值的索引位置(或返回NA如果有行中沒有TRUE值)。下面的代碼完成了工作,但它使用了一個apply()調用,我相信這只是for循環的一個包裝。我正在處理一些大型數據集,並且性能正在受到影響。有更快的方法嗎?如何R中矢量化在矩陣中的每一行此操作

> x <- matrix(rep(c(F,T,T),10), nrow=10) 
> x 
     [,1] [,2] [,3] 
[1,] FALSE TRUE TRUE 
[2,] TRUE TRUE FALSE 
[3,] TRUE FALSE TRUE 
[4,] FALSE TRUE TRUE 
[5,] TRUE TRUE FALSE 
[6,] TRUE FALSE TRUE 
[7,] FALSE TRUE TRUE 
[8,] TRUE TRUE FALSE 
[9,] TRUE FALSE TRUE 
[10,] FALSE TRUE TRUE 

> apply(x,1,function(y) which(y)[1]) 
[1] 2 1 1 2 1 1 2 1 1 2 
+1

在可讀性方面,'apply' /'which'非常清晰AR。 – Vince 2010-08-24 14:15:38

回答

4

不知道這是任何好轉,但這是一個解決辦法:

> x2 <- t(t(matrix(as.numeric(x), nrow=10)) * 1:3) 
> x2[x2 == 0] <- Inf 
> rowMins(x2) 
[1] 2 1 1 2 1 1 2 1 1 2 

編輯:這裏有一個更好的解決方案使用基礎R:

> x2 <- (x2 <- which(x, arr=TRUE))[order(x2[,1]),] 
> x2[as.logical(c(1,diff(x2[,1]) != 0)),2] 
[1] 2 1 1 2 1 1 2 1 1 2 
+0

謝謝謝恩,這可以完成工作。 – Abiel 2010-08-24 13:33:19

+1

需要首先加載fBasics或fUtilities我認爲... – John 2010-08-24 13:45:13

+0

好趕上...'需要(fBasics)'。 – Shane 2010-08-24 14:24:21

2

你可以得到一個通過使用%%%/%很多速度:

x <- matrix(rep(c(F,T,T),10), nrow=10) 

z <- which(t(x))-1 
((z%%ncol(x))+1)[match(1:nrow(x), (z%/%ncol(x))+1)] 

這可以根據需要進行調整:如果您想爲列執行此操作,則不必轉置矩陣。

嘗試了在1,000,000×5矩陣:

x <- matrix(sample(c(F,T),5000000,replace=T), ncol=5) 

system.time(apply(x,1,function(y) which(y)[1])) 

#> user system elapsed 
#> 12.61 0.07 12.70 

system.time({ 
z <- which(t(x))-1 
(z%%ncol(x)+1)[match(1:nrow(x), (z%/%ncol(x))+1)]} 
) 

#> user system elapsed 
#> 1.11 0.00 1.11 

你可以得到相當多的這種方式。

+0

如果連續不存在TRUE,則此解決方案將爲此提供NA。 – 2010-08-24 15:49:36

3

幾年後,我想添加兩種替代方法。

1)隨着max.col

> max.col(x, "first") 
[1] 2 1 1 2 1 1 2 1 1 2 

2)隨着aggregate

> aggregate(col ~ row, data = which(x, arr.ind = TRUE), FUN = min)$col 
[1] 2 1 1 2 1 1 2 1 1 2 

作爲性能是一個問題,讓我們來測試在更大的數據集的不同方法。首先,創建用於每個方法的函數:

abiel <- function(n){apply(n, 1, function(y) which(y)[1])} 
maxcol <- function(n){max.col(n, "first")} 
aggr.min <- function(n){aggregate(col ~ row, data = which(n, arr.ind = TRUE), FUN = min)$col} 
shane.bR <- function(n){x2 <- (x2 <- which(n, arr=TRUE))[order(x2[,1]),]; x2[as.logical(c(1,diff(x2[,1]) != 0)),2]} 
joris <- function(n){z <- which(t(n))-1;((z%%ncol(n))+1)[match(1:nrow(n), (z%/%ncol(n))+1)]} 

其次,創建一個更大的數據集:

xl <- matrix(sample(c(F,T),9e5,replace=TRUE), nrow=1e5) 

第三,運行基準:

library(microbenchmark) 
microbenchmark(abiel(xl), maxcol(xl), aggr.min(xl), shane.bR(xl), joris(xl), 
       unit = 'relative') 

這導致:

Unit: relative 
     expr  min   lq  mean  median   uq  max neval cld 
    abiel(xl) 55.102815 33.458994 15.781460 33.243576 33.196486 2.911675 100 d 
    maxcol(xl) 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 100 a  
aggr.min(xl) 439.863935 262.595535 118.436328 263.387427 256.815607 16.709754 100  e 
shane.bR(xl) 12.477856 8.522470 7.389083 13.549351 24.626431 1.748501 100 c 
    joris(xl) 7.922274 5.449662 4.418423 5.964554 9.855588 1.491417 100 b