質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.86%

pythonで文書間のコサイン類似度を求める

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,893

mangopurin

score 6

前提・実現したいこと

tf-idf法を用いた結果を用いて、文書間のコサイン類似度を求めています。
tf-idfを用いて、テキストの文書ベクトルを求めることは出来ました。
ここから、文書間のコサイン類似を求める方法が分かりません。

今後、多くの文書に対して、コサイン類似度を求めるので、同じコードを書くのではなく、for文でまわしたいと考えています。

御享受、よろしくお願いします。

該当のソースコード

import MeCab
import math







sentence = ["私はみかんを食べました。","私はリンゴを食べました。"]
print(sentence)
num = len(sentence)
result = []
print("全文書数")
print(num)


a = []


for i in range(num):
    tagger = MeCab.Tagger()
    result.append(tagger.parse(sentence[i]))
    print("形態素解析した結果 result")
    print(result)

for i in range(num):
    for chunk in result[i]:
        #print(chunk)
        cols = chunk.split("\t")
        #print(cols)
        if len(cols) >= 4:
            parts = cols[3].split(",")
            if parts[0].startswith("動詞"):
                a.append(cols[0])

print(a)
#文章を形態素解析して、文章事の単語をリストへ
wordCount = {}#辞書
allCount = {}
sub_tfstore = {}
tfcounter = {}
tfstore = {}
sub_idf = {}
idfstore = {}
merge_idf = {}
tfidf = {}
merge_tfidf = {}
wordList = []

sum = 0

for i in range(num): #resultに形態素解析の結果
    wordList.append(result[i].split()[:-1:2])#wordListにresultを加える
    print("単語のみ")
    print(wordList)

for i in range(num):#iをnum分まわす
    for word in wordList[i]:#wordをwordList分まわす
        allCount[i] = wordCount.setdefault(word,0)#wordCountのwordが0
        wordCount[word]=1
    allCount[i] = wordCount

    wordCount = {}
    print("単語の数")
    print(allCount)

for i in range(num):#tfの分母を計算
    for word in allCount[i]:
        sum = sum + allCount[i][word]
    sub_tfstore[i] = sum
    sum = 0
    print(sub_tfstore)

for i in range(num):
    for word in allCount[i]:
        tfcounter[word] = allCount[i][word]*1.0/sub_tfstore[i]
    tfstore[i] = tfcounter
    print("tfの値")
    print(tfstore)
    tfcounter = {}

for i in range(num):
    for word in wordList[i]:
        wordCount.setdefault(word,0)
    for word in allCount[i]:
        wordCount[word] += 1
    sub_idf = wordCount
    print("単語の文章あたりの出現回数")
    print(sub_idf)

for i in range(num):
    for word in allCount[i]:
        idfstore[word] = math.log(1.0*math.fabs(num)/math.fabs(sub_idf[word]))
    merge_idf[i] = idfstore
    idfstore = {}
    print("idfの値")
    print(merge_idf)


#tfidfの計算
for i in range(num):
    for word in allCount[i]:
        tfidf[word] = tfstore[i][word]*merge_idf[i][word]
    merge_tfidf[i] = tfidf
    tfidf = {}
    print("tfidfの値")
    print(merge_tfidf)

for i in range(num):
    for word,count in sorted(merge_tfidf[i].items(),key = lambda x:x[1],reverse = True):
        print('text%d: %-16s %2.3f' % (i+1,word,count))

結果

text1: みかん              0.087
text1: 私                0.000
text1: は                0.000
text1: を                0.000
text1: 食べ               0.000
text1: まし               0.000
text1: た                0.000
text1: 。                0.000
text2: リンゴ              0.087
text2: 私                0.000
text2: は                0.000
text2: を                0.000
text2: 食べ               0.000
text2: まし               0.000
text2: た                0.000
text2: 。                0.000

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

scipyを使うとベクトルの配列→距離行列への変換が容易に行なえます。

from scipy.spatial.distance import pdist, squareform

data = [[1.0, 0.0, 0.5, 0.3, 0.2],
        [2.3, 3.1, 0.1, 0.3, 0.5],
        [1.0, 0.5, 0.2, 0.1, 0.1]]

dmat = squareform(pdist(data, metric="cosine"))
print(dmat)
""" =>
[[0.         0.44631947 0.14469177]
 [0.44631947 0.         0.1162559 ]
 [0.14469177 0.1162559  0.        ]]
"""

余談ですが、tfidfの計算もsklearnで楽に行なえます。分かち書きされた文字列のリストを渡せば良いです。日本語でちゃんと動かすには、analyzerを自分で定義するなどする必要があります。

https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html

from sklearn.feature_extraction.text import TfidfVectorizer

data = ["お腹 が 空い た",
        "お腹 が いっぱい",
        "電車 が 空い た",
        "りんご が いっぱい"]

tfidf = TfidfVectorizer(analyzer=lambda s:s.split())
vectors = tfidf.fit_transform(data).toarray()
print(tfidf.get_feature_names())
print(vectors)
""" =>
['いっぱい', 'お腹', 'が', 'た', 'りんご', '空い', '電車']
[[0.         0.53931298 0.35696573 0.53931298 0.         0.53931298        0.        ]
 [0.64043405 0.64043405 0.42389674 0.         0.         0.                0.        ]
 [0.         0.         0.32902288 0.4970962  0.         0.4970962         0.6305035 ]
 [0.5728925  0.         0.37919167 0.         0.72664149 0.                0.        ]]
"""

特別な理由がなければ自分で書く必要はありません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる