Desidero un modo per calcolare in modo efficiente la somiglianza Jaccard tra i documenti di uno tm::DocumentTermMatrix
. Posso fare qualcosa di simile per la somiglianza del coseno tramite il pacchetto slam come mostrato in this answer. Mi sono imbattuto in another question and response su CrossValidated che era R specifico ma sull'algebra della matrice non è necessariamente il percorso più efficiente. Ho provato ad implementare questa soluzione con le più efficienti funzioni slam ma non ottengo la stessa soluzione di quando utilizzo un approccio meno efficiente di forzare il DTM a una matrice e di usare proxy::dist
.Somiglianza jaccard efficiente DocumentTermMatrix
Come è possibile calcolare in modo efficiente la somiglianza Jaccard tra i documenti di una grande DocumentTermMatrix in R?
#data & pacages
library(Matrix);library(proxy);library(tm);library(slam);library(Matrix)
mat <- structure(list(i = c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 3L, 1L,
2L, 3L, 3L, 3L, 4L, 4L, 4L, 4L), j = c(1L, 1L, 2L, 2L, 3L, 3L,
4L, 4L, 4L, 5L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L), v = c(1,
1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1), nrow = 4L,
ncol = 12L, dimnames = structure(list(Docs = c("1", "2",
"3", "4"), Terms = c("computer", "is", "fun", "not", "too",
"no", "it's", "dumb", "what", "should", "we", "do")), .Names = c("Docs",
"Terms"))), .Names = c("i", "j", "v", "nrow", "ncol", "dimnames"
), class = c("DocumentTermMatrix", "simple_triplet_matrix"), weighting = c("term frequency",
"tf"))
#Inefficient Calcolo (risultati attesi)
proxy::dist(as.matrix(mat), method = 'jaccard')
## 1 2 3
## 2 0.000
## 3 0.875 0.875
## 4 1.000 1.000 1.000
#My Tentativo
A <- slam::tcrossprod_simple_triplet_matrix(mat)
im <- which(A > 0, arr.ind=TRUE)
b <- slam::row_sums(mat)
Aim <- A[im]
stats::as.dist(Matrix::sparseMatrix(
i = im[,1],
j = im[,2],
x = Aim/(b[im[,1]] + b[im[,2]] - Aim),
dims = dim(A)
))
## 1 2 3
## 2 2.0
## 3 0.1 0.1
## 4 0.0 0.0 0.0
Le uscite non corrispondono.
FYI Ecco il testo originale:
c("Computer is fun. Not too fun.", "Computer is fun. Not too fun.",
"No it's not, it's dumb.", "What should we do?")
mi aspetto elementi 1 & 2 da 0 a distanza e l'elemento 3 per essere più vicino a Elemento 1 di elemento 1 e 4 (mi aspetto più lontana distanza in quanto nessuna parola è condivisa) come visto nella soluzione proxy::dist
.
EDIT
Si noti che anche su un DTM di medie dimensioni della matrice diventa enorme. Ecco un esempio con il pacchetto vegan. Nota 4 minuti per risolvere dove la somiglianza del coseno è ~ 5 secondi.
library(qdap); library(quanteda);library(vegan);library(slam)
x <- quanteda::convert(quanteda::dfm(rep(pres_debates2012$dialogue), stem = FALSE,
verbose = FALSE, removeNumbers = FALSE), to = 'tm')
## <<DocumentTermMatrix (documents: 2912, terms: 3368)>>
## Non-/sparse entries: 37836/9769780
## Sparsity : 100%
## Maximal term length: 16
## Weighting : term frequency (tf)
tic <- Sys.time()
jaccard_dist_mat <- vegan::vegdist(as.matrix(x), method = 'jaccard')
Sys.time() - tiC#Time difference of 4.01837 mins
tic <- Sys.time()
tdm <- t(x)
cosine_dist_mat <- 1 - crossprod_simple_triplet_matrix(tdm)/(sqrt(col_sums(tdm^2) %*% t(col_sums(tdm^2))))
Sys.time() - tiC#Time difference of 5.024992 secs
Non sono sicuro di aver capito il tuo commento. Cosa c'è di sbagliato nella mia risposta? Produce similitudini jaccard corrette e funziona piuttosto velocemente. –
Scusate se il mio commento sembra troppo maleducato. Risposta corretta –
grazie molto utile, PS mi piace le nuove aggiunte a text2vec –