R:将矩阵的上三角部分转换为对称矩阵

时间:2016-06-03 11:35:21

标签: r matrix

我在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?

1 个答案:

答案 0 :(得分:5)

我认为这里有两个问题。

  

现在我想使用非常大的矩阵

然后不要使用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进行寻址将导致性能下降。但我相信你可以在内循环之外移动乘法,只留下加法。