R: вычислить косинусное расстояние от матрицы терм-документов с помощью TM и прокси
Я хочу рассчитать косинусное расстояние между авторами корпуса. Возьмем корпус из 20 документов.
require(tm)
data("crude")
length(crude)
# [1] 20
Я хочу узнать косинусное расстояние (сходство) среди этих 20 документов. Я создал термин-документ матрицы с
tdm <- TermDocumentMatrix(crude,
control = list(removePunctuation = TRUE,
stopwords = TRUE))
тогда я должен преобразовать его в матрицу, чтобы передать его в dist()
на прокси пакета
tdm <- as.matrix(tdm)
require(proxy)
cosine_dist_mat <- as.matrix(dist(t(tdm), method = "cosine"))
наконец, я удаляю диагональ моей косинусной матрицы расстояний (так как меня это не интересует на расстоянии между документом и самим собой) и вычислить среднее расстояние между каждым документом и другим документом 19 корпуса
diag(cosine_dist_mat) <- NA
cosine_dist <- apply(cosine_dist_mat, 2, mean, na.rm=TRUE)
cosine_dist
# 127 144 191 194
# 0.6728505 0.6788326 0.7808791 0.8003223
# 211 236 237 242
# 0.8218699 0.6702084 0.8752164 0.7553570
# 246 248 273 349
# 0.8205872 0.6495110 0.7064158 0.7494145
# 352 353 368 489
# 0.6972964 0.7134836 0.8352642 0.7214411
# 502 543 704 708
# 0.7294907 0.7170188 0.8522494 0.8726240
пока все хорошо (с небольшими корпусов). Проблема в том, что этот метод не очень хорошо масштабируется для больших массивов документов. На этот раз это кажется неэффективным из-за двух вызовов as.matrix()
, передать tdm
С tm to прокси и, наконец, рассчитать среднее значение.
это можно ли придумать более умный способ получить тот же результат?
2 ответов
С tm
терминальные матрицы документов - это просто разреженные "простые триплетные матрицы" из slam
пакет, вы можете использовать функции там, чтобы вычислить расстояния непосредственно из определения косинусного сходства:
library(slam)
cosine_dist_mat <- 1 - crossprod_simple_triplet_matrix(tdm)/(sqrt(col_sums(tdm^2) %*% t(col_sums(tdm^2))))
Это использует умножение разреженных матриц. В моих руках tdm с 2963 терминами в 220 документах и разреженностью 97% занял всего пару секунд.
Я не профилировал это, поэтому я понятия не имею, если это быстрее, чем proxy::dist()
.
примечание: для этого, чтобы работать, вы должны не принудить tdm к регулярной матрице, i.e не делать tdm <- as.matrix(tdm)
.
первый. Великий код MAndrecPhD! Но мне кажется, он хотел написать:--2-->
cosine_dist_mat <- crossprod_simple_triplet_matrix(tdm)/(sqrt(col_sums(tdm^2) %*% t(col_sums(tdm^2))))
его код в написанном виде возвращает оценку несходства. Мы хотим 1 по диагонали для косинусного сходства, а не 0. https://en.wikipedia.org/wiki/Cosine_similarity. Я могу ошибаться, и вы, ребята, действительно хотите получить оценку несходства, но я подумал, что упомяну об этом, так как мне потребовалось немного подумать, чтобы разобраться.