#はじめに
言語処理100本ノック 2020 (Rev 1)の第4章: 形態素解析の自分なりの解答です。
どんどん雑になってきてる
- 環境
- google colab
#目次
###第4章: 形態素解析
30. 形態素解析結果の読み込み
31. 動詞
32. 動詞の原形
33. 「AのB」
34. 名詞の連接
35. 単語の出現頻度
36. 頻度上位10語
37. 「猫」と共起頻度の高い上位10語
38. ヒストグラム
39. Zipfの法則
夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ.
なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい
#30. 形態素解析結果の読み込み
形態素解析結果(neko.txt.mecab)を読み込むプログラムを実装せよ.ただし,各形態素は表層形(surface),基本形(base),品詞(pos),品詞細分類1(pos1)をキーとするマッピング型に格納し,1文を形態素(マッピング型)のリストとして表現せよ.第4章の残りの問題では,ここで作ったプログラムを活用せよ.
[
[{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...], 1文
[{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...],
.
.
[{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...]
]
import MeCab
#改行ごとに区切る
def split_sentence(sentence):
li = sentence.split('\n')
return li
def split_words(words):
li = []
for i in words:
li.append(i.split('\t'))
return li
def get_element(morphoed_sentence):
li =[]
for i in morphoed_sentence:
if i[0] == 'EOS':
break
pos = i[4].split('-')
#print(i)
#print(pos)
if len(pos) == 1:
dic = {'surface':i[0], 'base':i[3], 'pos':pos[0] ,'pos1':pos[0]}
elif len(pos) >= 2:
dic = {'surface':i[0], 'base':i[3], 'pos':pos[0] ,'pos1':pos[1]}
li.append(dic)
return li
#1文づつ入れていく
def get_morpho(sentence):
# 形態素解析
sentence = tagger.parse(sentence)
# 各形態素ごとにリスト化
sentence = split_sentence(sentence)
# 各形態素の要素のリスト化 ['坊主', 'ボーズ', 'ボウズ', '坊主', '名詞-普通名詞-一般', '', '', '1']
sentence = split_words(sentence)
#指定要素の取得
elements_list = get_element(sentence)
return elements_list
#1行ごとにリスト化
def load_text(path):
'''
path : file path
return : 行ごとのリスト
'''
li = []
with open(path,'r') as neko:
for sentence in neko:
sentence = sentence.strip('\r\n')
#空行を避ける
if sentence == '':
continue
li.append(sentence)
return li
#neko.txt.mecabを読み込む
path = 'neko.txt.mecab'
def processor(path):
li = []
sentence = load_text(path)
for i in sentence:
li.append(get_morpho(i))
return li
res = processor(path)
for i in range(5):
print(res[i])
#31. 動詞
動詞の表層形をすべて抽出せよ.
for i in res:
for j in i:
if j["pos"] == "動詞":
print(j["surface"])
行く
し
廻っ
する
ある
択ば
出
来
ある
#32. 動詞の原形
動詞の原形をすべて抽出せよ.
for i in res:
for j in i:
if j["pos"] == "動詞":
print(j["base"])
行く
為る
回る
為る
有る
選ぶ
出る
来る
有る
#33. 「AのB」
2つの名詞が「の」で連結されている名詞句を抽出せよ.
#####コメント
「の」がきた時にその前の単語と後の単語が名詞
だとそれを抽出する。
各文の単語の+1,-1としててout of range
とかになるかなと思いましたが文構造的にならないと思ったのでこのようにしました.
for ind,i in enumerate(res):
#print(i)
for index,j in enumerate(i):
#print(index)
#print(j)
if j['surface'] == 'の' and res[ind][index-1]['pos'] == '名詞' and res[ind][index+1]['pos'] == '名詞':
print('{}の{}'.format(res[ind][index-1]['surface'],res[ind][index+1]['surface']))
掌の上
書生の顔
ものの見
はずの顔
顔の真中
穴の中
書生の掌
掌の裏
#34. 名詞の連接
名詞の連接(連続して出現する名詞)を最長一致で抽出せよ.
#####コメント
一文ごとに名詞のインデックスをリスト(noun_li)
に追加する、それ以外は−1を追加
連結の操作はコードみてください
最後にnoun_li
をリフレッシュしてなくてしばらく迷走してた。前も同じようなことした気がする
#noun_liをリフレッシュする
noun_li =[]
noun = ''
for ind,i in enumerate(res): #各行を送り出す ind:行数
# print(i)
for index,j in enumerate(i): #各行の要素を調べる index:要素のある場所
# print(j)
if j['pos'] == '名詞':
noun_li.append(index)
else:
noun_li.append(-1)
# print('-------------------------------')
noun_li.append("EOL")
# print(noun_li)
for k in noun_li:
if k != 'EOL':
if k > 0:
noun += res[ind][k]['surface']
else:
if len(noun) > 0:
print(noun)
noun = ''
else:
if len(noun) > 0:
print(noun)
noun = ''
noun_li.clear()
いたずら
両人
揃い
事
最初
うち
各自任意
行動
#35. 単語の出現頻度
文章中に出現する単語とその出現頻度を求め,出現頻度の高い順に並べよ.
def word_counter(data):
dic = {}
for i in data:
for j in i:
key = j['surface']
dic[key] = dic.get(key,0) + 1
return dic
li = []
word_counter_dic = word_counter(res)
for word, count in sorted(word_counter_dic.items(),key=lambda x:-x[1]):
li.append('{} : {}'.format(word,count))
for i in range(10):
print(li[i])
の : 9541
。 : 7486
て : 7418
に : 7060
、 : 6773
は : 6501
と : 6156
を : 6119
が : 5394
で : 4543
#36. 頻度上位10語
出現頻度が高い10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.
######コメント
日本語は文字化けするからいろいろ処理が必要、追い詰められたらやります
内包表記使えば自動で欲しい番目までリスト化できるの失念してた
import matplotlib.pyplot as plt
label = words[0:10]
left = [1,2,3,4,5,6,7,8,9,10]
height = counts[0:10]
plt.bar(left, height, tick_label=label, align="center")
#37. 「猫」と共起頻度の高い上位10語
「猫」とよく共起する(共起頻度が高い)10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.
#####コメント
共起する単語の定義は文の中に猫
があるモノの単語とします
import matplotlib.pyplot as plt
import japanize_matplotlib
#猫と共起する単語とその頻度の辞書を返す関数
def neko_co(data):
neko_co_occurence ={}
for sentence in data:
if '猫' in [words['surface'] for words in sentence]:
#print(sentence)
for word in [words['surface'] for words in sentence]: # word は key
#print(word)
neko_co_occurence[word] = neko_co_occurence.get(word,0) + 1
return neko_co_occurence
neko_co_dic = neko_co(res)
print(neko_co_dic)
del neko_co_dic['猫']
neko_co_word,neko_co_count = [],[]
for word, count in sorted(neko_co_dic.items(),key=lambda x:-x[1]):
neko_co_word.append(word)
neko_co_count.append(count)
label = neko_co_word[0:10]
left = [i+1 for i in range(10)]
height = neko_co_count[0:10]
plt.bar(left, height, tick_label=label, align="center")
print(neko_co_word)
print(neko_co_count)
{'吾輩': 55, 'は': 262, '猫': 237, 'で': 181, 'ある': 96, '。': 200,
['の', 'に', 'は', 'て', '、', 'と', 'を', '。', 'で', 'が', 'も', 'た', 'ある', 'し', 'ない', 'だ', 'な', 'から', '事', '吾輩',
[377, 264, 262, 240, 240, 221, 208, 200, 181, 169, 133, 117, 96, 96, 88, 66, 62, 61, 58, 55,
#38. ヒストグラム
単語の出現頻度のヒストグラムを描け.ただし,横軸は出現頻度を表し,1から単語の出現頻度の最大値までの線形目盛とする.縦軸はx軸で示される出現頻度となった単語の異なり数(種類数)である.
#####コメント
あってるのかわからない
1、2とかの数が多すぎて他の頻度が潰れてる.
横軸の1棒の範囲が1000になってるからだと思う
import matplotlib.pyplot as plt
#1-9541
x = re_counts
plt.hist(x)
plt.show()
#39. Zipfの法則
単語の出現頻度順位を横軸,その出現頻度を縦軸として,両対数グラフをプロットせよ.
#rank r
#freaquence f
# words words
r = [i+1 for i in range(len(counts))]
f = counts
import matplotlib.pyplot as plt
import japanize_matplotlib
fig, ax = plt.subplots()
ax.plot(r, f,'.')
plt.scatter(r,f,s=0.5)
ax.set_yscale('log') # y軸をlogスケールで描く
ax.set_xscale('log') # x軸をlogスケールで描く
plt.show()
#感想
ゴリ押しでなんとかなったような気もするけど自分の回答が効率的なのかどうかよくわからないので他の人のもみてみようと思う