####目的
日本語のテキスト文書を対象に、単語の出現回数を表示する。(= Bag-of-words の作成)
####Bag-of-words 作製の手順
-
テキストファイルの準備
-
形態素分析の関数定義
(日本語は英語と違いスペースが無いため、まず文節ごとに区切る必要がある) -
テキストファイル読み込みと形態素分析実施
-
形態素を id へ変換
-
コーパスの作成
(テキストファイルの文書を id の並びで表現) -
Bag of Words 作成と csv ファイルへの書き出し
(ソースコード github, https://github.com/MomonekoView/Bag-of-words)
####環境
Python 3.4.5
Windows 10 or Linux 16.04
####1. テキストファイルの準備
今回例題として、青空文庫の夏目漱石の「心」を引用した。
https://www.aozora.gr.jp/cards/000148/files/773_14560.html
文章を .txt ファイルに転写し保存。(kokoro.txt)
下記はファイルの中身。
_________________________________
こころ
夏目漱石
+目次
上 先生と私
一
私わたくしはその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚はばかる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執とっても心持は同じ事である。よそよそしい頭文字かしらもじなどはとても使う気にならない。
(以下略)
_________________________________
####2. 形態素分析の関数定義
解析ソフトとして、janome を使用。インストールは pip install janome で OK。
(Mecab は Windows での環境構築が上手く行かず、使用を断念)
下記ソースで実施。名称 Text_Data の関数として定義。
除去する文字列については、あまり賢いやり方ではないが、マニュアルで個別に指定。
# coding: utf-8
import sys
import os
import pickle
import numpy as np
from janome.tokenizer import Tokenizer
def Text_Data(file):
# ファイルの読み込み
f = open(file)
text = f.read()
f.close()
text = text.split('\n')
# 文章で分割
for i in range(len(text)):
text[i] = text[i].split('。')
# 以下形態素解析(形態素に分割)
t = Tokenizer()
new_text = []
word_list = []
for i in range(len(text)):
new_text = new_text + text[i]
print(new_text)
# トークナイズしてリストに格納
for j in range(len(new_text)):
tokens = t.tokenize(new_text[j])
for k in range(len(tokens)):
word_list.append(tokens[k].surface)
# 除去する文字列を指定
rems = ['@', ',', ',', '。', ' ', 'http', 'https','//', '、', '×', '.',
'+', '+',
'/',', ', '(', ')', '(', ')', '://', 'RT', ':', ';',':', ';',
'」','「', '@', '-', '/', '%', '%', '!', '!', '#', '#',
'$', '$', '=', '=', 'ー', '¥', '^', '{', '}', '~', '~',
'&', '&', '・', '?', '??', '?', '??', '<', '<', '>', '>', '_', '_',
'co', 'jp', '(@', '○', '『', '』', '”', '’', '"', '…',
')」', '〇', '【', '】', '[', ']',
'から', 'より', 'こそ', 'でも', 'しか','さえ', 'けれど', 'たり', 'つつ', 'とも',
'たら', 'ある', 'なら', 'のに', 'です', 'ます', 'する', 'ほど', 'ない', 'くる',
'なり', 'そう', 'まし', 'その', 'この', 'あの', 'せる', 'どう', 'ため', 'どこ',
'いる', 'これ', 'それ', 'あれ', 'いい', 'など', 'あっ', 'もう', 'さん', 'じゃ',
'から', 'あり', 'ので', 'とも', 'ませ', 'でし', 'とき', 'こと', 'なる', 'って',
'ただ', 'まで', 'もの', 'つか', 'なっ', 'でき', 'もっ', 'けど', 'ほぼ', 'なー',
'そこ', 'ここ', 'だろ', 'なん', 'だっ', 'なあ', 'っけ', 'せる', 'やっ', 'また',
'どれ', 'なれ', 'かも', 'いく', 'いけ', 'いう', 'たい', 'あと', 'かも', 'しれ',
'こう', 'なく', 'よく', 'だけ', 'れる', 'よう', 'かけ', 'どの', 'てる', 'とか',
'という', '思う', '思っ', 'として', 'ところ', 'しまう', 'なんか', 'そんな',
'でしょ', 'しまい', 'わかり', 'まま'
]
# 除去リストに数字を追加
for number in range(10000):
rems.append(str(number))
# 除去リストに追加 ― 一文字のひらがなとカタカナ,全角数字,アルファベット大文字・小文字
hirakana = [chr(i) for i in range(12353, 12436)]
katakana = [chr(i) for i in range(12449, 12533)]
zenkaku_num = [chr(i) for i in range(65296, 65296+10)]
alph_L = [chr(i) for i in range(97, 97+26)]
alph_S = [chr(i) for i in range(65, 65+26)]
for a in range(len(hirakana)):
rems.append(hirakana[a])
for b in range(len(katakana)):
rems.append(katakana[b])
for c in range(len(zenkaku_num)):
rems.append(zenkaku_num[c])
for d in range(len(alph_L)):
rems.append(alph_L[d])
for e in range(len(alph_S)):
rems.append(alph_S[e])
words = [x for x in word_list if (x in rems) == False]
return words
####3. テキストファイル読み込みと形態素分析実施
# coding: utf-8
import os
import glob
import numpy as np
from text import Text_Data
from NLP_func import *
import pandas as pd
words = Text_Data("kokoro.txt")
print(words)
['こころ', '夏目', '漱石', '目次', '上', '\u3000', '先生', '私', '一', '私', 'わたくし', '人', '常に', '先生', '呼ん', 'だから', '先生', '書く', '本名', '打ち明け', '世間', '憚', 'はばかる', '遠慮', '方', '私', 'にとって', '自然', '私', '人', '記憶', '呼び', '起す', 'ごと', 'すぐ', '先生', 'たく', '筆', ・・・・・・・,, '妻', '己', 'おのれ', '過去', 'に対して', 'もつ', '記憶', 'なるべく', '純白', '保存', 'おい', 'やり', '私', '唯一', 'ゆい', '希望', '私', '死ん', '後', '妻', '生き', '以上', 'あなた', '限り', '打ち明け', 'られ', '私', '秘密', 'すべて', '腹の中', 'しまっ', 'おい', '下さい']
全角空白(\u3000)が見られるが、形態素分割はOKとする。
ちなみに、len(words) は 45273 である。
記号や数字、助詞などを除くと、およそ 4 万 5 千個の単語からなる小説ということになる。
####4. 形態素を id へ変換
リスト (words) 内の各単語に id を振る。
(「ゼロから作る Deep Learning ② 自然言語処理編,斎藤 康毅 著,O'REILLY」 の第 2 章を参照した)
# coding: utf-8
import numpy as np
def id_preprocess(words, word_to_id, id_to_word):
for word in words:
if word not in word_to_id:
new_id = len(word_to_id)
word_to_id[word] = new_id
id_to_word[new_id] = word
return word_to_id, id_to_word
word_to_id = {}
id_to_word = {}
word_to_id, id_to_word = id_preprocess(words, word_to_id, id_to_word)
print(word_to_id)
{'こころ': 0, '夏目': 1, '漱石': 2, '目次': 3, '上': 4, '\u3000': 5, '先生': 6, '私': 7, '一': 8, 'わたくし': 9, '人': 10, '常に': 11, '呼ん': 12, 'だから': 13, '書く': 14, '本名': 15, '打ち明け': 16, '世間': 17, '憚': 18, 'はばかる': 19, '遠慮': 20, '方': 21, 'にとって': 22, '自然': 23, '記憶': 24, '呼び': 25, '起す': 26, 'ごと': 27, 'すぐ': 28, 'たく': 29, '筆': 30, ・・・・・・・・, '徒労': 7730, '渡辺': 7731, '華山': 7732, 'なべ': 7733, 'かざん': 7734, '邯鄲': 7735, '画': 7736, '死期': 7737, '繰り延べ': 7738, '先達': 7739, 'せんだって': 7740, '果たす': 7741, '果たし': 7742, '善悪': 7743, '供する': 7744, '保存': 7745, '秘密': 7746}
記号や数字、助詞などを除いて、合計 7746 個の単語からなる模様。
word_to_id を逆にした id_to_word は下記。
print(id_to_word)
{0: 'こころ', 1: '夏目', 2: '漱石', 3: '目次', 4: '上', 5: '\u3000', 6: '先生', 7: '私', 8: '一', 9: 'わたくし', 10: '人', 11: '常に', 12: '呼ん', 13: 'だから', 14: '書く', 15: '本名', 16: '打ち明け', 17: '世間', 18: '憚', 19: 'はばかる', 20: '遠慮', 21: '方', 22: 'にとって', 23: '自然', 24: '記憶', 25: '呼び', 26: '起す', 27: 'ごと', 28: 'すぐ', 29: 'たく', 30: '筆', ・・・・・・・,7730: '徒労', 7731: '渡辺', 7732: '華山', 7733: 'なべ', 7734: 'かざん', 7735: '邯鄲', 7736: '画', 7737: '死期', 7738: '繰り延べ', 7739: '先達', 7740: 'せんだって', 7741: '果たす', 7742: '果たし', 7743: '善悪', 7744: '供する', 7745: '保存', 7746: '秘密'}
####5. コーパスの作成
def corpus_preprocess(words, word_to_id, id_to_word):
corpus = np.array([word_to_id[w] for w in words])
return corpus
corpus_dic = {}
corpus_dic = corpus_preprocess(words, word_to_id, id_to_word)
print(corpus_dic)
[ 0 1 2 ... 499 694 1260]
これは、元の文章に出現する各単語を id で置換したものに相当する。
len(corpus_dic) = len(words) = 45273 であり、元の小説 45273 語を単語 id で表現したことになる。
####6. Bag of Words 作成と csv ファイルへの書き出し
5 で作成した corpus_dic の id の頻度をカウントする。
bag_of_words = {}
vocab_size = len(word_to_id) # = len(id_to_word)
bag_of_words = {}
corpus_size = len(corpus_dic)
for i in range(vocab_size):
count = 0
for j in range(corpus_size):
if corpus_dic[j] == i: # 元の文書を走査していき,一致していればカウントする。
count += 1
bag_of_words[id_to_word[i]] = count
# 頻出順にソート
bag_of_words = sorted(bag_of_words.items(), key = lambda x: x[1], reverse=True)
# 単語を ID 順に CSV ファイルへ保存 # BOW を CSV ファイルへ保存
Table = pd.DataFrame(bag_of_words)
Table.to_csv('Table.csv', index=False, encoding="shift_jis")
####結果
出現回数でソートした単語は以下の通り。(上位30単語を表示)
やはり「先生」が多いのは納得。
親友の K について、text.py の中で、英文字を除去していたが、これは全角文字だから残ったと思われる。
大事な登場人物なので除去されなくて何故かほっとしている。