言語処理100本ノック 第4章: 形態素解析 (後編)

はじめに

今回は 言語処理100本ノック 第4章: 形態素解析 の後編です。

これまでの

ohshige.hatenablog.com ohshige.hatenablog.com

第4章: 形態素解析

Python 3.7.0 でやっていきます。 問題の解釈違い、間違い等ありましたら、教えていただけると幸いです。

github.com

前回の続きです。

35. 名詞の連接

問題

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

解答&出力

from div04.sec30 import get_neko_morphemes

morphemes_list = get_neko_morphemes()

result = []

for morphemes in morphemes_list:
    noun_list = []
    for morpheme in morphemes:
        if morpheme["pos"] == "名詞":
            noun_list.append(morpheme["surface"])
        else:
            if len(noun_list) > 1:
                result.append("".join(noun_list))
            noun_list = []
    else:
        if len(noun_list) > 1:
            result.append("".join(noun_list))

print(result[:10])
['人間中', '一番獰悪', '時妙', '一毛', 'その後猫', '一度', 'ぷうぷうと煙', '邸内', '三毛', '書生以外']

ひとこと

順番に見ていって、名詞が連続している限り配列に格納して、名詞でないときにその配列を連結させるようにしています。
結果を全て出力すると大変なので、最初の10件だけを出力しています。(以降、同様)

36. 単語の出現頻度

問題

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

解答&出力

from collections import Counter
from div04.sec30 import get_neko_morphemes

morphemes_list = get_neko_morphemes()

words = Counter([morpheme["base"] for morphemes in morphemes_list for morpheme in morphemes]).most_common()
print(words[:10])
[('の', 9194), ('。', 7486), ('て', 6848), ('、', 6772), ('は', 6420), ('に', 6243), ('を', 6071), ('だ', 5975), ('と', 5508), ('が', 5337)]

ひとこと

collections.Counterを使えば簡単に出現頻度を集計してくれるので楽です。
さらに、most_common()メソッドを使えば、出現頻度順に並べた上で特定の頻度以上のペア(出現対象と頻度)を抽出できるので大変便利です。

37. 頻度上位10語

問題

出現頻度が高い10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.

解答&出力

from collections import Counter
import matplotlib.pyplot as plt
from div04.sec30 import get_neko_morphemes

morphemes_list = get_neko_morphemes()

words = Counter([morpheme["base"] for morphemes in morphemes_list for morpheme in morphemes]).most_common()
word_name, word_count = list(zip(*words[:10]))

plt.rcParams["font.family"] = "IPAexGothic"
plt.bar(range(10), word_count, tick_label=word_name)
plt.savefig("fig37.png")

f:id:ohshige:20190529213330p:plain:w500

ひとこと

matplotlibの使い方で、詰まった部分がいくつかあったので他のブログ様を参考にしました。

from matplotlib.backends import _macosx

が出たときの対処はこちらを参考にしました。
Pythonでmatplotlibをimportするとエラーが出る場合の対処策(Mac) - Qiita

文字化けしてしまったときの対処はこちらを参考にしました。
matplotlibの文字化け問題でハマった時のメモ - Qiita

基本的には問題36を流用して、結果を棒グラフとして出力するようにしただけです。

38. ヒストグラム

問題

単語の出現頻度のヒストグラム(横軸に出現頻度,縦軸に出現頻度をとる単語の種類数を棒グラフで表したもの)を描け.

解答&出力

from collections import Counter
import matplotlib.pyplot as plt
from div04.sec30 import get_neko_morphemes

morphemes_list = get_neko_morphemes()

words = Counter([morpheme["base"] for morphemes in morphemes_list for morpheme in morphemes]).most_common()
_, word_count = list(zip(*words))

plt.rcParams["font.family"] = "IPAexGothic"
plt.hist(word_count, bins=50, range=(1, 50))
plt.savefig("fig38.png")

f:id:ohshige:20190529213835p:plain:w500

ひとこと

これも基本的には問題36を流用して、結果をヒストグラムとして出力するようにしただけです。
ですが、そのままやってしまうと出現頻度が偏りすぎていて情報量がほぼない結果になってしまうので、ビンの数を50に増やして(デフォルトだと10)、範囲を1〜50に狭めました。
対数をとっても良かったかもしれないです。

39. Zipfの法則

問題

単語の出現頻度順位を横軸,その出現頻度を縦軸として,両対数グラフをプロットせよ.

解答&出力

from collections import Counter
import matplotlib.pyplot as plt
from div04.sec30 import get_neko_morphemes

morphemes_list = get_neko_morphemes()

words = Counter([morpheme["base"] for morphemes in morphemes_list for morpheme in morphemes]).most_common()
_, word_count = list(zip(*words))

plt.rcParams["font.family"] = "IPAexGothic"
plt.plot(list(range(1, len(word_count) + 1)), word_count)
plt.xscale("log")
plt.yscale("log")
plt.savefig("fig39.png")

f:id:ohshige:20190529214341p:plain:w500

ひとこと

これも基本的には問題36を流用して、出現頻度順位と出現頻度を対数軸にプロットしただけです。
うまくZipfの法則が現れていると思います。
ジップの法則 - Wikipedia

おわりに

4章の後半はmatplotlibの使い方が慣れておらず難しいだけで、実装自体は特に悩むこともなくさくさくできました。

最近、100本ノックのもくもく会に参加できておらず、全然進んでいないのですが、引き続き頑張っていきたいです。

続き

まだ