コーパスの作者間のコサイン距離を計算したい。 20のドキュメントのコーパスを取りましょう。
_require(tm)
data("crude")
length(crude)
# [1] 20
_
この20のドキュメント間のコサイン距離(類似性)を調べたいと思います。用語とドキュメントのマトリックスを作成します
_tdm <- TermDocumentMatrix(crude,
control = list(removePunctuation = TRUE,
stopwords = TRUE))
_
次に、それを行列に変換して、proxyパッケージの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()
を2回呼び出して、tdm
をtmからproxyに渡し、最後に計算するのは非効率的だと思われます平均。
同じ結果を得るためのよりスマートな方法を考えることは可能ですか?
tm
の用語ドキュメント行列はslam
パッケージのスパースな「単純なトリプレット行列」であるため、そこで関数を使用して、コサイン類似度の定義から直接距離を計算できます。
_library(slam)
cosine_dist_mat <- 1 - crossprod_simple_triplet_matrix(tdm)/(sqrt(col_sums(tdm^2) %*% t(col_sums(tdm^2))))
_
これは、スパース行列の乗算を利用します。私の手では、220のドキュメントに2963の用語があり、97%のスパース性を持つtdmは、ほんの数秒しかかかりませんでした。
私はこれをプロファイリングしていないので、proxy::dist()
よりも速いかどうかわかりません。
注:これを機能させるには、not tdmを通常の行列に強制変換する必要があります。つまり、tdm <- as.matrix(tdm)
を実行しないでください。
最初。素晴らしいコードMANdrecPhD!しかし、私は彼が書くつもりだったと信じています:
cosine_dist_mat <- crossprod_simple_triplet_matrix(tdm)/(sqrt(col_sums(tdm^2) %*% t(col_sums(tdm^2))))
書かれた彼のコードは、非類似度スコアを返します。コサイン類似度の対角線上に0ではなく1が必要です。 https://en.wikipedia.org/wiki/Cosine_similarity 。私は誤解される可能性があり、皆さんは実際に非類似度スコアを望んでいますが、整理するのに少し考えが必要だったので、それについて言及したいと思いました。