web-dev-qa-db-ja.com

R:tmとプロキシを使用して用語ドキュメント行列からコサイン距離を計算します

コーパスの作者間のコサイン距離を計算したい。 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回呼び出して、tdmtmからproxyに渡し、最後に計算するのは非効率的だと思われます平均。

同じ結果を得るためのよりスマートな方法を考えることは可能ですか?

14
CptNemo

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)を実行しないでください。

15
NumerousHats

最初。素晴らしいコード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 。私は誤解される可能性があり、皆さんは実際に非類似度スコアを望んでいますが、整理するのに少し考えが必要だったので、それについて言及したいと思いました。

10
Luke Gallione