LoginSignup
6
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-11-13

言語処理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章で用いているデータは青空文庫で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2