tbsmcd.net : Home | Archives | Tags | Search | Profile

言語処理100本ノック 2020 第4章

2020-05-03

fig

言語処理100本ノック 2020 - NLP100 2020

関連記事: tag 言語処理100本ノック

所感

コードを書く上で難しいということはあまりないが、門外漢のため問題文に用いられる用語が分からず苦戦した。これらのタームを難なく理解できることもこの課題の狙いだと(勝手に)考えることにする。
形態素解析には MeCab を使う。解析結果をファイル(neko.txt.mecab)に保存してから扱うので、mecab-python のようなライブラリをインストールする必要はないから導入は楽だと思う。

code

今回からは IntelliJ IDEA (+ Python プラグイン)を使った。
実際には全問を1ファイルで解いているが、本記事ではコメントや図形を挿入するために分割して記述している部分がある。

#!/usr/local/bin/python3
import pprint

path = '/path/to/neko.txt.mecab'

# 030
with open(path) as f:
    s_split = f.read().split('EOS\n')

sentences = []
for s in s_split:
    if s == '':
        continue
    words_list = []
    for w in s.split('\n'):
        if w == '':
            continue
        (surface, attr) = w.split('\t')
        attrs = attr.split(',')
        words_list.append({
            'surface': surface,
            'base': attrs[6],
            'pos': attrs[0],
            'pos1': attrs[1]
        })
    sentences.append(words_list)
pprint.pprint(words_list)

MeCab の出力フォーマット

# 031
# リストを均す
from itertools import chain

flattened = list(chain.from_iterable(sentences))
surface_list = []

for d in flattened:
    if d['pos'] == '動詞':
        surface_list.append(d['surface'])
print(surface_list)

list の flatten は itertool を使うと良いと公式ドキュメントにあった。 reduce を使ったりしてたが、こっちのほうが早いし楽。

# 032 内包表記にした
print([d['base'] for d in flattened if d['pos'] == '動詞'])

# 033
no_list = []
for i in range(len(flattened) - 2):
    if flattened[i]['pos'] == '名詞' and flattened[i + 1]['surface'] == '' and flattened[i + 2]['pos'] == '名詞':
        no_list.append(flattened[i]['surface'] + '' + flattened[i + 2]['surface'])

print(no_list)

# 034
connection = ''
connection_list = []
for d in flattened:
    if d['pos'] == '名詞':
        connection += d['surface']
    else:
        if connection != '':
            connection_list.append(connection)
            connection = ''

print(connection_list)

# 035
from collections import Counter

c = Counter([d['surface'] for d in flattened])
most_common_list = c.most_common()
print(most_common_list)

出現回数を数える場合は Counter を使うのが便利で、特に[most_common](https://docs.python.org/ja/3/library/collections.html#collections.Counter.most_common] はタプル (要素, 出現回数) を出現回数順に取得できる。

# 036
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams

height = np.array([most_common_list[i][1] for i in range(10)])
left = np.array([most_common_list[i][0] for i in range(10)])

fig = plt.figure()
rcParams["font.family"] = 'Hiragino Maru Gothic Pro'
plt.bar(left, height)
fig.savefig('036.png')
fig 036

Linux だったら IPAex フォントを入れておくのが良いのだろうか。 Mac なのでプリインストールのフォントを使った。

# 037
cooccurrences = []
for s in sentences:
    words = [x['surface'] for x in s]
    if '' in words:
        cooccurrences.extend([x for x in words if x != ''])

c = Counter(cooccurrences)
most_common_list_cat = c.most_common()

height = np.array([most_common_list_cat[i][1] for i in range(10)])
left = np.array([most_common_list_cat[i][0] for i in range(10)])

fig = plt.figure()
rcParams["font.family"] = 'Hiragino Maru Gothic Pro'
plt.bar(left, height)
fig.savefig('037.png')

fig 037

「猫」と同じ文中に現れる回数を計算すればよいだろうか。きれいに書こうとしなければ簡単。

# 038
fig = plt.figure()
plt.hist([x[1] for x in most_common_list], bins=20, range=(1, 100))
fig.savefig('038.png')
fig 038

035 の結果を使う。表示する頻度を全体にすると可視化の意味がなくなるので100位程度までに絞る。


# 039
fig = plt.figure()
plt.scatter([i+1 for i in range(len(most_common_list))], [x[1] for x in most_common_list])
plt.xscale('log')
plt.yscale('log')
fig.savefig('039.png')

fig 039

Tags: 言語処理100本ノック Python