下三角矩阵和指数变换
的距离矩阵是打包格式的下三角矩阵,其中,所述下三角被存储作为由列一维向量的盒装存储。您可以通过
str(distMatrix)
# Class 'dist' atomic [1:10] 1 4 10 15 3 9 14 6 11 5
# ...
请注意,即使我们称之为dist(vec1, diag = TRUE, upper = TRUE)
,结果还是一样,只是将印刷风格的变化来检查。总之,无论您如何拨打dist
,您总是会获得一维数组。
假设一个完整的下三角是n-by-n
,那么它的第(i,j)
个元素将被映射到打包的1D数组中的第(j - 1) * (2 * n - 2 - j)/2 + (i - 1)
个元素。我们可以定义一个指数变换函数:
## `i` and `j` can both be vector input, but they must have the same length
f <- function (i, j, n) {
ifelse((i > j) & (j <= n), (j - 1) * (2 * n - 2 - j)/2 + (i - 1), NA_real_)
}
在另一方面,如果我们知道包装的数组中元素的位置,说k
,我们可以通过一个稍微复杂的功能找到(i,j)
:
## `k` can be a vector input
finv <- function (k, n) {
## starting position for each column
ptr_all_cols <- f(2:n, 1:(n - 1), n)
## maximum valid `k`
k_max <- n * (n - 1)/2
## `finv` operation on a scalar `k`
scaler_finv <- function (k) {
if (k > k_max) return(c(i = NA_real_, j = NA_real_))
j <- sum(ptr_all_cols <= k) ## get column index j
i <- k - ptr_all_cols[j] + j + 1 ## get row index i
c(i = i, j = j)
}
## "vectorization"
do.call(rbind, lapply(k, scaler_finv))
}
这些转换函数在内存使用上非常便宜,因为它们使用索引而不是矩阵。
基于变换函数finv
随着finv
有效的解决方案,它是晚饭有效地找到所需的元素。对于你的玩具例如,你可以使用
## the first `5` is the value to be matched; the second is matrix dimension
finv(which(distMatrix == 5), 5)
# i j
#[1,] 5 4
注意
一般来说,距离矩阵包含浮点数。使用==
来判断两个浮点数是否相等是相当危险的。阅读Why are these numbers not equal?了解更多和可能的策略。
替代
有由@RHertel提出一个方便的答案。那些拥有10,000声誉仍然能够看到它:
mat <- stats:::as.matrix.dist(dist(vec1)) * lower.tri(diag(vec1))
which(mat == 5, arr.ind = TRUE)
另一种方式把第一行是
mat <- matrix(0, n, n); mat[lower.tri(mat)] <- distMatrix
无论哪种方式,将花费更多的内存矩阵过程中存储了许多n-by-n
矩阵操作(虽然后者相对便宜)。当vec1
很长时,内存问题可能是一个瓶颈。
其它
f
和finv
可能是广义上非常有用的功能,至少它可以帮助理解全格式和压缩格式之间的指标怎么可以相互转化。
以下两个函数仅用于演示目的,它还检查f
和finv
的正确性。
## a function to verbose `f` transform, primarily used to check the correctness of `f`
verbose_f <- function (n) {
i <- rep(seq_len(n), times = n)
j <- rep(seq_len(n), each = n)
matrix(f(i, j, n), n)
}
## a function to verbose `finv` transform, primarily used to check the correctness of `finv`
verbose_finv <- function (k, n) cbind(k = k, finv(k, n))
我们以n = 5
为例。
verbose_f(5)
# [,1] [,2] [,3] [,4] [,5]
#[1,] NA NA NA NA NA
#[2,] 1 NA NA NA NA
#[3,] 2 5 NA NA NA
#[4,] 3 6 8 NA NA
#[5,] 4 7 9 10 NA
verbose_finv(1:15,5)
# k i j
# [1,] 1 2 1
# [2,] 2 3 1
# [3,] 3 4 1
# [4,] 4 5 1
# [5,] 5 3 2
# [6,] 6 4 2
# [7,] 7 5 2
# [8,] 8 4 3
# [9,] 9 5 3
#[10,] 10 5 4
#[11,] 11 NA NA
#[12,] 12 NA NA
#[13,] 13 NA NA
#[14,] 14 NA NA
#[15,] 15 NA NA
在这两种情况下,NA
暗示 “下标越界”。
您可以通过点击旁边的复选标记,选择以下最能解决您的问题的答案(假设他们中的任何一个都可以)标记为“已接受”。这对未来访问者来说可能是一个有用的指标。 – Frank