言語処理100本ノック-39(pandas使用):Zipfの法則


言語処理100本ノック 2015「第4章: 形態素解析」39本目「Zipfの法則」記録です。
「Zipfの法則」はWikipediaによると以下の説明で書かれていて、平たく言うと出現頻度が多ければ全体での割合が大きいという法則。

ジップの法則(ジップのほうそく、Zipf's law)あるいはジフの法則とは、出現頻度が k 番目に大きい要素が全体に占める割合が$\frac{1}{k}$に比例するという経験則

参考リンク

リンク 備考
039.Zipfの法則.ipynb 回答プログラムのGitHubリンク
素人の言語処理100本ノック:39 多くのソース部分のコピペ元
MeCab公式 最初に見ておくMeCabのページ

環境

種類 バージョン 内容
OS Ubuntu18.04.01 LTS 仮想で動かしています
pyenv 1.2.16 複数Python環境を使うことがあるのでpyenv使っています
Python 3.8.1 pyenv上でpython3.8.1を使っています
パッケージはvenvを使って管理しています
Mecab 0.996-5 apt-getでインストール

上記環境で、以下のPython追加パッケージを使っています。通常のpipでインストールするだけです。

種類 バージョン
matplotlib 3.1.3
pandas 1.0.1

第4章: 形態素解析

学習内容

夏目漱石の小説『吾輩は猫である』に形態素解析器MeCabを適用し,小説中の単語の統計を求めます.

形態素解析, MeCab, 品詞, 出現頻度, Zipfの法則, matplotlib, Gnuplot

ノック内容

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

なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

39. Zipfの法則

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

回答

回答プログラム 039.Zipfの法則.ipynb

import matplotlib.pyplot as plt
import pandas as pd

def read_text():
    # 0:表層形(surface)
    # 1:品詞(pos)
    # 2:品詞細分類1(pos1)
    # 7:基本形(base)
    df = pd.read_table('./neko.txt.mecab', sep='\t|,', header=None, 
                       usecols=[0, 1, 2, 7], names=['surface', 'pos', 'pos1', 'base'], 
                       skiprows=4, skipfooter=1 ,engine='python')
    return df[(df['pos'] != '空白') & (df['surface'] != 'EOS') & (df['pos'] != '記号')]

df = read_text()

frequency = df['surface'].value_counts().values.tolist()

plt.xscale('log')
plt.yscale('log')

plt.xlim(1, len(frequency)+1)
plt.ylim(1, frequency[0])
plt.xlabel('ランク')
plt.ylabel('出現頻度')

plt.scatter(x=range(1, len(frequency)+1), y=df['surface'].value_counts().values.tolist())

回答解説

出現頻度のリスト化

value_counts関数でユニークな頻度をカウントし、tolist関数でリスト化しています。

frequency = df['surface'].value_counts().values.tolist()

ログスケール表示

今回は問題文が「両対数グラフ」なので、ログスケールにしています。

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

plt.xscale('log')
plt.yscale('log')

x軸はリストの長さ+1(Pythonは0から始まるので)を最大値にして、y軸は降順ソートで最も多い0番目の値を最大値にしています。

plt.xlim(1, len(frequency)+1)
plt.ylim(1, frequency[0])

出力結果(実行結果)

プログラム実行すると以下の結果が出力されます。見事に右肩下がりになっていますね。