2018年04月12日

言語処理100本ノックでPython入門 #35 - 名詞の連接


言語処理100本ノック 2015の問題35に挑戦です。

■ 問題
夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

35. 名詞の連接
名詞の連接(連続して出現する名詞)を最長一致で抽出せよ.

■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractContinuousNouns(lines):
    for sentense in lines:
        noun = ''
        count = 0
        for word in sentense:
            if word['pos'] == '名詞' and (noun != '' or word['pos1'] != '副詞可能'):
                noun += word['surface']
                count += 1
            else:
                if count > 1:
                    yield noun
                noun = ''
                count = 0

def main():
    article = analyze()
    nouns = [surface for surface in extractContinuousNouns(article)]
    print(nouns)

if __name__ == '__main__':
    main()

■ ちょっと説明

単純に名詞の連続だと
'その後猫'  'この間おさん'  '自ら筆'  '元来放蕩家' 
などちょっとおかしな単語も引っ張ってきてしまっているので、連続した名詞の先頭のpos1が副詞可能な場合を除外してみました。

しかし、そうすると、 '前後不覚'  'ふだん着' などいくつか除外してほしくない単語も除外されてしまいました。 まあどっちもどっちですね。

この精度を高めようとすると、辞書登録をするしかないですかね。
それとも、pos2の内容もみて条件判定をするといった泥臭いコードを書く必要があるのでしょうか。

それと、ひさしぶりに if else 使いました。elseにの後ろにも:付けるんだった。すっかり忘れていました。


■ 結果

結果の先頭部分を載せます。
['人間中', '時妙', '一毛', '一度', 'ぷうぷうと煙', '邸内', '三毛', '書生以外', '四五遍', '三馬', '御台所', 'まま奥', '住家', '勉強家', '勉強家', '勤勉家', '二三ページ', '主人以外', '限り吾輩', '二人', '一つ床', '一人', '最後大変', '——猫', '神経胃弱性', '物指', '尻ぺたをひどく', '言語同断',
あまり良い結果とは言えないです。

「ぶうぶうと煙」は、どう考えても名詞の連結とは言えないよなー。
   

Posted by gushwell at 22:30Comments(0)Python

2018年04月09日

『業務システム開発モダナイゼーションガイド』とても良い本です


今年の2月に日経BP社から発売された『業務システム開発モダナイゼーションガイド 非効率な日本のSIを変革する実践的ベストプラクティス』を読み終わりました。

マイクロソフトコンサルティング本部で長年SI業界の開発現場を見てきた著者だから書ける渾身の一冊です。


赤間信幸(著)
日経BP社

本書は、主に、企業のIT部門、Sierおよびその関連企業でソフトウェア開発に関わる人々を対象にした本で、タイトルの通り、業務システム開発をいかに正しい方向に導き、開発現場を近代化できるようにしたら良いかを説いた内容となっています。

日本のSI業界が抱える問題点をどう改善しソフトウェア開発を進化させたら良いのか悩んでいる方には、得るものがたくさんあると思います。

エンジニアだけではなく、マネジメント層の方にも是非読んでほしい本です。
要件定義から設計、実装、テストといった各工程で何をやったら良いかの指針が示されています。

この本を読んで好感が持てるのは、理想論や頭でっかちの理論で「ソフトウェア開発はこうあるべき」と論じるのではなく、現実を直視し、コストと開発効率と品質のバランスを取り、やらなくて良いものはやらないという潔さがある点です。 そして、なぜそうするのか、何のためにそうするのかというWhyとWhatに焦点を当てている点です。

著者の赤間さんがマイクロソフトコンサルタントということもあり、MS製品を例に取っている箇所も多いですが、それは本質的な部分ではないので、MS製品を使った開発をしていない方にも十分読む価値があると思います。

筆者のIT業界を良くしたいという思いが伝わってくる本です。

    
Posted by gushwell at 21:30Comments(0)

2018年04月08日

言語処理100本ノックでPython入門 #34 - namedtuple


言語処理100本ノック 2015の問題34に挑戦です。

■ 問題
夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

34. 「AのB」
2つの名詞が「の」で連結されている名詞句を抽出せよ.
---

この問題は、入力ファイルの中から以下のようなものを見つけるということですね。
書生    名詞,一般,*,*,*,*,書生,ショセイ,ショセイ
の    助詞,連体化,*,*,*,*,の,ノ,ノ
顔    名詞,一般,*,*,*,*,顔,カオ,カオ

■ Pythonのコード
import re
from collections import namedtuple

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractNounphrases(lines):
    Morpheme = namedtuple('Morpheme', ['surface', 'pos'])
    for sentense in lines:
        prev2 = Morpheme('', '')
        prev1 = Morpheme('', '')
        for word in sentense:
            if word['pos'] == '名詞' and prev1.surface == 'の' and \
               prev1.pos == '助詞' and prev2.pos == '名詞':
                yield prev2.surface + prev1.surface + word['surface']
            prev2 = prev1
            prev1 = Morpheme(word['surface'], word['pos'])

def main():
    article = analyze()
    nouns = [surface for surface in extractNounphrases(article)]
    print(nouns[:50])

if __name__ == '__main__':
    main()

■ namedtuple


あまりスマートなやり方とは思えませんが、2行前までの情報を覚えておいて、「の」でつながる名詞句を求めています。

変数prev2が2つ前、prev1が一つ前の情報で、表層形と品詞を記憶するのにnamedtupleを使っています。 namedtupleは、その名の通り、フィールドに名前が付いたタプルです。

Pythonについて何か新しい知識を得ようと、Webをうろうろしていたらnamedtupleというのがあることを知りました。 この問題でも使えそうなので早速使ってみました。
from collections import namedtuple
でnamedtupleをインポートして、
Morpheme = namedtuple('Morpheme', ['surface', 'pos'])
とすると、namedtupleを定義できます。

上のコードは、Morphemeという名前のタプルを定義しています。 なんで、第1引数が必要なのか疑問ですが、そういうものなのだと納得することにします。 第2引数で、'surface' と 'pos'というフィールドメンバーを持つことを指定しています。 オブジェクトを作成するには、以下のように記述します。
item = Morpheme('吾輩','名詞')
これで、item.surfaceに'吾輩'が、item.posに'名詞'が設定されます。


■ 結果

結果は先頭の50個を表示しています。
['彼の掌', '掌の上', '書生の顔', 'はずの顔', '顔の真中', '穴の中', '書生の掌', '掌の裏', '何の事', '肝心の母親', '藁の上', '笹原の中', '池の前', '池の上', '一樹の蔭', '垣根の穴', '隣家の三', '時の通路', '一刻の猶予', '家の内', '彼の書生', '以外の人間', '前の書生', 'おさんの隙', 'おさんの三', '胸の痞', '家の主人', '主人の方', '鼻の下', '吾輩の顔', '自分の住', '吾輩の主人', '家のもの', 'うちのもの', '彼の書斎', '本の上', '皮膚の色', '本の上', '彼の毎夜', '以外のもの', '主人の傍', '彼の膝', '膝の上', '経験の上', '飯櫃の上', '炬燵の上', 'ここのうち', '供の寝床', '彼等の中間', '供の一']

  
Posted by gushwell at 21:47Comments(0)Python

2018年04月05日

言語処理100本ノックでPython入門 #33 - サ変接続の名詞


言語処理100本ノック 2015の問題33に挑戦です。

■ 問題


夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

33. サ変名詞
サ変接続の名詞をすべて抽出せよ.


■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractNouns(lines):
    for sentense in lines:
        seq = filter(lambda w: w['pos'] == '名詞' and w['pos1'] == 'サ変接続', sentense)
        yield from map(lambda w: w['surface'], seq)

def main():
    article = analyze()
    nouns = [surface for surface in extractNouns(article)]
    print(nouns[:50])

if __name__ == '__main__':
    main()


書いたコードについては、前回とほとんど同じなので、特筆すべき点はないです。

ラムダ式が少し複雑になったけど、タプルではなくて文字列を列挙しているので、プログラミングの観点からは、こちらのほうが易しいですね。

論理演算子の論理積が&&じゃなくてandなのがどうも面倒です。いつも&&と書いてエラーになっています。

ところで、このところ新しいし知識を仕入れなくても問題を解くことができるようになってきました。 文法で主要なのはあとはクラスくらいでしょうか? No.33以降の問題文を見てみると、第5章にクラスを実装せよという問題があるので、それまではクラスを使わずに解いていこうと思います。


■ 結果

プログラムでは取り出した先頭50個を表示しています。
なるほど、こういったものが、サ変接続の名詞なのか。
['見当', '記憶', '話', '装飾', '突起', '運転', '記憶', '分別', '決心', '我慢', '餓死', '訪問', '始末', '猶予', '遭遇', '我慢', '記憶', '返報', '勉強', '勉強', '昼寝', '珍重', '昼寝', '経験', '供', '供', '供', '供', '——', '——', '同居', '観察', '断言', '同衾', '供', '迫害', '尊敬', '生活', '剿滅', '議論', '所有', '憤慨', '観念', '御馳走', '掠奪', '代言', '我儘', '我儘', '失敗', '話']
  
Posted by gushwell at 22:10Comments(0)Python

2018年04月04日

言語処理100本ノックでPython入門 #32 - タプルをリスト内包表記で利用する


言語処理100本ノック 2015の問題32に挑戦です。

このあたりまで来ると、Pythonの言語機能を習得するという問題から、やりたいことをどうコードに落とし込むかという問題に焦点が移ってきていますね。

■ 問題


夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

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

■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractVerbs(lines):
    for sentense in lines:
        seq = filter(lambda w: w['pos'] == '動詞', sentense)
        yield from map(lambda w: (w['surface'], w['base']), seq)

def main():
    article = analyze()
    verbs = [(surface, base) for surface, base in extractVerbs(article)]
    print(verbs[:50])

if __name__ == '__main__':
    main()

■ ちょっとコードの解説


analyze関数は、問題30で作成したものと同じです。 せっかっくなので、動詞と原型を対で取得してみました。タプルを使っています。
(w['surface'], w['base'])
というコードを map関数の中で使って、yield fromで列挙しています。

main関数では、これを受け取り、リスト内包表記使ってリストに格納しています。
verbs = [(surface, base) for surface, base in extractVerbs(article)]
一つ目を取り出すならば、
print(verbs[0][0], verbs[0][1])
これで、
生れ 生れる
が表示されます。

ファイルに出力せよとか、表示せよとかの指定がないので、抽出したタプルのリストの先頭50個をprintに渡しています。

■ 結果

先頭50個を出力しています。
[('生れ', '生れる'), ('つか', 'つく'), ('し', 'する'), ('泣い', '泣く'), ('し', 'する'), ('いる', 'いる'), ('始め', '始める'), ('見', '見る'), ('聞く', '聞く'), ('捕え', '捕える'), ('煮', '煮る'), ('食う', '食う'), ('思わ', '思う'), ('載せ', '載せる'), ('られ', 'られる'), ('持ち上げ', '持ち上げる'), ('られ', 'られる'), ('し', 'する'), ('あっ', 'ある'), ('落ちつい', '落ちつく'), ('見', '見る'), ('見', '見る'), ('思っ', '思う'), ('残っ', '残る'), ('いる', 'いる'), ('さ', 'する'), ('れ', 'れる'), ('し', 'する'), ('逢っ', '逢う'), ('出会わ', '出会う'), ('し', 'する'), ('のみ', 'のむ'), ('なら', 'なる'), ('し', 'する'), ('いる', 'いる'), ('吹く', '吹く'), ('せ', 'する'), ('弱っ', '弱る'), ('飲む', '飲む'), ('知っ', '知る'), ('坐っ', '坐る'), ('おっ', 'おる'), ('する', 'する'), ('し', 'する'), ('始め', '始める'), ('動く', '動く'), ('動く', '動く'), ('分ら', '分る'), ('廻る', '廻る'), ('なる', 'なる')]
  
Posted by gushwell at 22:00Comments(0)Python

2018年04月02日

言語処理100本ノックでPython入門 #31 - yield fromが便利すぎる


言語処理100本ノック 2015の問題31を解きました。

■ 問題

夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

31. 動詞
動詞の表層形をすべて抽出せよ.


■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractVerbs(lines):
    for sentense in lines:
        seq = filter(lambda w: w['pos'] == '動詞', sentense)
        yield from map(lambda w: w['surface'], seq)

def main():
    article = analyze()
    verbs = [surface for surface in extractVerbs(article)]
    print(verbs[:50])
    print('-----')

if __name__ == '__main__':
    main()

■ ちょっとコードの解説


analyze関数は問題30で書いたものと同じです。このanalyzeの結果を使い、今回書いたextractVerbs関数で、動詞だけを抜き出しています。

キー'pos'の値を見れば、動詞かどうかが判断できます。ちなみに pos は、品詞: part of speech の頭文字だと思われます。

extractVerbs関数は、以下のforとifを使ったコードと同等です。
def extractVerbs(lines):
    for sentense in lines:
        for word in sentense:
            if word['pos'] == '動詞':
                yield word['surface']
上のコードは、for文で回しているだけなので、何も難しいことはやっていません。

抜き出した動詞は、yieldで列挙しています。 今回はfilter関数とmap関数を使って書いてみたのですが、1行に書きたいけど、1行だと長くなっちゃうし読みやすいとは言えないかな。
C#のLINQみたいに、メソッドチェーンできればいいのですが...

でも yield from 構文はすごく便利ですね。これはC#にも欲しい機能です。 yield from 構文が無いと、以下のように書かなくてはなりません。
seq = filter(lambda w: w['pos'] == '動詞', sentense)
seq = map(lambda w: w['surface'], seq)
for w in seq:
    yield w

main関数では、リスト内包表記使ってリストにしています。

■ 結果

取り出した結果の先頭50個を表示しています。
['生れ', 'つか', 'し', '泣い', 'し', 'いる', '始め', '見', '聞く', '捕え', '煮', '食う', '思わ', '載せ', 'られ', '持ち上げ', 'られ', 'し', 'あっ', '落ちつい', '見', '見', '思っ', '残っ', 'いる', 'さ', 'れ', 'し', '逢っ', '出会わ', 'し', 'のみ', 'なら', 'し', 'いる', '吹く', 'せ', '弱っ', '飲む', '知っ', '坐っ', 'おっ', 'する', 'し', '始め', '動く', '動く', '分ら', '廻る', 'なる']
  
Posted by gushwell at 22:34Comments(0)Python