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

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)[「第4章: 形態素解析」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch4)の[39本目「Zipfの法則」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec39)記録です。
「Zipfの法則」は[Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%83%E3%83%97%E3%81%AE%E6%B3%95%E5%89%87)によると以下の説明で書かれていて、平たく言うと**出現頻度が多ければ全体での割合が大きい**という法則。
> ジップの法則（ジップのほうそく、Zipf's law）あるいはジフの法則とは、出現頻度が k 番目に大きい要素が全体に占める割合が$\frac{1}{k}$に比例するという経験則


# 参考リンク

|リンク|備考|
|:--|:--|
|[039.Zipfの法則.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/039.Zipf%E3%81%AE%E6%B3%95%E5%89%87.ipynb)|回答プログラムのGitHubリンク|
|[素人の言語処理100本ノック:39](https://qiita.com/segavvy/items/e0a7994cc63c8be7380b)|多くのソース部分のコピペ元|
|[MeCab公式](https://taku910.github.io/mecab/)|最初に見ておくMeCabのページ|

# 環境

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

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

|種類|バージョン|
|:--|:--|
|matplotlib|3.1.3|
|pandas|1.0.1|

# [第4章: 形態素解析](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch4)
## 学習内容
> 夏目漱石の小説『吾輩は猫である』に形態素解析器MeCabを適用し，小説中の単語の統計を求めます．
>> 形態素解析, MeCab, 品詞, 出現頻度, Zipfの法則, [matplotlib](http://matplotlib.org/), [Gnuplot](http://www.gnuplot.info/)

## ノック内容
> 夏目漱石の小説『吾輩は猫である』の文章（[neko.txt](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt)）をMeCabを使って形態素解析し，その結果をneko.txt.mecabというファイルに保存せよ．このファイルを用いて，以下の問に対応するプログラムを実装せよ．

なお，問題37, 38, 39は[matplotlib](http://matplotlib.org/)もしくは[Gnuplot](http://www.gnuplot.info/)を用いるとよい．

### [39. Zipfの法則](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec39)
> 単語の出現頻度順位を横軸，その出現頻度を縦軸として，両対数グラフをプロットせよ．

# 回答
## 回答プログラム [039.Zipfの法則.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/039.Zipf%E3%81%AE%E6%B3%95%E5%89%87.ipynb)

```python
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`関数でリスト化しています。

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

### ログスケール表示
今回は問題文が「両対数グラフ」なので、ログスケールにしています。
> 単語の出現頻度順位を横軸，その出現頻度を縦軸として，両対数グラフをプロットせよ．

```python:
plt.xscale('log')
plt.yscale('log')
```

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/c129c4ad-a54c-bed3-ee4e-ca9b50adf61d.png)