餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

使っている名詞で2つの文の相関係数を取ってみる

Twitterで、ぬ氏が「二つの日本語の単語列(10-20文字ぐらい)があって、なるべく共通の単語が多いとか意味合い的に近いものを自動でマッチしたいときって何かいい方法ありますか?」と言うお題を出していたので、お気楽な方法を考えてみました。

わかち書きフレームワークのMeCabを利用して、2つの文を分解、単語1ダミー、単語2ダミー・・・と言う風に文Aと文Bのパラメーターをそれぞれつくって、相関係数を出して見ます。共通の単語が多ければ高い相関係数になるので、それで何かマッチング・アルゴリズムにかければ目的を達することができるでしょう。

説明すると長いわけですが、MeCabをインストールしてパスを通してコマンドラインで動くようにしてから、RMeCabをインストールして以下のように走らせたら終わりです。簡単ですね。

library(RMeCab)

# 比較する文を用意する
text <- c("牧瀬紅莉栖はオレの嫁", "牧瀬紅莉栖はツンデレ", "紅莉栖が生きている")

getWords <- function(text){
  res <- RMeCabC(text)
  value <- sapply(res, function(l){
    l[[1]]
  })
  type <- sapply(res, function(l){
    names(l[1])
  })
  value[type=="名詞"]
}

makeDummies <- function(words_all, words){
  0 + (words_all %in% words)*1
}


# 文ごとに名詞リストをつくる
words <- list()
for(i in 1:length(text)){
  words[[i]] <- getWords(text[i])
}

# 重複する単語は消して全体リストを作る
words_all <- unique(sort(unlist(words)))

# 文ごとに単語ダミーをつくる
words_dummies <- list()
for(i in 1:length(text)){
  words_dummies[[i]] <- makeDummies(words_all, words[[i]])
}

# 行列にまとめる
m <- matrix(unlist(words_dummies), length(words_all))
colnames(m) <- sprintf("text%d", 1:length(text))
rownames(m) <- words_all

# 相関係数を計算
cor(m)

ユーザー辞書を追加した、手元の環境だとこんな感じになりました。なお、ほとんどフィーリングで書いたので、RMeCabのコマンドを確認したら無駄が省ける可能性は大です。
f:id:uncorrelated:20200528183041p:plain

それでもすぐに、クラスター分析に持っていくことができます。

colnames(m) <- text
r_cluster <- hclust(dist(cor(m)), method="ward.D")
plot(r_cluster, main="MeCabで短文をクラスター分析", sub="", xlab="", ylab="", axes=FALSE)

f:id:uncorrelated:20200529112003p:plain

もちろん、具体的に何をするかは見えないので、役に立つかは謎です。文字列が長い場合は、よくあるテキストマイニング手法を真似しましょう。

同義語を揃える

使い道次第ですが、文書中に同義語(e.g. 社員募集=クルー募集)が多数考えられる場合は、同義語テーブルをつくって、一つの単語に揃えるほうが適切になるでしょう。

# 同義語テーブル(ベクターで用意/多い場合はファイルから読み込み推奨)
sv <- c("紅莉栖", "クリスティーナ", "助手", "セレブセブンティーン", "セレセブ", "蘇りし者", "ザ・ゾンビ")

# 最初の単語に揃える関数を用意(上にあわせ場合、MeCabに辞書登録している単語のみ有効)
l <- list()
for(i in 2:length(sv)){
  l[sv[i]] <- sv[1]
}

synonym <- function(words) sapply(words, function(word){
  ifelse(is.null(l[[word]]), word, l[[word]])
})

synonym(c("紅莉栖", "クリスティーナ", "セレセブ", "岡部"))

こんな感じで置換されます。

f:id:uncorrelated:20200529111723p:plain

MeCabのユーザー辞書

ユーザー辞書作成につかったcsvファイルの中身は以下です。

紅莉栖,1291,1291,5000,名詞,固有名詞,人名,名,*,*,くりす,クリス,クリス
クリスティーナ,1291,1291,5000,名詞,固有名詞,人名,名,*,*,くりすてぃーな,クリスティーナ,クリスティー
ツンデレ,1285,1285,1000,名詞,一般,*,*,*,*,*,ツンデレ,ツンデレ
セレブセブンティーン,1285,1285,5000,名詞,一般,*,*,*,*,*,セレブセブンティーン,セレブセブンティー
セレセブ,1285,1285,5000,名詞,一般,*,*,*,*,*,セレセブ,セレセブ
蘇りし者,1285,1285,5000,名詞,一般,*,*,*,*,*,ヨミガエリシモノ,ヨミガエリシモノ
ザ・ゾンビ,1285,1285,5000,名詞,一般,*,*,*,*,*,ザゾンビ,ザゾンビ

MeCabのドキュメントに詳しく書いてありますが、mecab-dict-index -d "C:\Program Files (x86)\MeCab\dic\ipadic" -u C:\path\to\animation.dic animation.csv して、res <- RMeCabC(text, dic="C:/path/to/animation.dic")と言うように使います。