形態素解析
まずは問題を解くための準備からしていきます。
夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ.
なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.
今回はGoogleのColaboratoryを使って実装を行なっていきます。
Mecabを使用するのでインストール必要がありますが、Colaboratoryを使用している方は以下のコマンドでインストールしてあげてください。
1 2 3 4 5 |
!apt install aptitude !aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y !pip install mecab-python3==0.7 |
インストールを終えたら、Mecabを使って形態素解析を行います。
1 2 3 4 5 6 7 |
import MeCab with open('neko.txt', 'r') as f1, open('neko.txt.mecab', 'w') as f2: tagger = MeCab.Tagger() f2.write((tagger.parse(f1.read()))) |
30. 形態素解析結果の読み込み
形態素解析結果(neko.txt.mecab)を読み込むプログラムを実装せよ.ただし,各形態素は表層形(surface),基本形(base),品詞(pos),品詞細分類1(pos1)をキーとするマッピング型に格納し,1文を形態素(マッピング型)のリストとして表現せよ.第4章の残りの問題では,ここで作ったプログラムを活用せよ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
dic = [] s = [] with open('neko.txt.mecab') as f: for line in f: t1 = line.split('\t') if (len(t1)>=2): t2 = t1[1].split(',') s.append({'surface': t1[0], 'base': t2[6], 'pos': t2[0], 'pos1': t2[1]}) if (t2[1] == '句点'): dic.append(s) s = [] |
neko.txt.mecabを読み込んだ後にfor文で1行ずつ処理をしていきます。簡単ですね。
31. 動詞
動詞の表層形をすべて抽出せよ.
1 2 3 4 5 6 |
for s in dic: for w in s: if w['pos'] == '動詞': print(w['surface']) |
ここからは30で作ったディクショナリを使って処理をしていきます。
posで品詞が分かるので、動詞であるものだけを表示しています。
32. 動詞の原形
動詞の原形をすべて抽出せよ.
1 2 3 4 5 6 |
for s in dic: for w in s: if w['pos'] == '動詞': print(w['base']) |
surfaceを表示するかbaseを表示するかの違いですね。簡単です。
33. 「AのB」
2つの名詞が「の」で連結されている名詞句を抽出せよ.
1 2 3 4 5 6 7 |
for s in dic: if (len(s) >= 3): for i in range(1, len(s)-1): if (s[i-1]['pos']=='名詞' and s[i]['surface']=='の' and s[i+1]['pos']=='名詞'): print(s[i-1]['surface']+s[i]['surface']+s[i+1]['surface']) |
単語をfor文で回していきますが、ここでi番目が”の”、そしてi-1番目とi+1番目が名詞であるかどうかを確認しています。
ここで気をつけることとして、iを0から始めるとi-1が-1となってしまいエラーになるので、配列の1番目から始めて”配列の長さ-1″番目で終わるようにします。
34. 名詞の連接
名詞の連接(連続して出現する名詞)を最長一致で抽出せよ.
1 2 3 4 5 6 7 8 9 10 11 12 |
nouns = [] for s in dic: for i in range(len(s)): if (s[i]['pos']=='名詞'): nouns.append(s[i]['surface']) else: if (len(nouns)>1): print(''.join(nouns)) nouns = [] |
名詞の場合にnouns配列に単語を追加しています。このnouns配列の長さが2より大きい場合(=名詞の連接が起こっている場合)にまとめて出力しています。
35. 単語の出現頻度
文章中に出現する単語とその出現頻度を求め,出現頻度の高い順に並べよ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fre = {} for s in dic: for w in s: if w['surface'] not in fre: fre[w['surface']] = 1; else: fre[w['surface']] += 1; result = sorted(fre.items(), key=lambda x: x[1], reverse=True) print(result) |
処理自体は簡単ですが、sorted()関数のkey引数にラムダ式を渡しています。
この場合は”lambda x: x[1]”となっていますが、これはxが渡されたときにx[1]を返すという意味です。
このxとして渡されるのは1番目の引数であるfre.items()で、freは”key: value”という形でデータが格納されているのに対して、.items()の場合は”key, value”という形でデータが返されます。したがってvalueを元にソートしたい場合にはx[1]としているわけです。
matplotlibの日本語化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# matplotlib日本語化 # !apt-get -y install fonts-ipafont-gothic # フォントのインストール # import matplotlib # matplotlib.get_cachedir() # キャッシュディレクトリの取得 # !ls /root/.cache/matplotlib # フォントのキャッシュファイルの取得 # !rm /root/.cache/matplotlib/fontlist-v310.json # 削除 # ランタイム再起動 # テスト import matplotlib.pyplot as plt import seaborn as sns sns.set(font='IPAGothic') plt.plot([0, 1], [0, 1]) plt.xlabel('横軸') plt.ylabel('縦軸') plt.title('タイトル') plt.show() |
グラフの描画にmatplotlibを使用するのですが、どうやらこのままでは日本語を表示すると文字化けしてしまうので、日本語化します。
matplotlib.get_cachedir()でキャッシュディレクトリを取得したのち、そのキャッシュディレクトリ(colaboratoryの場合は/root/.cache/matplotli)のどこにフォントのキャッシュファイルがあるのかを探します。キャッシュファイルが見つかったらそれを削除してランタイムをリスタートしましょう。
テストコードを実行してこんな感じの画像が表示されれば成功です。

36. 頻度上位10語
出現頻度が高い10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.
1 2 3 4 5 6 7 |
x = dict(result[:10]).keys() y = dict(result[:10]).values() plt.bar(x, y) plt.xlabel('単語') plt.ylabel('頻度') |
簡単ですね。

37. 「猫」と共起頻度の高い上位10語
「猫」とよく共起する(共起頻度が高い)10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fre = {} for s in dic: if (any(i['base']=='猫' for i in s)): for w in s: if w['surface'] not in fre: fre[w['surface']] = 1; else: fre[w['surface']] += 1; result = sorted(fre.items(), key=lambda x: x[1] if(x[0]!='猫') else 0, reverse=True) print(result) |
問題35の処理に加えて、文章に猫という単語が含まれている場合にのみ頻度を計算するようにしています。
またsorted()を実行する際に、引数のkeyとしてラムダ式を与えていますが、ここで猫をソートの順位に含めないようにしています。
38. ヒストグラム
単語の出現頻度のヒストグラム(横軸に出現頻度,縦軸に出現頻度をとる単語の種類数を棒グラフで表したもの)を描け.
1 2 3 4 5 |
plt.hist(dict(result).values(), bins=30, range=(0, 30)) plt.xlabel('単語の種類数') plt.ylabel('頻度') |
これも簡単ですね。
39. Zipfの法則
単語の出現頻度順位を横軸,その出現頻度を縦軸として,両対数グラフをプロットせよ.
1 2 3 4 5 6 7 |
plt.plot(list(dict(result).values()), range(1, len(result)+1)) plt.xlabel('出現頻度順位') plt.ylabel('出現頻度') plt.xscale('log') plt.yscale('log') |
普通にプロットした後にxとy軸をscale()で対数目盛りにしています。
おわりに
何か不明点や間違いなどがございましたら、気軽にコメントを残してくれるとありがたいです。
ありがとうございました。