Calcolo di Jaccard o altro coefficiente di associazione per i dati binari mediante la moltiplicazione della matrice


9

Voglio sapere se esiste un modo possibile per calcolare il coefficiente di Jaccard usando la moltiplicazione della matrice.

Ho usato questo codice

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

Questo è abbastanza ok da implementare in R. Ho fatto una somiglianza con i dadi, ma mi sono bloccato con Tanimoto / Jaccard. Qualcuno può aiutare?


Sembra che @ttnphns ne abbia parlato, ma dal momento che stai usando R, ho pensato di sottolineare anche che un certo numero di indici di somiglianza (inclusi quelli di Jaccard) sono già implementati nel veganpacchetto. Penso che tendano anche ad essere abbastanza ottimizzati per la velocità.
David J. Harris,

Risposte:


11

Sappiamo che Jaccard (calcolato tra due colonne qualsiasi di dati binari ) è , mentre Rogers-Tanimoto è , doveXaa+b+ca+da+d+2(b+c)

  • a - numero di righe in cui entrambe le colonne sono 1
  • b - numero di righe in cui questa e non l'altra colonna è 1
  • c - numero di righe in cui l'altra e non questa colonna è 1
  • d - numero di righe in cui entrambe le colonne sono 0

a+b+c+d=n , il numero di righe inX

Poi abbiamo:

XX=A è la matrice quadrata simmetrica di tra tutte le colonne.a

(notX)(notX)=D è la matrice quadrata simmetrica di tra tutte le colonne ("non X" sta convertendo 1-> 0 e 0-> 1 in X).d

Quindi, è la matrice quadrata simmetrica di Jaccard tra tutte le colonne.AnD

A+DA+D+2(n(A+D))=A+D2nAD è la matrice quadrata simmetrica di Rogers-Tanimoto tra tutte le colonne.

Ho verificato numericamente se queste formule danno il risultato corretto. Loro fanno.


Agg. Puoi anche ottenere matrici e :CBC

X B b XB=[1]XA , dove "[1]" denota matrice di quelli, dimensionato come . è la matrice quadrata asimmetrica di tra tutte le colonne; il suo elemento ij è il numero di righe in con 0 nella colonna i e 1 nella colonna j .XBbX

Di conseguenza, .C=B

Matrix può anche essere calcolato in questo modo, ovviamente: .n - A - B - CDnABC

Conoscendo le matrici , è possibile calcolare una matrice di qualsiasi coefficiente di somiglianza a coppie (dis) inventato per i dati binari.A,B,C,D


Le frazioni non hanno senso per le matrici a meno che non si spostino: moltiplicando a destra per un contrario, altrimenti si otterrà un risultato diverso rispetto alla moltiplicazione a sinistra. Inoltre, di solito non è il caso che un prodotto di due matrici simmetriche sia simmetrico. Intendi forse divisione per componente? Potresti sistemare la tua notazione per riflettere ciò che intendi sia la formula corretta?
whuber

@whuber Non uso inversione né moltiplicazione di matrici simmetriche quadrate. X è la matrice di dati binari e X'X è la sua matrice SSCP. not Xè X dove 1-> 0, 0-> 1. E qualsiasi divisione qui è divisione elementally. Per favore, correggi la mia notazione se vedi che non è appropriato.
ttnphns

Come calcolare il prodotto interno (notX) ′ (notX) in R?
user4959

@ user4959, non conosco R. Qui si consiglia X! tuttavia il risultato è VERO / FALSO booleano, non numerico 1/0. Nota che ho aggiornato la mia risposta in cui dico che esiste anche un altro modo per arrivare alla matrice D.
ttnphns,

9

La soluzione di cui sopra non è molto buona se X è scarsa. Perché prendere! X produrrà una matrice densa, occupando un'enorme quantità di memoria e calcolo.

Una soluzione migliore è usare la formula Jaccard [i, j] = #common / (#i + #j - #common) . Con le matrici sparse puoi farlo come segue (nota che il codice funziona anche per matrici non sparse):

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

Questo può o non può esserti utile, a seconda delle tue esigenze. Supponendo che tu sia interessato alla somiglianza tra le assegnazioni di cluster:

Il coefficiente di somiglianza Jaccard o l' indice Jaccard può essere utilizzato per calcolare la somiglianza di due assegnazioni di cluster.

Date le etichette L1e L2, Ben-Hur, Elisseeff e Guyon (2002) hanno dimostrato che l'indice Jaccard può essere calcolato usando prodotti a punti di una matrice intermedia. Il codice seguente sfrutta questo per calcolare rapidamente l'indice Jaccard senza dover memorizzare le matrici intermedie in memoria.

Il codice è scritto in C ++, ma può essere caricato in R usando il sourceCppcomando.

/**
 * 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));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.