Menghitung Jaccard atau koefisien asosiasi lainnya untuk data biner menggunakan perkalian matriks

9

Saya ingin tahu apakah ada cara yang mungkin untuk menghitung koefisien Jaccard menggunakan perkalian matriks.

Saya menggunakan kode ini

    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]        
       }
     }

Ini cukup ok untuk diterapkan di R. Saya telah melakukan kemiripan dadu, tetapi terjebak dengan Tanimoto / Jaccard. Adakah yang bisa membantu?

pengguna4959
sumber
Sepertinya @ttnphns sudah membahas hal ini, tetapi karena Anda menggunakan R, saya pikir saya juga akan menunjukkan bahwa sejumlah indeks kesamaan (termasuk Jaccard) sudah diterapkan dalam veganpaket. Saya pikir mereka cenderung dioptimalkan dengan cukup baik untuk kecepatan juga.
David J. Harris

Jawaban:

11

Kita tahu bahwa Jaccard (dihitung antara dua kolom data biner mana pun ) adalah , sedangkan Rogers-Tanimoto adalah , di manaXaa+b+ca+da+d+2(b+c)

  • a - jumlah baris di mana kedua kolom adalah 1
  • b - jumlah baris di mana ini dan bukan kolom lainnya adalah 1
  • c - jumlah baris di mana yang lain dan bukan kolom ini adalah 1
  • d - jumlah baris di mana kedua kolom adalah 0

a+b+c+d=n , jumlah baris dalamX

Maka kita memiliki:

XX=A adalah matriks simetris persegi antara semua kolom.a

(notX)(notX)=D adalah matriks simetris kuadrat dari antara semua kolom ("bukan X" mengonversi 1-> 0 dan 0-> 1 dalam X).d

Jadi, adalah matriks simetris persegi dari Jaccard antara semua kolom.AnD

A+DA+D+2(n(A+D))=A+D2nAD adalah matriks simetris persegi dari Rogers-Tanimoto di antara semua kolom.

Saya memeriksa secara numerik apakah formula ini memberikan hasil yang benar. Mereka melakukannya.


Pembaruan. Anda juga dapat memperoleh matriks dan :BC

B=[1]XA , di mana "[1]" menunjukkan matriks yang, berukuran sebagai . adalah matriks asimetris kuadrat dari antara semua kolom; elemen ij adalah jumlah baris dalam dengan 0 di kolom i dan 1 di kolom j .XBbX

Akibatnya, .C=B

Matriks juga dapat dihitung dengan cara ini, tentu saja: .DnABC

Mengetahui matriks , Anda dapat menghitung matriks dari setiap koefisien kemiripan berpasangan (dis) yang ditemukan untuk data biner.A,B,C,D

ttnphns
sumber
Pecahan tidak masuk akal untuk matriks kecuali mereka bepergian: mengalikan di sebelah kanan dengan invers sebaliknya akan memberikan hasil yang berbeda dari mengalikan di sebelah kiri. Selain itu, biasanya tidak terjadi bahwa produk dari dua matriks simetris adalah simetris. Apakah Anda mungkin berarti pembagian komponen demi komponen? Bisakah Anda memperbaiki notasi Anda untuk mencerminkan apa yang Anda inginkan adalah formula yang benar?
whuber
@whuber saya tidak menggunakan inversi atau perkalian dari matriks simetris persegi . X adalah matriks data biner dan X'X adalah matriks SSCP-nya. not Xadalah X di mana 1-> 0, 0-> 1. Dan setiap divisi di sini adalah divisi elementwise. Harap perbaiki notasi saya jika Anda melihatnya tidak sesuai.
ttnphns
Bagaimana cara menghitung produk dalam (notX) ′ (notX) dalam R?
user4959
@ user4959, saya tidak tahu R. Di sini ! X direkomendasikan; namun hasilnya adalah boolean BENAR / SALAH, bukan numerik 1/0. Perhatikan bahwa saya memperbarui jawaban saya di mana saya mengatakan bahwa ada juga cara lain untuk sampai pada matriks D.
ttnphns
9

Solusi di atas tidak terlalu baik jika X jarang. Karena mengambil! X akan membuat matriks padat, mengambil banyak memori dan komputasi.

Solusi yang lebih baik adalah dengan menggunakan rumus Jaccard [i, j] = #common / (#i + #j - #common) . Dengan matriks jarang Anda dapat melakukannya sebagai berikut (perhatikan kode juga berfungsi untuk matriks non-jarang):

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 )
}
pengguna41844
sumber
1

Ini mungkin bermanfaat bagi Anda, tergantung pada apa kebutuhan Anda. Dengan asumsi bahwa Anda tertarik pada kesamaan antara tugas pengelompokan:

Koefisien Kemiripan Jaccard atau Indeks Jaccard dapat digunakan untuk menghitung kesamaan dari dua tugas pengelompokan.

Diberi label L1dan L2, Ben-Hur, Elisseeff, dan Guyon (2002) telah menunjukkan bahwa indeks Jaccard dapat dihitung dengan menggunakan produk-titik dari matriks perantara. Kode di bawah memanfaatkan ini untuk dengan cepat menghitung Indeks Jaccard tanpa harus menyimpan matriks perantara dalam memori.

Kode ditulis dalam C ++, tetapi dapat dimuat ke R menggunakan sourceCppperintah.

/**
 * 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));
}
Richard
sumber