言語処理100本ノック-35(pandas使用):名詞の連接

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)[「第4章: 形態素解析」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch4)の[35本目「名詞の連接」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec35)記録です。
前回と同様、pandasで簡単に処理できないない内容です。ですが、「名詞の連接」部分は10行程度の難しくはない処理です。

# 参考リンク

|リンク|備考|
|:--|:--|
|[035.名詞の連接.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/035.%E5%90%8D%E8%A9%9E%E3%81%AE%E9%80%A3%E6%8E%A5.ipynb)|回答プログラムのGitHubリンク|
|[素人の言語処理100本ノック:35](http://qiita.com/segavvy/items/bda3a16d8bb54bd01f73)|多くのソース部分のコピペ元|
|[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でインストールするだけです。

|種類|バージョン|
|:--|:--|
|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/)を用いるとよい．

### [35. 名詞の連接](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec35)
> 名詞の連接（連続して出現する名詞）を最長一致で抽出せよ．

### 課題補足(「」について)

# 回答
## 回答プログラム [035.名詞の連接.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/035.%E5%90%8D%E8%A9%9E%E3%81%AE%E9%80%A3%E6%8E%A5.ipynb)

```python
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 = read_text()

nouns = []
for index in df['surface'].index:
    nouns.append(df['surface'][index])
    
    # ひとつ先のインデックスがない場合は名詞連接の終端
    if (index + 1) not in df.index:
        
        # 複数(連接)の場合
        if len(nouns) > 1:
            print(len(nouns), '\t', index, '\t', ''.join(nouns))
        nouns = []
    
    # 多いので制限
    if index > 2000:
        break
```

## 回答解説
### ファイル読込
今回は名詞以外は不要なのでファイル読込直後に名詞だけを対象にエントリを絞っています。

```python:
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'] == '名詞']
```

### 名詞連接抽出
あとはループでの処理です。次エントリのインデックスが+1かどうかで連接終端判断をしています。連接終端で連接リストの長さが1以上の場合のみ名詞連接を出力します。

```python:
nouns = []
for index in df['surface'].index:
    nouns.append(df['surface'][index])
    
    # ひとつ先のインデックスがない場合は名詞連接の終端
    if (index + 1) not in df.index:
        
        # 複数(連接)の場合
        if len(nouns) > 1:
            print(len(nouns), '\t', index, '\t', ''.join(nouns))
        nouns = []
```

### 出力結果(実行結果)
プログラム実行すると以下の結果が出力されます。

```bash:出力結果
2 	 28 	 した所
2 	 66 	 人間中
2 	 69 	 一番獰悪
2 	 172 	 時妙
2 	 190 	 一毛
2 	 209 	 その後猫
2 	 222 	 一度
2 	 688 	 邸内
2 	 860 	 書生以外
3 	 1001 	 四五遍
2 	 1028 	 この間おさん
2 	 1031 	 三馬
2 	 1106 	 御台所
2 	 1150 	 まま奥
2 	 1235 	 終日書斎
2 	 1255 	 勉強家
2 	 1266 	 勉強家
2 	 1288 	 勤勉家
3 	 1392 	 二三ページ
2 	 1515 	 主人以外
2 	 1581 	 限り吾輩
2 	 1599 	 朝主人
2 	 1690 	 一番心持
2 	 1733 	 一つ床
2 	 1781 	 最後大変
3 	 1829 	 神経胃弱性
2 	 1913 	 言語同断
2 	 1961 	 しようもの
3 	 1965 	 家内総がかり
```