概要
TF-IDFは文書内の各単語の重要度を表す指標で、前回はその計算方法を詳しく見ていきました。
今回はポケモン図鑑の説明文を用いて、各ポケモンの特徴的な単語を調べてみます。その結果から、フシギダネは本当に「たねポケモン」と言えるのかどうか、といったことを考察してみます。
ポケモン図鑑説明文のスクレイピング
Webスクレイピング用のパッケージ「vcscraping」で初代151匹の漢字説明文(初代~ソード・シールドまで)を取得しました。
ポケモン説明文はこちらのサイトより。
データはこちらに置いておきます。
コード
pip
janomeというライブラリ(https://github.com/mocobeta/janome)をpipします。
pip install janome
説明文の読み込み
データを読み込みます。
import csv
import math
import numpy as np
import pandas as pd
from janome.tokenizer import Tokenizer
#csvファイルの読み込み、ポケモン名と説明文の辞書型に -> {'フシギダネ': '生まれたときから背中に植物の…'}
data = pd.read_csv('save_151.csv', encoding='UTF-8', low_memory=False)
data = np.array(data)
pokemon_words = dict(zip(data[:, 1], data[:, 2]))
print(pokemon_words['フシギダネ'])
生まれたときから 背中に 植物の タネが あって 少しずつ 大きく 育つ。 生まれたときから 背中に 不思議な タネが 植えてあって 体と ともに 育つという。 何日だって なにも 食べなくても 元気! 背中のタネに たくさん 栄養が あるから 平気だ! 生まれて しばらくの あいだ 背中の タネに つまった 栄養を とって 育つ。 日なたで 昼寝を する 姿を 見かける。太陽の 光を いっぱい 浴びることで 背中の タネが 大きく 育つのだ。 生まれてから しばらくの あいだは 背中の タネから 栄養を もらって 大きく 育つ。
この時点では半角スペースが含まれていますが、後の単語化で除去されます。
説明文の単語分割
janomeで各ポケモンの説明文を分割して単語リストにします。
#janomeで単語に分割する関数
def split_sentence(sentence):
t = Tokenizer()
words = []
for token in t.tokenize(sentence):
#文書を単語に分割し、動詞、名詞、形容詞のみ抽出する
if token.part_of_speech.split(',')[0] in ['動詞', '名詞', '形容詞']:
#()や記号やゲームのタイトルを除外する
if token.surface in ['(', ')', 'X', 'Y', 'ピカ', 'ブイ', 'ソード', 'シールド', '!', '一']:
continue
words.append(token.surface)
return words
#ポケモンの説明文を分割して単語リストに置き換える -> {'フシギダネ': ['生まれ', 'とき', '背中', ...]}
for name in pokemon_words.keys():
pokemon_words[name] = split_sentence(pokemon_words[name])
print(pokemon_words['フシギダネ'])
['生まれ', 'とき', '背中', '植物', 'タネ', 'あっ', '大きく', '育つ', '生まれ', 'とき', '背中', '不思議', 'タネ', '植え', 'あっ', '体', '育つ', 'いう', '何', '日', 'なに', '食べ', '元気', '背中', 'タネ', 'たくさん', '栄養', 'ある', '平気', '生まれ', 'あいだ', '背中', 'タネ', 'つまっ', '栄養', 'とっ', '育つ', '日なた', '昼寝', 'する', '姿', '見かける', '太陽', '光', '浴びる', 'こと', '背中', 'タネ', '大きく', '育つ', 'の', '生まれ', 'あいだ', '背中', 'タネ', '栄養', 'もらっ', '大きく', '育つ']
TF、IDF、TF-IDFの計算関数
TF、IDF、TF-IDFの計算関数です。計算内容は過去記事とにらめっこしてください!
#TF、単語とtfの辞書を返す
def tf(words):
result = {}
for word in words: #同じ単語が2回以上計算される可能性あり(上書きのため結果に影響なし)
#TF計算
result[word] = words.count(word) / len(words)
return result
#IDF、単語とidfの辞書を返す
def idf(words, words_list_noduplicate):
result = {}
for word in words: #同じ単語が2回以上計算される可能性あり(上書きのため結果に影響なし)
#使用されている文書数をカウント
count = 0
for name in pokemon_words.keys():
if word in pokemon_words[name]:
count += 1
#IDF計算
result[word] = math.log(len(pokemon_words) / count) + 1
return result
#TF-IDF
def tf_idf(tf_result, idf_result):
result = {}
for word in tf_result.keys():
#TF-IDF計算
result[word] = tf_result[word] * idf_result[word]
#降順にソートして上位3件に絞って返す
return sorted(result.items(), key=lambda x:x[1], reverse=True)[0:3]
メイン処理
最後にメイン処理して結果を書き出しました。
results = []
#各ポケモンの処理
for name in pokemon_words.keys():
#各単語のTF、単語とtfの辞書を返す
tf_result = tf(pokemon_words[name])
#各単語のIDF、単語とidfの辞書を返す
idf_result = idf(pokemon_words[name], pokemon_words)
#各単語のTF-IDF
tf_idf_result = tf_idf(tf_result, idf_result)
#tf_idfの結果リストの先頭にポケモン名を挿入
tf_idf_result.insert(0, name)
results.append(tf_idf_result)
print(results[0])
#結果の書き出し
with open('tf-idf_151.csv', 'w', encoding='UTF-8', newline='') as f:
for result in results:
writer = csv.writer(f)
writer.writerow(result)
['フシギダネ', ('タネ', 0.4709476754944102), ('背中', 0.33653182736061504), ('育つ', 0.32373349656599193)]
フシギダネの説明に使われている単語は、TF-IDFが大きい順にタネ0.47、背中0.34、育つ0.32でした。
考察
フシギダネの分類は公式には「たねポケモン」ですが、「タネ」は他のポケモンであまり使われておらずフシギダネで繰り返し使われている「特徴的な単語」と言えるので、まさにぴったりの分類名だと思います。
一方、「やどかりポケモン」のヤドランは「シェルダー」が0.62と非常に特徴的です。これはもうシェルダーポケモンにしたほうが良いというわけです(!?)。
以下にTF-IDFのトップ10を並べておきます。
公式の分類 | ポケモン | TF-IDF | 判定 |
ツルじょうポケモン | モンジャラ | ツル 0.72 | OK |
やどかりポケモン | ヤドラン | シェルダー 0.62 | シェルダーポケモン |
きのこポケモン | パラス | キノコ 0.52 | OK |
ドリルポケモン | ニドクイン | ウロコ 0.51 | ウロコポケモン |
どくばちポケモン | スピアー | 刺し 0.50 針 0.30 | 針刺しポケモン |
たねポケモン | フシギダネ | タネ 0.47 背中 0.34 | OK |
きのこポケモン | パラセクト | キノコ 0.46 胞子 0.37 | OK |
たまごポケモン | ラッキー | タマゴ 0.45 | OK |
たねポケモン | フシギソウ | つぼみ 0.45 | つぼみポケモン |
フラワーポケモン | ラフレシア | 花粉 0.45 花びら 0.35 | 花びら花粉ポケモン |
なお、初代の説明文といえばライチュウとゴースの「インド象」が特徴的かと予想していたのですが、ライチュウの「インド」が0.028と低く、これは初代でしか使われない表現のためTFが低いのが原因です。ゴースは初代の漢字説明文がなくスクレイピングできていませんでした。
なお初代151匹のみで行ったため、全900匹くらい(?)を使用すればまた結果は変わってくると思います。