【问题标题】:Efficient algorithm to turn matrix subdiagonal to columns r将矩阵下对角线转换为列 r 的有效算法
【发布时间】:2017-03-22 12:07:12
【问题描述】:

我有一个非方阵,需要对其下对角线进行一些计算。我发现最好的方法是将下对角线转换为列/行并使用 cumprod 之类的函数。现在我使用定义如下的 for 循环和 exdiag:

exdiag <- function(mat, off=0) {mat[row(mat) == col(mat)+off]}

但是它并不是真正有效的。你知道任何其他算法可以实现这种结果吗?

一个小例子来说明我在做什么:

exdiag <- function(mat, off=0) {mat[row(mat) == col(mat)+off]}
mat <- matrix(1:72, nrow = 12, ncol = 6)
newmat <- matrix(nrow=11, ncol=6)
for (i in 1:11){
   newmat[i,] <- c(cumprod(exdiag(mat,i)),rep(0,max(6-12+i,0)))
}

最好的问候, 阿图尔

【问题讨论】:

  • 为什么不把矩阵做成正方形,然后提取对角线
  • 我不太明白。你能详细说明一下吗?
  • 请您提供一个可重现的示例(包括预期输出),以便我们了解您的目标
  • 当然,我添加了一个例子来展示我在做什么

标签: r algorithm matrix transformation


【解决方案1】:

从非方阵中获取所有可能对角线的最快但迄今为止最神秘的解决方案是将矩阵视为向量并简单地构造一个id 向量以供选择。最后,您可以根据需要将其转换回矩阵。

以下函数可以做到这一点:

exdiag <- function(mat){

  NR <- nrow(mat)
  NC <- ncol(mat)
  smalldim <- min(NC,NR)
  if(NC > NR){
    id <- seq_len(NR) + 
      seq.int(0,NR-1)*NR +
      rep(seq.int(1,NC - 1), each = NR)*NR


  } else if(NC < NR){
    id <- seq_len(NC) + 
      seq.int(0,NC-1)*NR +
      rep(seq.int(1,NR - 1), each = NC)

  } else {
    return(diag(mat))
  }
  out <- matrix(mat[id],nrow = smalldim)
  id <- (ncol(out) + 1 - row(out)) - col(out) < 0
  out[id] <- NA
  return(out)
}

请记住,您必须考虑矩阵的形成方式。

在这两种情况下,我都遵循相同的逻辑:

  • 首先构造一个序列,指示沿最小维度的位置
  • 在此序列中,添加 0、1、2、... 乘以行长。

这将创建第一个对角线。完成此操作后,您只需添加一个序列,将整个前一个序列移动 1(向下或向右),直到到达矩阵的末尾。要右移,我需要将此序列乘以行数。

最后,您可以使用这些索引从 mat 中选择正确的位置,并将所有这些作为矩阵返回。由于此代码的矢量化特性,您必须检查最后的下对角线是否正确。这些包含的元素比第一个少,因此您必须将不属于该次对角线的值替换为 NA。同样在这里,您可以简单地使用索引技巧。

您可以按如下方式使用它:

> diag1 <- exdiag(amatrix)
> diag2 <- exdiag(t(amatrix))
> identical(diag1, diag2)
[1] TRUE

为了得出你的结果

amatrix <- matrix(1:72, ncol = 6)
diag1 <- exdiag(amatrix)
res <- apply(diag1,2,cumprod)
res[is.na(res)] <- 0
t(res)

【讨论】:

  • 感谢您的想法,我会尽力解决我的问题。根据维基百科,子对角线是元素 Aij,它遵循规则 j=i+k 和 k > 0。因此,我忽略了矩阵的上三角部分(包括对角线),而且我也对每个都没有元素的元素感兴趣列(所以接近矩阵末尾的列)。
  • @Swistak 我已经添加了您需要的代码,以便根据子对角线的定义重现您的结果。要获得完全相同的结果,您必须转置最终结果。否则,您可以在 exdiag 函数中逐行填充矩阵,但是您必须检查 NA 添加。
  • 谢谢,该代码比我以前使用的代码快 10 倍!
【解决方案2】:

您可以修改 diag() 函数。

exdiag <- function(mat, off=0) {mat[row(mat) == col(mat)+off]}

exdiag2 <- function(matrix, off){diag(matrix[-1:-off,])}

速度测试:

mat = diag(10, 10000,10000)

off = 4

> system.time(exdiag(mat,4))

  user  system elapsed 
  7.083   2.973  10.054 

> system.time(exdiag2(mat,4))

   user  system elapsed 
  5.370   0.155   5.524 

> system.time(diag(mat))

   user  system elapsed 
  0.002   0.000   0.002

看起来矩阵的子集需要很多时间,但它仍然比您的实现更好。可能还有很多其他子集方法,它们优于我的解决方案。 :)

【讨论】:

  • R 中的 Diag 只允许取对角线,我想提取子对角线。我一直在寻找类似于 matlab 中 diag 的函数,但似乎没有这样的函数,我真的不知道如何自己有效地编写它。
  • 使用子集修改 diag() 函数:步骤 1. 取矩阵的子集,2. 使用使用 diag()
  • 不幸的是,当您添加列子集时, diag 的性能比我以前的函数差。我试了一下:exdiag2 &lt;- function(mat, off=0) { n = ncol(mat) ifelse(off==0,diag(mat),diag(mat[-(1:off),-((n-off+1):n)])) }
  • 嗨@Swistak,我更新了我的解决方案,请检查一下。
  • 嗨@xirururu,不幸的是它对我的机器没有帮助:(&gt; time &lt;- proc.time() &gt; t1 &lt;- exdiag(tryryry, 4) &gt; time2 &lt;- proc.time() &gt; t2 &lt;- exdiag2(tryryry,4) &gt; time3 &lt;- proc.time() &gt; time2 - time user system elapsed 2.64 0.97 3.93 &gt; time3 - time2 user system elapsed 3.70 0.25 4.09
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 2019-04-21
  • 1970-01-01
  • 2017-06-17
  • 2016-10-03
  • 2022-11-29
相关资源
最近更新 更多