行列乗算を使用してバイナリデータの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で実装しても問題ありません。ダイスの類似性を1つ作成しましたが、谷本/ Jaccardで行き詰まりました。誰でも手伝ってくれる?


@ttnphnsでこれがカバーされているように見えますが、Rを使用しているため、多くの類似性インデックス(Jaccardを含む)がすでにveganパッケージに実装されていることも指摘しておきたいと思います。速度についてもかなり最適化されている傾向があると思います。
デビッドJ.ハリス

回答:


11

Jaccard(バイナリデータXの任意の2つの間で計算される)はバツ、ロジャース-タニモトはa+daa+b+c、ここでa+da+d+2b+c

  • a-両方の列が1である行の数
  • b-これが他の列ではなく1である行の数
  • c-この列ではなく他の列が1である行の数
  • d-両方の列が0である行の数

Xの行数a+b+c+d=バツ

それから私達は持っています:

の正方対称行列であり、全ての列の間です。バツバツ=a

は、すべての列間の dの正方対称行列です(「not X」は、Xで1-> 0および0-> 1に変換されます)。otバツotバツ=Dd

だから、は、すべての列間のJaccardの正方対称行列です。D

は、すべての列間のRogers-Tanimotoの正方対称行列です。+D+D+2+D=+D2D

これらの式が正しい結果を与えるかどうかを数値で確認しました。彼らはします。


更新。行列およびCも取得できます。BC

"[1]"を意味マトリックスとして大きさのもの、の、 XBは、すべての列間の bの正方非対称行列です。その要素ij Xの行数で、列iは0、列jは1です。B=[1]バツバツBbバツ

したがって、です。C=B

行列また、もちろん、この方法で計算することができる:N - A - B - CDBC

行列が分かれば、バイナリデータ用に発明されたペアワイズ(非)相似係数の行列を計算できます。BCD


逆転しない限り、分数は行列にとって意味がありません。右で逆数を乗算すると、左で乗算する場合とは異なる結果が得られます。さらに、通常、2つの対称行列の積が対称であるとは限りません。おそらく、コンポーネントごとの分割を意味しますか?表記を修正して、意図したものを正しい式に反映できますか?
whuber

@whuber 正方対称行列の反転も乗算も使用しません。Xはバイナリデータマトリックスで、X'XはSSCPマトリックスです。not XXで、1-> 0、0-> 1です。そして、ここでの分割は要素ごとの分割です。適切でないと思われる場合は、表記を修正してください。
ttnphns 2013

Rの内積(notX) ′(notX)を計算する方法は?
user4959 2013

@ user4959、Rはわかりません。ここでは!Xをお勧めします。ただし、結果はブール値のTRUE / FALSEであり、数値の1/0ではありません。Dマトリックスに到達する別の方法もあると言って、私の回答を更新したことに注意してください。
ttnphns 2013

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

これは、ニーズが何であるかに応じて、役立つ場合とそうでない場合があります。クラスタリング割り当て間の類似性に興味があると仮定します。

Jaccard類似度係数またはJaccardインデックスを使用して、2つのクラスタリング割り当ての類似度を計算できます。

ラベル付けL1とを考慮してL2Ben-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));
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.