【问题标题】:Indexing the elements of a matrix in R在R中索引矩阵的元素
【发布时间】:2016-06-03 13:31:54
【问题描述】:

这个问题很傻,但我想知道我是否遗漏了什么。 假设有一个向量k 包含一些数字,比如说

> k
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

我想把它转换成矩阵

> m
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    0    6    7    8    9
[3,]    0    0   10   11   12
[4,]    0    0    0   13   14
[5,]    0    0    0    0   15

我的第一个想法是使用upper.tri() 的东西,例如m[upper.tri(m, diag = TRUE)] <- k,但这不会给出上面的矩阵。

有没有更智能的解决方案?下面是我的解决方案,但让我们说我并不太为此感到自豪。


rows <- rep(1:5, 5:1)

cols1 <- rle(rows)$lengths


cols <- do.call(c, lapply(1:length(cols1), function(x) x:5))

for(i in 1:length(k)) {
  m[rows[i], cols[i]] <- k[i]
}

【问题讨论】:

    标签: r matrix


    【解决方案1】:

    这是一个使用lower.trit 转置结果的选项:

    k <- 1:15
    m <- matrix(0, 5,5)
    m[lower.tri(m, diag = TRUE)] <- k
    m <- t(m)
    m 
    #     [,1] [,2] [,3] [,4] [,5]
    #[1,]    1    2    3    4    5
    #[2,]    0    6    7    8    9
    #[3,]    0    0   10   11   12
    #[4,]    0    0    0   13   14
    #[5,]    0    0    0    0   15
    

    微基准测试

    由于与 Joseph 的基准有些混淆,这里有另一个。我测试了大小为 10*10 的矩阵的三种解决方案; 100*100; 1000*1000; 10000*10000。

    结果:

    显然,性能很大程度上取决于矩阵的大小。对于大型矩阵,Joseph 的答案表现最快,而对于较小的矩阵,我的方法是最快的。请注意,这并未考虑内存效率。

    可重现的基准:

    Joseph <- function(k, n) {
      y <- 1L
      t <- rep(0L,n)
      j <- c(y, sapply(1:(n-1L), function(x) y <<- y+(n+1L)-x))
      t(vapply(1:n, function(x) c(rep(0L,x-1L),k[j[x]:(j[x]+n-x)]), t, USE.NAMES = FALSE))
    }
    
    Frank <- function(k, n) {
      m = matrix(0L, n, n)
      m[ which(lower.tri(m, diag=TRUE), arr.ind=TRUE)[, 2:1] ] = k
      m
    }
    
    docendo <- function(k,n) {
      m <- matrix(0L, n, n)
      m[lower.tri(m, diag = TRUE)] <- k
      t(m)
    }
    
    library(microbenchmark)
    library(data.table)
    library(ggplot2)
    n <- c(10L, 100L, 1000L, 10000L)
    k <- lapply(n, function(x) seq.int((x^2 + x)/2))
    
    b <- lapply(seq_along(n), function(i) {
      bm <- microbenchmark(Joseph(k[[i]], n[i]), Frank(k[[i]], n[i]), docendo(k[[i]], n[i]), times = 10L)
      bm$n <- n[i]
      bm
    })
    
    b1 <- rbindlist(b)
    
    ggplot(b1, aes(expr, time)) +
      geom_violin() +
      facet_wrap(~ n, scales = "free_y") +
      ggtitle("Benchmark for n = c(10L, 100L, 1000L, 10000L)")
    

    检查结果是否相等:

    all.equal(Joseph(k[[1]], n[1]), Frank(k[[1]], n[1]))
    #[1] TRUE
    all.equal(Joseph(k[[1]], n[1]), docendo(k[[1]], n[1]))
    #[1] TRUE
    

    注意:我没有在比较中包含 George 的方法,因为从 Joseph 的结果来看,它似乎慢了很多。所以我的基准测试中比较的所有方法都只用基础 R 编写。

    【讨论】:

    • 我知道一定有办法用upper.tri 或lower.tri 制作它。谢谢!
    • 感谢您提供全面的基准测试。这些图表也是一个非常好的触摸!您和@Frank 提供的解决方案更直观(更清晰的 IMO),并且可能对大多数情况更有用。
    • @JosephWood,这是一个有趣的问题,老实说,我有点惊讶您的解决方案对于大型矩阵的表现如此出色。希望你能得到更多的支持。
    • @docendo,我不太担心投票。想出解决方案让我很开心,是的,我也很惊讶它这么快。在对一个非常大的示例进行第一次基准测试之后,我确信我不小心测试了一个较小的矩阵,因为我对 k 使用了不同的索引(即k1, k2, etc.)。 Base R 从未停止让我感到惊讶。
    【解决方案2】:

    @docendodiscimus 回答的变体:您可以更改行和列索引,而不是转置,您可以通过将 lower.tri 包装在 which 中来获得:

    n = 5
    m = matrix(0, n, n)
    
    m[ which(lower.tri(m, diag=TRUE), arr.ind=TRUE)[, 2:1] ] = seq(sum(seq(n)))
    
    
         [,1] [,2] [,3] [,4] [,5]
    [1,]    1    2    3    4    5
    [2,]    0    6    7    8    9
    [3,]    0    0   10   11   12
    [4,]    0    0    0   13   14
    [5,]    0    0    0    0   15
    

    要了解它的工作原理,请按步骤查看左侧:

    • lower.tri(m, diag=TRUE)
    • which(lower.tri(m, diag=TRUE), arr.ind=TRUE)
    • which(lower.tri(m, diag=TRUE), arr.ind=TRUE)[, 2:1]

    我猜如果矩阵很大,转置可能会很昂贵,这就是我考虑这个选项的原因。注意:约瑟夫伍德的回答表明我错了,因为转置方式在他的基准测试中更快。


    (感谢@JosephWood:)您可以使用(n^2 - n)/2 + n,而不是使用sum(seq(n)) 进行枚举和求和。

    【讨论】:

    • 注意:(n^2 - n)/2 + n 等于 sum(seq(n))。不错的答案!
    【解决方案3】:
    library(miscTools)
    k <- 1:15
    triang(k, 5)
    

    【讨论】:

      【解决方案4】:

      这是一个非常快速的基础 R 解决方案:

      更新

      我稍微修改了代码,以便我只调用一次vapply,而不是我之前使用的sapply/vapply 组合(我也摆脱了USE.NAMES=FALSE,因为它似乎没有任何区别)。虽然这有点干净,但它并没有显着改变我机器上的时间(我用绘图重新运行了 docendo 的基准测试,它似乎几乎相同)。

      Triangle1 <- function(k,n) {
          y <- -n
          r <- rep(0L,n)
          t(vapply(1:n, function(x) {y <<- y+n+2L-x; c(rep(0L,x-1L),k[y:(y+n-x)])}, r))
      }
      

      以下是一些时间安排:

      Triangle2 <- function(k,n) {
          m <- matrix(0, n,n)
          m[lower.tri(m, diag = TRUE)] <- k
          t(m)
      }
      
      Triangle3 <- function(k, n) {
          m = matrix(0, n, n)
          m[ which(lower.tri(m, diag=TRUE), arr.ind=TRUE)[, 2:1] ] = k   ## seq(sum(seq(n)))  for benchmarking
          m
      }
      
      k2 <- 1:50005000
      n2 <- 10^4
      
      system.time(t1 <- Triangle1(k2,n2))
      user  system elapsed           ## previously   user  system elapsed
      2.29    0.08    2.41           ##              2.37    0.13    2.52
      
      system.time(t2 <- Triangle2(k2,n2))
      user  system elapsed 
      5.40    0.91    6.30
      
      system.time(t3 <- Triangle3(k2,n2))
      user  system elapsed 
      7.70    1.03    8.77 
      
      system.time(t4 <- triang(k2,n2))
      user  system elapsed 
      433.45    0.20  434.88
      

      让我有点费解的是,Triangle1 生成的对象的大小是所有其他解决方案的一半。

      object.size(t1)
      400000200 bytes
      
      object.size(t2)   ## it's the same for t3 and t4
      800000200 bytes
      

      当我做一些检查时,它只会变得更加混乱。

      all(sapply(1:ncol(t1), function(x) all(t1[,x]==t2[,x])))
      [1] TRUE
      
      class(t1)
      [1] "matrix"
      class(t2)
      [1] "matrix"
      
      attributes(t1)
      $dim
      [1] 10000 10000
      attributes(t2)
      $dim
      [1] 10000 10000
      
      ## not sure what's going on here
      identical(t1,t2)
      [1] FALSE
      
      identical(t2,t3)
      [1] TRUE
      

      正如@Frank 在 cmets 中指出的那样,t1 是一个整数矩阵,而其他的是数字矩阵。我应该知道这一点,因为 most important R functions 会从一开始就告诉我这些信息。

      str(t1)
      int [1:10000, 1:10000] 1 0 0 0 0 0 0 0 0 0 ...
      str(t2)
      num [1:10000, 1:10000] 1 0 0 0 0 0 0 0 0 0 ...
      

      【讨论】:

      • Triangle3 计算 k 而其他人是免费的,对吧?我不知道这需要多长时间,但这会使基准看起来不对称。
      • @Frank,对此感到抱歉。时间现在反映k 被传递给Triangle3。我会说seq(sum(seq(n))) 非常快。它在我的机器上为n = 10^4注册0.06
      • t1 较小且不相同,因为它是一个整数矩阵。
      猜你喜欢
      • 2012-05-28
      • 2013-01-03
      • 2018-03-20
      • 2020-02-18
      • 1970-01-01
      • 2010-12-22
      • 2017-02-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多