お知らせ

ただいま、シンタックスハイライターの設定を見直しております。
プログラムが見にくくなっているページがありますが、ご容赦ください。

TF-IDFを求めてWordCloudをつくるプログラム(3)

テキストマイニング

自然光の中でコーディング

こんにちは。今回は前回の続きで、TF-IDFを求めてWordCloudをつくるプログラムについて、詳細を書いていきたいと思います。プログラムが長いので、枕の話はなしで早速本題に入ります。

形態素解析をして形態素の出現回数を数える部分

29~83行目では、形態素解析を行い、それをもとに形態素の出現回数を数えています。長いので、分割して動作を説明します。

準備

31~40行目は、必要な編集の初期化を行っています。

# --------------- 【形態素解析をして、語の出現回数を求める】 ---------------
 
word_list = { }			# 形態素(語)の出現回数を記録
tf = { }			# tf の値を記録
word_documents = { }            # 語がいくつの文章に含まれているかを数えるための変数
count_documents = { }           # ナイーブベイズ用
count_all = 0			# 文章の数を数える(正確には行数を数える)
 
 
# janomeを組み込む
from janome.tokenizer import Tokenizer
t = Tokenizer()			# 変数 t を使って形態素解析をする

31~35行目では、変数の初期化をしています。

31行目の変数word_listはカテゴリーごとに出現語を記録します。高校でTF-IDF(2)での例では、

{ 'いちご’ : { '果物’ : 1 , 'ケーキ’ : 2 , 'ビタミン’ : 1 , '赤い’ : 1 } ,
'りんご’ : { '果物’ : 1 , 'ビタミン’ : 1 , '赤い’ : 1 , ’ジュース’ : 1 , '青森’ : 1 } ,
'キウィ’ , { 'ビタミン’ : 1 , '毛’ : 1 , '緑’ : 1 , '黄色’ : 1 } }

というようにカテゴリーごとに出現語の辞書をつくるために初期化しています。

32行目は、出現回数をもとにTFを計算して記録するための変数TFを初期化します。

33行目は、IDFを計算するための変数word_documentsを初期化しています。この変数は、形態素がいくつの文章にまたがって出現しているかを記録するためのものです。

34・35行目は、今回のプログラムでは不要な行です。このプログラムでは、はじめは機械学習の1つである単純ベイズ分類器(ナイーブベイズ分類器)も計算するように作っていた名残です。

39・40行目は、janomeを使えるようにするための準備です。変数tを用いて形態素解析をします。標準ライブラリではないので、インストールしていなければ、pipコマンドでインストールする必要があります。

カテゴリーごとにループするための準備

43~50行目は、カテゴリーごとにループで処理するための準備です。

# 分類ごとに 形態素解析して、tf を求める
for category in documents:
    document = documents[category]		# 分類ごとに文章を document に入れる
    word_list[category] = { }			# 分類に含まれる語を入れる辞書を用意する
    word_count = 0				# 全形態素数を数えるために初期化する
    count_documents[category] = 0               # 分類ごとの文章数を数える
 
    lines = document.split( '\n' )		# 文章を行ごとに分ける

44行目では、辞書の変数documentsからキーであるカテゴリーを1つずつ変数categoryに取得しループします。

45行目では、変数documentにそのカテゴリーの文章を代入します。

46行目から48行目は変数の初期化です。

46行目では、カテゴリーごとの出現語の回数を表す変数documentsのバリューを辞書として初期化しています。

47行目では、TFを求めるためにTFの分母になる全形態素数を数える変数word_countを初期化しています。

48行目は、今回のプログラムでは不要な行です。これも単純ベイズ分類器の名残です。

50行目では、変数documentに入っている文章を行ごとに分割して、52行目から行ごとにループできるように変数linesに代入しています。

テキストファイルの行ごとのループを処理するための準備

52~56行目は、テキストファイルの行ごとにループする準備です。

    for line in lines:				# 1行ごとに以下の処理を繰り返す
        tokens = t.tokenize( line )		# 1行分の文章を形態素解析する
        count_all += 1				# 文章数を 1 増やす
        count_documents[category] += 1 	        # ナイーブベイズ用 
        words_line = [ ]                        # すでに文章内に同じ語が出現したかどうかのチェック用の変数

52行目では、テキストファイルを行ごとに分割した変数linesから1行ずつ変数lineに取得してループします。

53行目では、変数lineの文を形態素に分解して変数tokensに代入しています。

54・55行目は、今回のプログラムでは不要な行です。これも単純ベイズ分類器の名残です。

56行目は、IDFを計算するために文章内ですでに同じ語が出現しているかどうかをチェックするためのリストを初期化しています。

形態素ごとの処理(品詞によりストップワードを分類)

58~67行目では、品詞により処理するか否かを分類しています。

        for token in tokens:			# 形態素ごとに以下の処理を繰り返す
            word = token.base_form		# word に、形態素(語)の原形を代入する
 
            if word == '*':			# 原形が *(アスタリスク) のとき
                word = token.surface	        # word は、語の表層形(元の形)にする
                
            # 品詞が「名詞」「動詞」「形容詞」でない場合は、 tf を求めない	
            partOfSpeech = token.part_of_speech.split( ',' )[0]
            if partOfSpeech != '名詞'  and  partOfSpeech != '動詞'  and  partOfSpeech != '形容詞':
                continue

58行目では、形態素に分解された変数tokensから1つずつ形態素を変数tokenに取得しループします。

59行目では、変数tokenに取得した形態素の原形を取得しています。61・62行目では、原形が取得できなかった場合に文章内で記述された元の形を取得しています。

65行目では、変数tokenに取得した形態素の品詞を取得しています。66・67行目では、名詞・動詞・形容詞以外は処理しないようにしています。

形態素ごとの処理(出現回数を数える)

69~83行目では、形態素ごとに出現回数を数える処理をしています。

            # 初めて出現した語の場合、新しい語を word_list に登録する
            if not word in word_list[category]:
                word_list[category][word] = 0
 
            # 語が行内に既に出現していない場合は初期化
            if not word in word_documents:
                word_documents[word] = 0
 
            # 語が行内に出現していない場合
            if not word in words_line:
                words_line.append(word)         # 行内の出現リストに語を加える(出現チェック用に追加)
                word_documents[word] += 1       # 出現文章数を 1 増やす(idf計算用)
			
            word_list[category][word] += 1	# 分類内での語の出現回数を 1 増やす
            word_count += 1			# 分類内の全語数を 1 増やす

70・71行目では、カテゴリー内で初めて出現した形態素を変数word_listに出現回数の初期値0で登録します。このように辞書に登録しないで82行目を実行するとエラーになるため、辞書に追加しています。

74・75行目では、これまでに処理した文章ではじめて出現した形態素を変数word_documentsに初期値0で登録します。先ほどの処理と同様に80行目でのエラーを避けるために、前もって辞書に追加してます。

78~80行目では、文章ではじめて出現した形態素に対する処理です。79行目で処理済みであることを記録し、80行目でその形態素が出現した文章数を1だけ増やしています。

82行目では、文章内での形態素の出現回数を1だけ増やすことで出現回数を数えています。

83行目では、文章内の全形態素の個数を1だけ増やすことで全形態素の個数を数えています。

TFを計算する部分

85~90行目は、TFを計算します。

    # tf に値を入れられるよう初期化する
    tf[category] = { }
 
    # 語ごとに 出現回数/全形態素数 を求める
    for word in word_list[category]:
        tf[category][word] = word_list[category][word] / word_count

86行目では、カテゴリーごとのTFを辞書として記録できるように初期化しています。

89・90行目では、カテゴリー内で出現した形態素ごとにTFを計算しています。計算式は、”\(形態素の出現回数 / 全形態素数\)”です。

以上でTFの計算ができました。ここまでで十分に長くなってしまったのでおしまいにします。続きは次回。それではまた。

この記事を書いた人
春日井 優

高校で情報科という教科を担当しています。以前は数学科も担当していました。(今でも数学科の教員免許状は有効です。)プログラムを覚えたのは、「ゲームセンターあらし」という漫画のキャラクターがBASICを解説する「こんにちはマイコン」を読んだことがきっかけでした。

Posted by kasugai