LoginSignup
6
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-11-05

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

32. 動詞の原形

動詞の原形をすべて抽出せよ.

出来上がったコード:

main.py
# coding: utf-8
import MeCab
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()

# 1文ずつ辞書のリストを取得し動詞を抽出
verbs = set()
verbs_test = []     # 確認用の出現順リスト
lines = neco_lines()
for line in lines:
    for morpheme in line:
        if morpheme['pos'] == '動詞':
            verbs.add(morpheme['base'])
            verbs_test.append(morpheme['base'])     # 確認用の出現順リストにも追加

# 確認しやすいようverbs_testを使って出現順にソートして表示
print(sorted(verbs, key=verbs_test.index))

実行結果:

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

端末(先頭部分)
['生れる', 'つく', 'する', '泣く', 'いる', '始める', '見る', '聞く', '捕える', '煮る', '食う', '思う', '載せる', 'られる', '持ち上げる', 'ある', '落ちつく', 'いう', '残る', 'れる', '逢う', '出会う', 'なる', '吹く', '咽せる', '弱る', '飲む', '知る', '坐る', 'おる', '動く', '分る', '廻る', '助かる', 'さる', '出る', '考え出す', '気が付く', '見える', '隠す', 'しまう', '違う', '明く', '這い出す', '棄てる', '考える', '迎る', '来る', 'くれる', '考え付く', 'やる', '渡る', '暮れる', 'かかる', '減る', 'あるく', 'そる', '這う', '行く', '這入る', '崩れる', 'もぐり込む', '破れる', '知れる', '云う', '至る', '忍び込む', '降る', '出来る', 'つかむ', '抛り出す', 'ねぶる', '任せる', '上る', '投げ出す', '繰り返す', 'おす', 'してやる', '下りる', 'つまみ出す', 'ぶら下げる', '向ける', '出す', '困る', '撚る', '眺める', '置く', '極める', '合せる', '帰る', '見せる', '覗く', '読みかける', 'たらす', '帯びる', 'あらわす', 'ひろげる', '読む', '垂らす', '限る', '寝る', '勤まる', 'せる', '鳴らす', '住み込む', '跳ねる', '付ける', 'つける', '得る', '入れる', 'つとめる', '乗る', '構う', 'やむを得る', '入る', 'ねる', '見出す', '割り込む', '醒ます', 

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

原形とは

おそらく原形とは基本形のことですよね。そのため、前問で結果に格納していたsurfacebaseに変えただけです。
実行後の状態のprint(verb_test)の結果も載せておきます。こちらは重複が含まれます。

print(verb_test)の結果(先頭部分)
['生れる', 'つく', 'する', '泣く', 'いる', 'する', 'いる', '始める', '見る', '聞く', '捕える', '煮る', '食う', '思う', '載せる', 'られる', '持ち上げる', 'られる', 'する', 'ある', '落ちつく', '見る', 'いう', '見る', '思う', '残る', 'いる', 'する', 'れる', 'する', '逢う', '出会う', 'する', 'なる', 'する', 'いる', '吹く', '咽せる', '弱る', '飲む', '知る', '坐る', 'おる', 'する', 'する', '始める', '動く', '動く', '分る', '廻る', 'なる', '助かる', '思う', 'いる', 'さる', 'する', '出る', 'する', 'いる', '考え出す', '分る', '気が付く', '見る', 'いる', 'おる', '見える', '隠す', 'しまう', '違う', '明く', 'いる', 'られる', '這い出す', '見る', '棄てる', 'られる', '這い出す', 'ある', '坐る', 'する', '考える', '見る', '出る', 'する', '泣く', '迎る', '来る', 'くれる', '考え付く', 'やる', '見る', '来る', '渡る', '暮れる', 'かかる', '減る', '来る', '泣く', '出る', 'ある', 'あるく', 'する', 'そる', '廻る', '始める', 'する', '這う', '行く', '出る', '這入る', 'なる', '思う', '崩れる', 'もぐり込む', '破れる', 'いる', 'する', '知れる', '云う', '至る', 'する', 'なる', 'いる', '忍び込む', '分る', 'なる', '減る', '降る', '来る', '出来る', 'なる', 'あるく', '行く', '考える', '這入る', 'おる', '見る', 'する', '逢う', '見る', 'つかむ', '抛り出す', '思う', 'ねぶる', '任せる', 'いる', '出来る', '見る', '這う', '上る', '投げ出す', 'れる', '投げ出す', 'れる', '這う', '上る', '這う', '上る', '投げ出す', 'れる', '繰り返す', 'する', 'いる', 'おす', '云う', 'なる', 'してやる', '下りる', 'つまみ出す', 'れる', 'する', 'いう', '出る', '来る', 'ぶら下げる', '向ける', '出す', '出す', '上る', '来る', '困る', 'いう', '撚る', '眺める', 'おる', '置く', 'やる', '這入る', 'しまう', '聞く', '見える', '抛り出す', '極める', 'する', '合せる', '帰る', '這入る', '出る', '来る', '思う', 'いる', '見せる', 'いる', 'いう', '覗く', '見る', 'する', 'いる', 'ある', '読みかける', 'ある', 'たらす', 'いる', '帯びる', 'あらわす', 'いる', '食う', '食う', '飲む', '飲む', 'ひろげる', '読む', 'なる', '垂らす', '繰り返す', '考える', 'ある', '生れる', 'なる', '限る', '寝る', 'いる', '勤まる', '出来る', '云う', 'せる', '来る', '鳴らす', 'いる', '住み込む', '行く', '跳ねる', '付ける', 'られる', 'する', 'くれる', 'する', 'れる', '至る', 'つける', 'くれる', '分る', '出来る', '得る', '入れる', 'くれる', 'いる', 'つとめる', '読む', '乗る', 'する', '乗る', '構う', 'やむを得る', '寝る', 'する', '入る', 'もぐり込む', 'ねる', 'なる', '入る', '寝る', '見出す', '割り込む', '醒ます',

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

内包表記

ループでリストを作っていますので、この部分の内包表記に挑戦してみました。2重ループになっているので、まずは内側のループだけを内包表記にしています。

main2.pyの後半部分
# 形態素解析
parse_neko()

# 1文ずつ辞書のリストを取得し動詞を抽出
list_verbs = []     # 出現順リスト、重複あり
for line in neco_lines():
    list_verbs.extend(
        morpheme['base'] for morpheme in line if morpheme['pos'] == '動詞'
    )
verbs = set(list_verbs)

# 確認しやすいようlist_verbsを使って出現順にソートして表示
print(sorted(verbs, key=list_verbs.index))

すでに内側を内包表記にしただけで混乱し始めてますが、同じ感じで外のループも内包表記にできそうですね。次回も似たような問題が続くので挑戦してみようと思います。

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


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

6
2
2

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