言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。
第4章: 形態素解析
夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ.
なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.
###39. Zipfの法則
単語の出現頻度順位を横軸,その出現頻度を縦軸として,両対数グラフをプロットせよ.
####出来上がったコード:
# coding: utf-8
import MeCab
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
fname = 'neko.txt'
fname_parsed = 'neko.txt.mecab'
def parse_neko():
'''「吾輩は猫である」を形態素解析
「吾輩は猫である」(neko.txt)を形態素解析してneko.txt.mecabに保存する
'''
with open(fname) as data_file, \
open(fname_parsed, mode='w') as out_file:
mecab = MeCab.Tagger()
out_file.write(mecab.parse(data_file.read()))
def neco_lines():
'''「吾輩は猫である」の形態素解析結果のジェネレータ
「吾輩は猫である」の形態素解析結果を順次読み込んで、各形態素を
・表層形(surface)
・基本形(base)
・品詞(pos)
・品詞細分類1(pos1)
の4つをキーとする辞書に格納し、1文ずつ、この辞書のリストとして返す
戻り値:
1文の各形態素を辞書化したリスト
'''
with open(fname_parsed) as file_parsed:
morphemes = []
for line in file_parsed:
# 表層形はtab区切り、それ以外は','区切りでバラす
cols = line.split('\t')
if(len(cols) < 2):
raise StopIteration # 区切りがなければ終了
res_cols = cols[1].split(',')
# 辞書作成、リストに追加
morpheme = {
'surface': cols[0],
'base': res_cols[6],
'pos': res_cols[0],
'pos1': res_cols[1]
}
morphemes.append(morpheme)
# 品詞細分類1が'句点'なら文の終わりと判定
if res_cols[1] == '句点':
yield morphemes
morphemes = []
# 形態素解析
parse_neko()
# Counterオブジェクトに単語をセット
word_counter = Counter()
for line in neco_lines():
word_counter.update([morpheme['surface'] for morpheme in line])
# 全件取得
list_word = word_counter.most_common()
# 出現数のリスト取得
counts = list(zip(*list_word))[1]
# グラフで使うフォント情報(デフォルトのままでは日本語が表示できない)
fp = FontProperties(
fname='/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf'
)
# 散布図のデータ指定
plt.scatter(
range(1, len(counts) + 1), # x軸:順位
counts # y軸:出現頻度
)
# 軸の値の範囲の調整
plt.xlim(1, len(counts) + 1)
plt.ylim(1, counts[0])
# 対数グラフに
plt.xscale('log')
plt.yscale('log')
# グラフのタイトル、ラベル指定
plt.title("39. Zipfの法則", fontproperties=fp)
plt.xlabel('出現度順位', fontproperties=fp)
plt.ylabel('出現頻度', fontproperties=fp)
# グリッドを表示
plt.grid(axis='both')
# 表示
plt.show()
###散布図
parse_neko()
とneco_lines()
は前問と同じです。
今回は散布図にしてみました。散布図はpyplot.scatter()
にx軸の配列とy軸の配列を渡せば作ってくれます。簡単!
対数グラフにするには、pyplot.xscale()
とpyplot.yscale()
で指定します。
###Zipfの法則とは
ジップとかジフとか読むそうで、何かを集計した時に、その出現頻度の高い順に並べると(個々の順位)×(その出現頻度)
が一定になる法則だそうです。この法則通りの場合は、両対数グラフに書くと斜め下がりの直線になります。確かになっていますね。
単語の出現頻度だけでなく、いろいろな自然現象、社会現象で成り立つ場合があるらしいのです。なんだか不思議。興味のある方はググってみてください。
40本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第4章で用いているデータは青空文庫で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。