30
まずはmecabを使って
$ mecab neko.txt > neko.txt.mecab
を実行します。mecabのインストールは読者が頑張ってください。
word_array = [] with open('neko.txt.mecab') as r: r_lines = r.read() r_test = r_lines.splitlines() r_test_1bun = r_test[4:10] # 1文目?の範囲 [4:10] for line in r_test_1bun: if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) print(word_array)
出力は
['吾輩\t名詞,代名詞,一般,*,*,*,吾輩,ワガハイ,ワガハイ', 'は\t助詞,係助詞,*,*,*,*,は,ハ,ワ', '猫\t名詞,一般,*,*,*,*,猫,ネコ,ネコ', 'で\t助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ', 'ある\t助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル', '。\t記号,句点,*,*,*,*,。,。,。']
[{'surface': '吾輩', 'base': '吾輩', 'pos': '名詞', 'pos1': '代名詞'}, {'surface': 'は', 'base': 'は', 'pos': '助詞', 'pos1': '係助詞'}, {'surface': '猫', 'base': '猫', 'pos': '名詞', 'pos1': '一般'}, {'surface': 'で', 'base': 'だ', 'pos': '助動詞', 'pos1': '*'}, {'surface': 'ある', 'base': 'ある', 'pos': '助動詞', 'pos1': '*'}, {'surface': '。', 'base': '。', 'pos': '記号', 'pos1': '句点'}]
1文目を「吾輩は猫である。」の文と解釈した。
line.split('\t')[1].split(',')[6]とline.split('\t')[1].split(',')[0]とline.split('\t')[1].split(',')[1]を同時に処理できたら美しいが、思いつかなかった。
最初に
from pprint import pprint
とimportして
pprint(word_array)で表示すると
[{'base': '吾輩', 'pos': '名詞', 'pos1': '代名詞', 'surface': '吾輩'},
{'base': 'は', 'pos': '助詞', 'pos1': '係助詞', 'surface': 'は'},
{'base': '猫', 'pos': '名詞', 'pos1': '一般', 'surface': '猫'},
{'base': 'だ', 'pos': '助動詞', 'pos1': '*', 'surface': 'で'},
{'base': 'ある', 'pos': '助動詞', 'pos1': '*', 'surface': 'ある'},
{'base': '。', 'pos': '記号', 'pos1': '句点', 'surface': '。'}]
となって見やすい。
31
path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) ##### verb_surface_set = set() #動詞のsurfaceのset for word in word_array: if word['pos']=='動詞': verb_surface_set.add(word['surface']) print(verb_surface_set)
#####までは問題30と同様である。
出力は例えば
{'出さ', '押しやる', 'もらう',
で始まり
'背く', 'うっ', '勝る'}
で終わる。(set型なので順不同)
すべて抽出は重複しているものを除外すると解釈し、set()で管理した。
32
path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) verb_base_set = set() #動詞の原型のset for word in word_array: if word['pos']=='動詞': verb_base_set.add(word['base']) print(verb_base_set)
出力は
{'保つ', 'かたちづくる', '漲る',
で始まり
'うたう', '出会う', '求む'}
で終わる
最後の表示でsurfaceをbaseにするだけである。
33
path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) ##### AnoB_set = set() for i in range(len(word_array)-2): if (word_array[i]['pos'] == '名詞' and word_array[i+1]['surface'] == 'の' and word_array[i+2]['pos'] == '名詞'): AnoB_set.add(word_array[i]['surface']+word_array[i+1]['surface']+word_array[i+2]['surface']) print(AnoB_set)
range(len(word_array)-2):が気に入らないなら、
path = 'neko.txt.mecab' surface_old = '' mode_AnoB = 0 with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() AnoB_set = set() for line in r_split: if line == 'EOS' or line == '': # EOSや空行は辞書登録しない。 mode_AnoB = 0 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] pos = line.split('\t')[1].split(',')[0] if mode_AnoB == 2 and pos == '名詞': AnoB_set.add(surface_old+'の'+surface) if mode_AnoB == 1 and surface == 'の': mode_AnoB = 2 elif pos == '名詞': surface_old = surface mode_AnoB = 1 else: mode_AnoB = 0 print(AnoB_set)
出力は例えば
{'日本一の堪能', '君の親愛', '紫の紐'
で始まり
'年頃の比較', '這裏の消息', '吾輩の顔'}
で終わる。
mode_AnoBは、
名詞があれば1
その直後に「の」があれば2
それ以外で0である変数である。
つまりmode_AnoB==2の段階で次の単語が名詞なら、出力するための集合に入れる。
surface_oldに1個目の名詞を入れている。
「の」は名詞ではない。
今回は改行でmode_AnoBを0としたが、
「倦んじて薫ずる香裏に君の
霊か相思の煙のたなびき」
という改行で区切られた部分の「君の霊」を抽出したい場合はそうしなければよい。
また、
if mode_AnoB < 2 and pos == '名詞': surface_old = surface mode_AnoB = 1 elif mode_AnoB == 1 and surface == 'の': mode_AnoB = 2 elif mode_AnoB == 2 and pos == '名詞': AnoB_set.add(surface_old+'の'+surface) surface_old = surface mode_AnoB = 1 else: mode_AnoB = 0
のようにしてもよい。
最初の部分は mode_AnoB==0とすると
[名詞][名詞]の[名詞]
などが漏れるため、mode_AnoB < 2としなければいけないことに注意。
34
path = 'neko.txt.mecab' successive_noun = '' successive_noun_mode = 0 successive_noun_set = set() with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: if line == 'EOS' or line == '': continue surface = line.split('\t')[0] pos = line.split('\t')[1].split(',')[0] if pos == '名詞': successive_noun_mode = 1 successive_noun = successive_noun+surface elif successive_noun_mode == 1: successive_noun_set.add(successive_noun) successive_noun_mode = 0 successive_noun = '' print(successive_noun_set)
出力は例えば、
{'奇言奇行', '神酒供え', '筒袖'
で始まり、
'挙動', '肌', '反照'}
で終わる。
「十七味調唐辛子調」なども抽出できている。
名詞が続く間はsuccessive_noun_modeが1、続かなくなったら0。そのときに連続した名詞をsetに追加。
35
path = 'neko.txt.mecab' word_freq_dict = dict() with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: if line == 'EOS': continue surface = line.split('\t')[0] if surface in word_freq_dict: word_freq_dict[surface] += 1 else: word_freq_dict[surface] = 1 print(sorted(word_freq_dict.items(),reverse=True, key = lambda x: x[1])[:10])
出力は
[('の', 9194), ('。', 7486), ('て', 6868), ('、', 6772), ('は', 6420), ('に', 6243), ('を', 6071), ('と', 5508), ('が', 5337), ('た', 3988)]
collections.Counterを使ってもいいと思う。
36
import matplotlib.pyplot as plt import matplotlib import collections matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro' path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: #line(example) = {一\t名詞,数,*,*,*,*,一,イチ,イチ} if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) surface_list = [d['surface'] for d in word_array] word_count_by_surface = collections.Counter(surface_list) graph_list = word_count_by_surface.most_common()[:10] x = [taple[0] for taple in graph_list] y = [taple[1] for taple in graph_list] fig = plt.figure() plt.bar(x,y) plt.title('出現頻度上位10') plt.xlabel('単語') plt.ylabel('出現回数') plt.show() fig.savefig('36_image.png')
出力はimport matplotlib.pyplot as plt
import matplotlib
でグラフの環境を、
import collections
で計数するモジュールを、importし、
matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro'
でグラフ内の日本語のフォントを設定。
surface_list[]にすべてのsurfaceを入れ、collections.Counterで計数。
そして、.most_common()[:10]によって最頻10単語を取り出す。
ここで、形式は(単語, 回数)のタプルであるため
x = [taple[0] for taple in graph_list]
とリスト内包表記でリストを作る。
fig = plt.figure()
以降でグラフを書き、ラベルを適宜追加する。
plt.show()は処理を止めてしまうため、グラフを閉じずに処理を続けたい場合はplt.pause(0.01)など。
37
import matplotlib.pyplot as plt import matplotlib import collections matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro' path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_sentences = r_lines.split('EOS') for sentence in r_sentences: if '猫' in sentence: #猫と同じ文(=共起) sentence_line = sentence.split('\n') for word in sentence_line: if word == '': continue surface = word.split('\t')[0] base = word.split('\t')[1].split(',')[6] pos = word.split('\t')[1].split(',')[0] pos1 = word.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) surface_list = [d['surface'] for d in word_array] surface_list = [elem for elem in surface_list if elem != '猫'] # 猫自体を共起としない word_count_by_surface = collections.Counter(surface_list) graph_list = word_count_by_surface.most_common()[:10] x = [taple[0] for taple in graph_list] y = [taple[1] for taple in graph_list] fig = plt.figure() plt.bar(x,y) plt.title('出現頻度上位10') plt.xlabel('単語') plt.ylabel('出現回数') plt.show() fig.savefig('37_image.png')
出力は行ごとではなく'EOS'ごとに分割し、「猫」が含まれる場合にのみ単語を辞書に登録している。
その後、「猫」それ自身は共起の対象ではないとし、「猫」をリスト内包表記で除いている。
その後は問題36と同様に計数してグラフに出力。
38
import matplotlib.pyplot as plt import matplotlib import collections matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro' path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: #line(example) = {一\t名詞,数,*,*,*,*,一,イチ,イチ} if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) surface_list = [d['surface'] for d in word_array] word_count_by_surface = collections.Counter(surface_list) graph_list = word_count_by_surface.most_common() x = [taple[1] for taple in graph_list] fig = plt.figure() plt.hist(x, bins=100, log=True, range=(1,100)) plt.title('出現頻度ヒストグラム') plt.xlabel('出現頻度') plt.ylabel('単語数') plt.show() fig.savefig('38_image.png')
計数後にsurfaceを無視し、出現回数のみをリストに保存する。
plt.hist()で100本のヒストグラムを描画。
視認性をあげるためにlog=Trueとした。
39
import matplotlib.pyplot as plt import matplotlib import collections matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro' path = 'neko.txt.mecab' word_array = [] with open(path) as r: r_lines = r.read() r_split = r_lines.splitlines() for line in r_split: #line(example) = {一\t名詞,数,*,*,*,*,一,イチ,イチ} if line == 'EOS': # EOSは辞書登録しない。 continue #EOSでない単語は辞書登録 surface = line.split('\t')[0] base = line.split('\t')[1].split(',')[6] pos = line.split('\t')[1].split(',')[0] pos1 = line.split('\t')[1].split(',')[1] newword_dict = {'surface':surface, 'base':base, 'pos':pos, 'pos1':pos1} word_array.append(newword_dict) surface_list = [d['surface'] for d in word_array] word_count_by_surface = collections.Counter(surface_list) graph_list = word_count_by_surface.most_common() y = [taple[1] for taple in graph_list] x = range(1,len(y)+1) fig = plt.figure() plt.scatter(x, y, marker='.') plt.xscale('log') plt.yscale('log') plt.title('Zipfの法則') plt.xlabel('順位') plt.ylabel('出現回数') plt.show() fig.savefig('39_image.png')
出力はyは問題38のxと同様に出現回数である。
順位であるxは単純にrangeで生成。1から始める。
マーカを小さな丸とする散布図をplt.scatter(x, y, marker='.')で描画した。
Zipfの法則とは、
出現頻度が k 番目に大きい要素が全体に占める割合が 1/k に比例するという経験則
(ジップの法則 - Wikipedia)
であり、両対数グラフで直線的になっていることで確認できる。