2013-03-18 118 views
2

我大约六个月前开始使用R,并且我在R中获得了一些经验。最近,我遇到了有关矩阵内子集的问题,希望能够帮助您制定解决方案我有更高的效率。'R'没有循环的矩阵子集

我想要做的是以下几点。假设我有一个矩阵和两个向量如下:

# matrix 
a <- matrix(seq(1,100,by=1),10,10) 
# vector (first column of matrix a) 
b <- c(2,4,5,6,7,8) 
# vector (column numbers of matrix a) 
c <- c(5,3,1,4,6,2) 

只是重申,

  • 矢量b指矩阵a的第一列。
  • 向量c是指矩阵的列号a

我想获得tmp99 <- a[b,c:8]。但是,当我这样做时,我收到以下警告消息。

Warning message: 
In c:8 : numerical expression has 6 elements: only the 
     first used (index has to be scalar and not vector) 

所以,我试着解决问题,使用循环和列表,我得到我想要的解决方案。我假设有一个比这更有效的解决方案。该解决方案是我到目前为止是这样的:

a <- matrix(seq(1,100,by=1),10,10) 
b <- c(2,4,5,6,7,8) 
c <- c(5,3,1,4,6,2) 
tmp <- list() 
for (i in 1:length(b)) tmp[[i]] <- c(a[b[i],(c[i]:8)]) 
tmp99 <- t(sapply(tmp, '[', 1:max(sapply(tmp, length)))) 
tmp99[is.na(tmp99)] <- 0 

我想知道什么是如果有办法避免使用循环实现上述,因为我的矩阵尺寸为200000 x 200,因为我有做这个很多(在我的问题中,bc被确定为代码的另一部分的一部分,所以我不能使用绝对索引号),我想减少相同的时间。任何帮助将不胜感激。谢谢。

+0

这是为什么标有'html',只有是什么? – CBroe 2013-03-18 11:04:04

+0

作为一般的良好实践,您可能希望避免通过函数名称调用变量(如'c') – ds440 2013-03-18 15:09:40

回答

1

以下是使用base程序包执行此操作的一种方法。有可能是更好的解决方案使用data.table但以下工作:)

a <- matrix(seq(1, 100, by = 1), 10, 10) 
b <- c(2, 4, 5, 6, 7, 8) 
c <- c(5, 3, 1, 4, 6, 2) 

res <- t(sapply(X = mapply(FUN = function(b, c) expand.grid(b, seq(from = c, to = 8)), b, c, SIMPLIFY = FALSE), FUN = function(x) { 
    c(a[as.matrix(x)], rep(0, 8 - nrow(x))) 
})) 

res 
##  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] 
## [1,] 42 52 62 72 0 0 0 0 
## [2,] 24 34 44 54 64 74 0 0 
## [3,] 5 15 25 35 45 55 65 75 
## [4,] 36 46 56 66 76 0 0 0 
## [5,] 57 67 77 0 0 0 0 0 
## [6,] 18 28 38 48 58 68 78 0 



# Let's break it down in multiple steps. 

coordinates <- mapply(FUN = function(b, c) expand.grid(b, seq(from = c, to = 8)), b, c, SIMPLIFY = FALSE) 

# below sapply subsets c using each element in coordinates and pads result with additional 0s such that total 8 elements are returned. 

res <- sapply(X = coordinates, FUN = function(x) { 
    c(a[as.matrix(x)], rep(0, 8 - nrow(x))) 
}) 
res 
##  [,1] [,2] [,3] [,4] [,5] [,6] 
## [1,] 42 24 5 36 57 18 
## [2,] 52 34 15 46 67 28 
## [3,] 62 44 25 56 77 38 
## [4,] 72 54 35 66 0 48 
## [5,] 0 64 45 76 0 58 
## [6,] 0 74 55 0 0 68 
## [7,] 0 0 65 0 0 78 
## [8,] 0 0 75 0 0 0 


# you probably need result as traspose 
res <- t(res) 

res 
##  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] 
## [1,] 42 52 62 72 0 0 0 0 
## [2,] 24 34 44 54 64 74 0 0 
## [3,] 5 15 25 35 45 55 65 75 
## [4,] 36 46 56 66 76 0 0 0 
## [5,] 57 67 77 0 0 0 0 0 
## [6,] 18 28 38 48 58 68 78 0 
2

你可以尝试某种矩阵索引的解决方案,是这样的。目前尚不清楚实际上是否会更快;在小的情况下,我认为它肯定会是,但是在大的情况下,创建矩阵到索引的开销可能需要比遍历for循环更长的时间。为了得到更好的答案,编制一个类似于我们可以测试的数据集。

idx.in <- cbind(rep(b, 8-c+1), unlist(lapply(c, function(x) x:8))) 
idx.out <- cbind(rep(seq_along(b), 8-c+1), unlist(lapply(c, function(x) 1:(8-x+1)))) 
tmp99 <- array(0, dim=apply(idx.out, 2, max)) 
tmp99[idx.out] <- a[idx.in] 

这是一个带有矩阵索引的版本,但是它为每一行分别进行。这可能会更快,具体取决于要替换的行数和列数。你想避免的是内存不足,for循环可以提供帮助,因为它不会同时在内存中保存每一步的所有细节。

out <- array(0, dim=c(length(b), 8-min(c)+1)) 
for(idx in seq_along(b)) { 
    out[cbind(idx, 1:(8-c[idx]+1))] <- a[cbind(b[idx], c[idx]:8)] 
} 
out 
+0

非常感谢Aaron,@geektrader,Roland和Arun向我展示了如何加速解决方案。我尝试了4种方法中的3种(还没有尝试过Arun的方法),并且它们比当前的'for循环'解决方案更慢和/或需要更多内存。为了完整性,我有16GB RAM i7系统。与此同时,我将尝试构建一个数据集,像Aaron建议的那样,并且发布相同的内容,看看是否有帮助。谢谢大家花时间为我提供帮助。我已经明确了解解决这个问题的不同方法。 – Ram 2013-03-19 08:10:41

0
tmp <- lapply(seq_len(length(b)),function(i) { 
    res <- a[b[i],c[i]:8] 
    res <- c(res,rep(0,c[i]-1)) 
    res 
               }) 
tmp99 <- do.call("rbind",tmp) 
#  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] 
# [1,] 42 52 62 72 0 0 0 0 
# [2,] 24 34 44 54 64 74 0 0 
# [3,] 5 15 25 35 45 55 65 75 
# [4,] 36 46 56 66 76 0 0 0 
# [5,] 57 67 77 0 0 0 0 0 
# [6,] 18 28 38 48 58 68 78 0