【问题标题】:'R' Matrix subset without loop'R' 没有循环的矩阵子集
【发布时间】:2013-03-06 16:28:05
【问题描述】:

大约 6 个月前我开始使用 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 &lt;- 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 被确定为代码的另一部分的一部分,因此我无法使用绝对索引号),我想减少相同的时间。任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 为什么这个标签是html,只有那个?
  • 作为一般的好习惯,您可能希望避免通过函数名称调用变量(如c

标签: r for-loop subset apply


【解决方案1】:

您可以尝试某种矩阵索引解决方案,像这样。目前尚不清楚它是否真的会更快。在小情况下,我认为肯定会,但在大情况下,从创建矩阵到索引所花费的开销可能比仅仅运行 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

【讨论】:

  • 非常感谢 Aaron、@geektrader、Roland 和 Arun 向我展示了可能加速解决方案的方法。我尝试了 4 种方法中的 3 种(还没有尝试过 Arun 的方法),结果它们比当前的“for loop”解决方案更慢和/或需要更多内存。为了完整起见,我有 16GB RAM i7 系统。同时,我将尝试像 Aaron 建议的那样构建一个数据集,然后发布相同的内容,看看是否有帮助。感谢大家花时间为我提供帮助。我肯定已经学会了解决这个问题的不同方法。
【解决方案2】:
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

【讨论】:

    【解决方案3】:

    以下是使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-20
      • 2021-05-24
      • 2012-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-23
      相关资源
      最近更新 更多