我正在尝试从R中的下三角矩阵创建对称矩阵。
在先前的问与答(Convert upper triangular part of a matrix to symmetric matrix)用户李哲源中,对于大型矩阵,这不应该在R中完成,而在C中提出解决方案。但是我不理解C和以前从未使用过Rccp
这样的例子,所以不知道如何解释答案。但是很明显,那里的C代码会生成我不想要的随机数(rnorm
)。 我想放入一个正方形矩阵并得出一个对称矩阵。
对于给定的方阵A
,其下部三角形中有数据,我如何在C中高效创建对称矩阵并在R中使用对称矩阵?
答案 0 :(得分:5)
快速适应as.matrix on a distance object is extremely slow; how to make it faster?中的dist2mat
功能。
library(Rcpp)
cppFunction('NumericMatrix Mat2Sym(NumericMatrix A, bool up2lo, int bf) {
IntegerVector dim = A.attr("dim");
size_t n = (size_t)dim[0], m = (size_t)dim[1];
if (n != m) stop("A is not a square matrix!");
/* use pointers */
size_t j, i, jj, ni, nj;
double *A_jj, *A_ij, *A_ji, *col, *row, *end;
/* cache blocking factor */
size_t b = (size_t)bf;
/* copy lower triangular to upper triangular; cache blocking applied */
for (j = 0; j < n; j += b) {
nj = n - j; if (nj > b) nj = b;
/* diagonal block has size nj x nj */
A_jj = &A(j, j);
for (jj = nj - 1; jj > 0; jj--, A_jj += n + 1) {
/* copy a column segment to a row segment (or vise versa) */
col = A_jj + 1; row = A_jj + n;
for (end = col + jj; col < end; col++, row += n) {
if (up2lo) *col = *row; else *row = *col;
}
}
/* off-diagonal blocks */
for (i = j + nj; i < n; i += b) {
ni = n - i; if (ni > b) ni = b;
/* off-diagonal block has size ni x nj */
A_ij = &A(i, j); A_ji = &A(j, i);
for (jj = 0; jj < nj; jj++) {
/* copy a column segment to a row segment (or vise versa) */
col = A_ij + jj * n; row = A_ji + jj;
for (end = col + ni; col < end; col++, row += n) {
if (up2lo) *col = *row; else *row = *col;
}
}
}
}
return A;
}')
对于方阵A
,此函数Mat2Sym
将其下三角部分(带有换位)复制到其上三角部分,以使其在up2lo = FALSE
下对称,而在{ {1}}。
请注意,该功能覆盖 up2lo = TRUE
,而不会占用额外的内存。要保留输入矩阵并创建新的输出矩阵,请将A
而不是A + 0
传递到函数中。
A
## an arbitrary asymmetric square matrix
set.seed(0)
A <- matrix(runif(25), 5)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.2655087 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.3721239 0.9446753 0.17655675 0.7176185 0.2121425
#[4,] 0.5728534 0.6607978 0.68702285 0.9919061 0.6516738
#[5,] 0.9082078 0.6291140 0.38410372 0.3800352 0.1255551
## lower triangular to upper triangular; don't overwrite
B <- Mat2Sym(A + 0, up2lo = FALSE, 128)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2655087 0.3721239 0.5728534 0.9082078
#[2,] 0.2655087 0.8983897 0.9446753 0.6607978 0.6291140
#[3,] 0.3721239 0.9446753 0.1765568 0.6870228 0.3841037
#[4,] 0.5728534 0.6607978 0.6870228 0.9919061 0.3800352
#[5,] 0.9082078 0.6291140 0.3841037 0.3800352 0.1255551
## A is unchanged
A
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.2655087 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.3721239 0.9446753 0.17655675 0.7176185 0.2121425
#[4,] 0.5728534 0.6607978 0.68702285 0.9919061 0.6516738
#[5,] 0.9082078 0.6291140 0.38410372 0.3800352 0.1255551
使用## upper triangular to lower triangular; overwrite
D <- Mat2Sym(A, up2lo = TRUE, 128)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.89669720 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.20168193 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.06178627 0.2059746 0.17655675 0.7176185 0.2121425
#[4,] 0.76984142 0.4976992 0.71761851 0.9919061 0.6516738
#[5,] 0.77744522 0.9347052 0.21214252 0.6516738 0.1255551
## A has been changed; D and A are aliased in memory
A
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.89669720 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.20168193 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.06178627 0.2059746 0.17655675 0.7176185 0.2121425
#[4,] 0.76984142 0.4976992 0.71761851 0.9919061 0.6516738
#[5,] 0.77744522 0.9347052 0.21214252 0.6516738 0.1255551
软件包
Matrix
对于稀疏矩阵特别有用。为了兼容性,它还为密集矩阵定义了一些类,例如“ dgeMatrix”,“ dtrMatrix”,“ dtpMatrix”,“ dsyMatrix”,“ dspMatrix”。
鉴于正方形Matrix
,使A
对称的方法如下。
Matrix
set.seed(0)
A <- matrix(runif(25), 5)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.2655087 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.3721239 0.9446753 0.17655675 0.7176185 0.2121425
#[4,] 0.5728534 0.6607978 0.68702285 0.9919061 0.6516738
#[5,] 0.9082078 0.6291140 0.38410372 0.3800352 0.1255551
## equivalent to: Mat2Sym(A + 0, TRUE, 128)
new("dsyMatrix", x = base::c(A), Dim = dim(A), uplo = "U")
#5 x 5 Matrix of class "dsyMatrix"
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.89669720 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.20168193 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.06178627 0.2059746 0.17655675 0.7176185 0.2121425
#[4,] 0.76984142 0.4976992 0.71761851 0.9919061 0.6516738
#[5,] 0.77744522 0.9347052 0.21214252 0.6516738 0.1255551
## equivalent to: Mat2Sym(A + 0, FALSE, 128)
new("dsyMatrix", x = base::c(A), Dim = dim(A), uplo = "L")
#5 x 5 Matrix of class "dsyMatrix"
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2655087 0.3721239 0.5728534 0.9082078
#[2,] 0.2655087 0.8983897 0.9446753 0.6607978 0.6291140
#[3,] 0.3721239 0.9446753 0.1765568 0.6870228 0.3841037
#[4,] 0.5728534 0.6607978 0.6870228 0.9919061 0.3800352
#[5,] 0.9082078 0.6291140 0.3841037 0.3800352 0.1255551
方法不理想的原因有三个:
Matrix
槽作为数值矢量,因此我们必须做x
,这实际上在RAM中创建了矩阵的副本; 这里是一个快速的比较:
base::c(A)
请注意library(bench)
A <- matrix(runif(5000 * 5000), 5000)
bench::mark("Mat2Sym" = Mat2Sym(A, FALSE, 128),
"Matrix" = new("dsyMatrix", x = base::c(A), Dim = dim(A), uplo = "L"),
check = FALSE)
# expression min mean median max `itr/sec` mem_alloc n_gc n_itr
# <chr> <bch:tm> <bch:tm> <bch:tm> <bch:t> <dbl> <bch:byt> <dbl> <int>
#1 Mat2Sym 56.8ms 57.7ms 57.4ms 59.4ms 17.3 2.48KB 0 9
#2 Matrix 334.3ms 337.4ms 337.4ms 340.6ms 2.96 190.74MB 2 2
有多快。同样,在“覆盖”模式下不会进行内存分配。
As G. Grothendieck mentions,我们也可以使用“ dspMatrix”。
Mat2Sym
同样,由于使用set.seed(0)
A <- matrix(runif(25), 5)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.2655087 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.3721239 0.9446753 0.17655675 0.7176185 0.2121425
#[4,] 0.5728534 0.6607978 0.68702285 0.9919061 0.6516738
#[5,] 0.9082078 0.6291140 0.38410372 0.3800352 0.1255551
## equivalent to: Mat2Sym(A + 0, TRUE, 128)
new("dspMatrix", x = A[upper.tri(A, TRUE)], Dim = dim(A), uplo = "U")
#5 x 5 Matrix of class "dspMatrix"
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.89669720 0.2016819 0.06178627 0.7698414 0.7774452
#[2,] 0.20168193 0.8983897 0.20597457 0.4976992 0.9347052
#[3,] 0.06178627 0.2059746 0.17655675 0.7176185 0.2121425
#[4,] 0.76984142 0.4976992 0.71761851 0.9919061 0.6516738
#[5,] 0.77744522 0.9347052 0.21214252 0.6516738 0.1255551
## equivalent to: Mat2Sym(A + 0, FALSE, 128)
new("dspMatrix", x = A[lower.tri(A, TRUE)], Dim = dim(A), uplo = "L")
#5 x 5 Matrix of class "dspMatrix"
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.8966972 0.2655087 0.3721239 0.5728534 0.9082078
#[2,] 0.2655087 0.8983897 0.9446753 0.6607978 0.6291140
#[3,] 0.3721239 0.9446753 0.1765568 0.6870228 0.3841037
#[4,] 0.5728534 0.6607978 0.6870228 0.9919061 0.3800352
#[5,] 0.9082078 0.6291140 0.3841037 0.3800352 0.1255551
或Matrix
,upper.tri
是次优的方法。
lower.tri
特别是,我们看到使用“ dspMatrix”的效率甚至比使用“ dsyMatrix”的效率低。
答案 1 :(得分:2)
在使用C / C ++进行可能的过早优化之前,请检查是否有密集矩阵
A + t(A)
就足够了(假设A的唯一非零元素在对角线以下或对角线上方。
此外,如果存在内存问题,那么Matrix程序包具有打包的对称类dspMatrix
,可以这样创建:
library(Matrix)
A <- matrix(c(0, 2, 3, 0, 0, 4, 0, 0, 0), 3) # dense lower triangular test input
dspA <- as(A + t(A), "dspMatrix")
给予:
> str(dspA)
Formal class 'dspMatrix' [package "Matrix"] with 5 slots
..@ x : num [1:6] 0 2 0 3 4 0 <- only 6 elements stored, not 9
..@ Dim : int [1:2] 3 3
..@ Dimnames:List of 2
.. ..$ : NULL
.. ..$ : NULL
..@ uplo : chr "U"
..@ factors : list()
或者可以直接从上三角部分创建它:
# use upper triangular part since we already created dspA that way
tA <- t(A)
dspA2 <- new("dspMatrix", Dim = as.integer(c(3,3)),
x = tA[upper.tri(tA, diag = TRUE)])
identical(dspA, dspA2)
## [1] TRUE