【问题标题】:Cosine similarity calculation without nested loops没有嵌套循环的余弦相似度计算
【发布时间】:2025-12-25 23:30:11
【问题描述】:

我尝试使用余弦相似度来编写相似度矩阵,并且使用了嵌套循环。我知道嵌套循环在 R 中并不总是惯用的,而且这种实现需要很长时间才能执行。

我想知道如何将此代码转换为没有嵌套循环的代码。

 cosine.sim <- function(data) 
{
        data <- t(data)
        cos.sim <- matrix (data = 1, nrow = ncol(data), ncol = ncol(data))
        for(i in 1:(ncol(data)-1))
        {
                for(j in (i+1):ncol(data))
                {
                        A <- sqrt ( sum (data[,i] ^2) )
                        B <- sqrt ( sum (data[,j] ^2) )
                        C <- sum ( data[,i] * data[,j] ) 
                        cos.sim [i,j] <- C / (A * B)
                        cos.sim [j,i] <- C / (A * B)
                }
        }
        return (cos.sim)
}

【问题讨论】:

  • R 中的循环很好,只要您预先分配所有必要的对象。您的示例不可重现。粘贴一些数据并向我们展示输出的样子。如果您用文字或伪算法描述您正在尝试实现目标的内容和方式,也会有所帮助。
  • 另外,最好避免使用函数作为变量名,例如data
  • 如果要计算相关系数,则不需要编写这样的程序,直接使用已有的即可,例如 library(Hmisc) rcorr(x, type="pearson")或检查其他类型
  • 我会将 A 计算移出嵌套循环(因为它对每个 j 给出相同的结果),并且可能测试 i 和 j 是否相同以避免设置对角线两次(但不确定这一点会更快)。
  • 您是否尝试过一个已经在完整矩阵中计算余弦距离的包?这是一个帖子,你可以找到关于它的讨论*.com/questions/2535234/find-cosine-similarity-in-r>。

标签: r


【解决方案1】:

使用低级叉积函数应该比在 R 中做同样的事情快几个数量级。

示例数据

> set.seed(1)
> (data<-matrix(runif(30),5,6))
          [,1]       [,2]      [,3]      [,4]      [,5]       [,6]
[1,] 0.2655087 0.89838968 0.2059746 0.4976992 0.9347052 0.38611409
[2,] 0.3721239 0.94467527 0.1765568 0.7176185 0.2121425 0.01339033
[3,] 0.5728534 0.66079779 0.6870228 0.9919061 0.6516738 0.38238796
[4,] 0.9082078 0.62911404 0.3841037 0.3800352 0.1255551 0.86969085
[5,] 0.2016819 0.06178627 0.7698414 0.7774452 0.2672207 0.34034900

以下是等价的

 > tcrossprod(data/sqrt(rowSums(data^2)))
          [,1]      [,2]      [,3]      [,4]      [,5]
[1,] 1.0000000 0.8193235 0.8644710 0.6829105 0.5854560
[2,] 0.8193235 1.0000000 0.8523731 0.6810237 0.5835957
[3,] 0.8644710 0.8523731 1.0000000 0.7884536 0.8815997
[4,] 0.6829105 0.6810237 0.7884536 1.0000000 0.6324778
[5,] 0.5854560 0.5835957 0.8815997 0.6324778 1.0000000

但可能比你的函数快得多

> cosine.sim(data)
          [,1]      [,2]      [,3]      [,4]      [,5]
[1,] 1.0000000 0.8193235 0.8644710 0.6829105 0.5854560
[2,] 0.8193235 1.0000000 0.8523731 0.6810237 0.5835957
[3,] 0.8644710 0.8523731 1.0000000 0.7884536 0.8815997
[4,] 0.6829105 0.6810237 0.7884536 1.0000000 0.6324778
[5,] 0.5854560 0.5835957 0.8815997 0.6324778 1.0000000

【讨论】:

  • 这些都是不错的选择。
  • @A.webb 你能解释一下为什么你做 tcrosspod(data/sqrt(rowSums(data^2))) 而不是 tcrossprod(data)/sqrt(rowsums(data^2)) 吗?
  • @alily 匹配 OP。我不认为其他选项是等效的。