使用矩阵乘法计算二进制数据的Jaccard或其他关联系数


9

我想知道是否有任何可能的方法可以使用矩阵乘法来计算Jaccard系数。

我用了这段代码

    jaccard_sim <- function(x) {
    # initialize similarity matrix
    m <- matrix(NA, nrow=ncol(x),ncol=ncol(x),dimnames=list(colnames(x),colnames(x)))
    jaccard <- as.data.frame(m)

    for(i in 1:ncol(x)) {
     for(j in i:ncol(x)) {
        jaccard[i,j]= length(which(x[,i] & x[,j])) / length(which(x[,i] | x[,j]))
        jaccard[j,i]=jaccard[i,j]        
       }
     }

在R中实现这一点是完全可以的。我完成了骰子的相似性,但是被Tanimoto / Jaccard所卡住。有人可以帮忙吗?


看起来@ttnphns涵盖了这一点,但是由于您使用的是R,我想我还要指出,该vegan软件包中已经实现了许多相似性索引(包括Jaccard)。我认为它们在速度上也趋于很好地优化。
David J. Harris,

Answers:


11

我们知道,Jaccard(在二进制数据任意两之间计算)是,而Rogers-Tanimoto是,其中Xaa+b+ca+da+d+2(b+c)

  • a-两列均为1的行数
  • b-此行而不是另一列为1的行数
  • c-另一个而不是此列为1的行数
  • d-两列均为0的行数

a+b+c+d=n,的行数X

然后我们有:

XX=A是所有列之间的的平方对称矩阵。a

(notX)(notX)=D是所有列之间的平方对称矩阵(“非X”在X中转换1-> 0和0-> 1)。d

因此,是所有列之间Jaccard的平方对称矩阵。AnD

A+DA+D+2(n(A+D))=A+D2nAD是...的平方对称矩阵所有列之间的Rogers-Tanimoto。

我用数字检查了这些公式是否给出正确的结果。他们是这样。


更新。您还可以获取矩阵和:BC

B=[1]XA,其中“[1]”个位的表示矩阵,大小为。是所有列之间的平方不对称矩阵;它的元素ij是的行数,第i 0,第j 1 。XBbX

因此,。C=B

矩阵当然也可以用这种方法计算:。DnABC

知道矩阵,您就能计算出为二进制数据发明的任何成对(非)相似系数的矩阵。A,B,C,D


除非通勤,分数对矩阵毫无意义:在右边乘以一个逆数将得到与在左边相乘不同的结果。此外,两个对称矩阵的乘积通常不是对称的。您可能是指按组件划分吗?您能否修正符号以反映您的意图是正确的公式?
Whuber

@whuber我不使用平方对称矩阵的求逆或乘法。X是二进制数据矩阵,而X'X是其SSCP矩阵。not X是X,其中1-> 0,0-> 1。而且这里的任何划分都是元素划分。如果您认为不合适,请更正我的注释。
ttnphns

如何计算R中的内积(notX)′(notX)?
2013年

@ user4959,我不知道R。这里推荐X!但是结果是布尔值TRUE / FALSE,而不是数字1/0。请注意,我更新了答案,我说还有另一种方法可以得出D矩阵。
ttnphns

9

如果X稀疏,上述解决方案不是很好。因为采用!X会形成密集的矩阵,因此会占用大量的内存和计算量。

更好的解决方案是使用公式Jaccard [i,j] = #common /(#i + #j-#common)。使用稀疏矩阵,您可以按以下方式进行操作(请注意,代码也适用于非稀疏矩阵):

library(Matrix)
jaccard <- function(m) {
    ## common values:
    A = tcrossprod(m)
    ## indexes for non-zero common values
    im = which(A > 0, arr.ind=TRUE)
    ## counts for each row
    b = rowSums(m)

    ## only non-zero values of common
    Aim = A[im]

    ## Jacard formula: #common / (#i + #j - #common)
    J = sparseMatrix(
          i = im[,1],
          j = im[,2],
          x = Aim / (b[im[,1]] + b[im[,2]] - Aim),
          dims = dim(A)
    )

    return( J )
}

1

根据您的需求,这可能对您有用或无效。假设您对集群分配之间的相似性感兴趣:

雅卡德相似系数或雅卡德索引可用于计算两个聚类分配的相似度。

给定标签L1L2Ben-Hur,Elisseeff和Guyon(2002)表明,可以使用中间矩阵的点积来计算Jaccard指数。下面的代码利用此代码快速计算Jaccard索引,而不必将中间矩阵存储在内存中。

该代码用C ++编写,但是可以使用sourceCpp命令加载到R中。

/**
 * The Jaccard Similarity Coefficient or Jaccard Index is used to compare the
 * similarity/diversity of sample sets. It is defined as the size of the
 * intersection of the sets divided by the size of the union of the sets. Here,
 * it is used to determine how similar to clustering assignments are.
 *
 * INPUTS:
 *    L1: A list. Each element of the list is a number indicating the cluster
 *        assignment of that number.
 *    L2: The same as L1. Must be the same length as L1.
 *
 * RETURNS:
 *    The Jaccard Similarity Index
 *
 * SIDE-EFFECTS:
 *    None
 *
 * COMPLEXITY:
 *    Time:  O(K^2+n), where K = number of clusters
 *    Space: O(K^2)
 *
 * SOURCES:
 *    Asa Ben-Hur, Andre Elisseeff, and Isabelle Guyon (2001) A stability based
 *    method for discovering structure in clustered data. Biocomputing 2002: pp.
 *    6-17. 
 */
// [[Rcpp::export]]
NumericVector JaccardIndex(const NumericVector L1, const NumericVector L2){
  int n = L1.size();
  int K = max(L1);

  int overlaps[K][K];
  int cluster_sizes1[K], cluster_sizes2[K];

  for(int i = 0; i < K; i++){    // We can use NumericMatrix (default 0) 
    cluster_sizes1[i] = 0;
    cluster_sizes2[i] = 0;
    for(int j = 0; j < K; j++)
      overlaps[i][j] = 0;
  }

  //O(n) time. O(K^2) space. Determine the size of each cluster as well as the
  //size of the overlaps between the clusters.
  for(int i = 0; i < n; i++){
    cluster_sizes1[(int)L1[i] - 1]++; // -1's account for zero-based indexing
    cluster_sizes2[(int)L2[i] - 1]++;
    overlaps[(int)L1[i] - 1][(int)L2[i] - 1]++;
  }

  // O(K^2) time. O(1) space. Square the overlap values.
  int C1dotC2 = 0;
  for(int j = 0; j < K; j++){
    for(int k = 0; k < K; k++){
      C1dotC2 += pow(overlaps[j][k], 2);
    }
  }

  // O(K) time. O(1) space. Square the cluster sizes
  int C1dotC1 = 0, C2dotC2 = 0;
  for(int i = 0; i < K; i++){
    C1dotC1 += pow(cluster_sizes1[i], 2);
    C2dotC2 += pow(cluster_sizes2[i], 2);
  }

  return NumericVector::create((double)C1dotC2/(double)(C1dotC1+C2dotC2-C1dotC2));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.