Edited at

素人の言語処理100本ノック:36

More than 1 year has passed since last update.

言語処理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を用いるとよい.



36. 単語の出現頻度


文章中に出現する単語とその出現頻度を求め,出現頻度の高い順に並べよ.



出来上がったコード:


main.py

# coding: utf-8

import MeCab
from collections import Counter
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()
print(list_word)



実行結果:

長いので先頭部分のみです。


端末(先頭部分)

[('の', 9198), ('。', 7486), ('て', 6798), ('、', 6772), ('は', 6418), ('に', 6177), ('を', 6047), ('と', 5486), ('が', 5339), ('た', 3968), ('で', 3791), ('「', 3231), ('」', 3225), ('も', 2433), ('ない', 2386), ('だ', 2361), ('し', 2303), ('から', 2022), ('ある', 1717), ('な', 1607), ('か', 1553), ('ん', 1521), ('いる', 1250), ('事', 1205), ('へ', 1034), ('する', 988), ('う', 981), ('です', 977), ('君', 973), ('もの', 972), ('云う', 937), ('主人', 932), ('よう', 707), ('ね', 674), ('この', 641), ('ば', 624), ('御', 595), ('人', 590), ('何', 584), ('その', 582), ('一', 553), ('なる', 531), ('そう', 531), ('さ', 525), ('よ', 509), ('吾輩', 481),


結果全体はGitHubにアップしています。


collections.Counterオブジェクト

parse_neko()neco_lines()前問と同じです。

出現頻度の算出と並び替えは、collections.Counterオブジェクトを使えば簡単です。Counterdictのサブクラスで、イテレータを渡せば{要素, カウント}の辞書を自動的に作ってくれます。

イテレータはCounterオブジェクトの生成時に渡すこともできますし、update()で追加していくこともできます。今回は1行分ずつ追加していきました。

また、most_common()を使えば、出現頻度順に並んだリストが取り出せます。引数nを指定すれば、出現頻度が高い上位n件のみ取り出せます。

なお、GitHubに上げている結果全体は、後半に出現頻度1のものが大量に続きます。この部分は順不同なので、もし付き合わせなどに利用される際はご注意ください。

 

37本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第4章で用いているデータは青空文庫で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。