【问题标题】:R: Convert upper triangular part of a matrix to symmetric matrixR:将矩阵的上三角部分转换为对称矩阵
【发布时间】:2016-10-03 11:01:07
【问题描述】:

我在 R 中有矩阵的上三角部分(没有对角线),并且想从上三角部分生成一个对称矩阵(对角线上有 1,但可以稍后调整)。我通常这样做:

res.upper <- rnorm(4950)
res <- matrix(0, 100, 100)
res[upper.tri(res)] <- res.upper
rm(res.upper)
diag(res) <- 1
res[lower.tri(res)]  <- t(res)[lower.tri(res)]

这很好用,但现在我想处理非常大的矩阵。因此,我希望避免同时存储 res.upper 和 res(填充为 0)。有什么办法可以直接将 res.upper 转换为对称矩阵,而不必先初始化矩阵 res?

【问题讨论】:

  • 我可以编写编译后的代码,有时我也会这样做以加快我的功能。但是,我真的不明白如何避免使用额外的内存。在 C/C++ 代码中,我还会先初始化一个像上面的 res 一样的对象。那不会也使用额外的内存吗?或者在使用 C/C++ 时这不是问题,因为这些语言中的内存分配更“智能”?这可能是个愚蠢的问题,但我是统计学家而不是计算机科学家,所以我真的不知道内存分配在内部是如何工作的。
  • 你不必为我编写函数,这不是问题。我熟悉编译和内联包。我只是没有足够的背景来理解这如何解决我的记忆问题。但是,如果您向我保证确实如此,我会编写该函数(如果您将其作为答案而不是评论给出,我会接受您的答案)。
  • 您是否尝试过使用bigmemory 包中的big.matrix?可能是一种解决您的内存限制的方法
  • 无论如何,最终的矩阵都必须从 kernlab 库中转换为 kernelMatrix 类。我不知道这是否适用于 big.matrix 但我一定会看看它!谢谢你的提示!我不知道那个包,这是我第一次使用这么大的矩阵。
  • 您还可以找到有用的“Matrix”包——即sparseMatrix(i = sequence(1:99), j = rep(2:100, 1:99), x = res.upper, symmetric = TRUE, dims = c(100, 100)),以避免多次复制和使用(可能)较小的对象

标签: r matrix


【解决方案1】:

我认为这里有两个问题。

现在我想处理非常大的矩阵

那么不要使用 R 代码来完成这项工作。 R 将使用比您预期更多的内存。试试下面的代码:

res.upper <- rnorm(4950)
res <- matrix(0, 100, 100)
tracemem(res)  ## trace memory copies of `res`
res[upper.tri(res)] <- res.upper
rm(res.upper)
diag(res) <- 1
res[lower.tri(res)]  <- t(res)[lower.tri(res)]

这是你将得到的:

> res.upper <- rnorm(4950)  ## allocation of length 4950 vector
> res <- matrix(0, 100, 100)  ## allocation of 100 * 100 matrix
> tracemem(res)
[1] "<0xc9e6c10>"
> res[upper.tri(res)] <- res.upper
tracemem[0xc9e6c10 -> 0xdb7bcf8]: ## allocation of 100 * 100 matrix
> rm(res.upper)
> diag(res) <- 1
tracemem[0xdb7bcf8 -> 0xdace438]: diag<-  ## allocation of 100 * 100 matrix
> res[lower.tri(res)]  <- t(res)[lower.tri(res)]
tracemem[0xdace438 -> 0xdb261d0]: ## allocation of 100 * 100 matrix
tracemem[0xdb261d0 -> 0xccc34d0]: ## allocation of 100 * 100 matrix

在 R 中,您必须使用 5 * (100 * 100) + 4950 双字来完成这些操作。而在 C 语言中,您最多只需要 4950 + 100 * 100 双字(事实上,100 * 100 就足够了!稍后会谈到它)。在没有额外的内存分配的情况下,很难直接在 R 中覆盖对象。

有什么方法可以直接将res.upper 转换为对称矩阵,而无需先初始化矩阵res?

您必须为res 分配内存,因为这就是您最终得到的;但无需为res.upper 分配内存。 可以初始化上三角,同时填充下三角。考虑如下模板:

#include <Rmath.h>  // use: double rnorm(double a, double b)
#include <R.h>  // use: getRNGstate() and putRNGstate() for randomness
#include <Rinternals.h>  // SEXP data type

## N is matrix dimension, a length-1 integer vector in R
## this function returns the matrix you want
SEXP foo(SEXP N) {
  int i, j, n = asInteger(N);
  SEXP R_res = PROTECT(allocVector(REALSXP, n * n));  // allocate memory for `R_res`
  double *res = REAL(R_res);
  double tmp;  // a local variable for register reuse
  getRNGstate();
  for (i = 0; i < n; i++) {
    res[i * n + i] = 1.0;  // diagonal is 1, as you want
    for (j = i + 1; j < n; j++) {
      tmp = rnorm(0, 1);  
      res[j * n + i] = tmp; // initialize upper triangular
      res[i * n + j] = tmp;  // fill lower triangular
      }
    }
  putRNGstate();
  UNPROTECT(1);
  return R_res;
  }

代码尚未优化,因为在最内层循环中使用整数乘法j * n + i 进行寻址会导致性能下降。但我相信您可以将乘法移到内部循环之外,而只将加法留在内部。

【讨论】:

  • 感谢您的解释!我接受了你的回答。我会听从你的建议,然后用 C 或 C++ 编写那部分。对于其他读者:Zheyuan Li 建议使用编译后的代码(通过 R 中的 inline 包),我请他解释这如何解决我的内存问题。
  • 请注意——在你的第二个例子中(x = 1:4...),'x' 必须被转换,无论哪种方式,因为将“双”分配给一个“整数”;即x[1] = 4L 不应复制
  • 我猜想y = x 或模拟函数调用(function(val) val)(x) 应该在随后调用"[&lt;-" 时将“x”标记为要复制
  • 感谢您的模板!
猜你喜欢
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 2022-11-29
相关资源
最近更新 更多