言語処理100本ノック-34(pandas使用):「AのB」

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)[「第4章: 形態素解析」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch4)の[34本目「「AのB」」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec34)記録です。
今回は行情報の組み合わせに対する課題なので、今までのように簡単にはできません。pandasやSQLなどの苦手な部分ですね。ですが、単なるループ処理なので難しくはないです。

# 参考リンク

|リンク|備考|
|:--|:--|
|[034.「AのB」.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/034.%E3%80%8CA%E3%81%AEB%E3%80%8D.ipynb)|回答プログラムのGitHubリンク|
|[素人の言語処理100本ノック:34](https://qiita.com/segavvy/items/92f1177b6f095eb686a8)|多くのソース部分のコピペ元|
|[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/)を用いるとよい．

### [34. 「AのB」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec34)
> 2つの名詞が「の」で連結されている名詞句を抽出せよ．

# 回答
## 回答プログラム [034.「AのB」.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/04.%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/034.%E3%80%8CA%E3%81%AEB%E3%80%8D.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')
    # EOS, 記号, 空白を残す
    return df

df = read_text()

POS_TARGET = '名詞'

for index in df['surface'].index:
    
    # 1行目・最終行に特別なロジックなし
    if df['surface'][index] == 'の' \
     and df['pos'][index-1] == POS_TARGET \
     and df['pos'][index+1] == POS_TARGET:
        print(index, '\t', df['surface'][index-1] + 'の' + df['surface'][index+1])
    
    # 多いので制限
    if index > 2000:
        break
```

## 回答解説
### ファイル読込
前回までのノックとは異なり、EOS・記号・空白行の除去はしていません。文末や記号で明確を含めて「AのB」が連続であることを条件にしたかったためです。

```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')
    # EOS, 記号, 空白を残す
    return df
```

### 「AのB」判断
PandasのSeriesのindexでループします。そして、前後の行が名詞かを判断してます。

```python:
for index in df['surface'].index:
    
    # 1行目・最終行に特別なロジックなし
    if df['surface'][index] == 'の' \
     and df['pos'][index-1] == POS_TARGET \
     and df['pos'][index+1] == POS_TARGET:
        print(index, '\t', df['surface'][index-1] + 'の' + df['surface'][index+1])
```

### 出力結果(実行結果)
プログラム実行すると以下の結果が出力されます。多いので2000行までしか見ていないです。

```bash:出力結果
118 	 彼の掌
144 	 掌の上
151 	 書生の顔
197 	 はずの顔
235 	 顔の真中
248 	 穴の中
292 	 書生の掌
294 	 掌の裏
382 	 何の事
421 	 肝心の母親
478 	 藁の上
484 	 笹原の中
498 	 ようやくの思い
516 	 池の前
658 	 ようやくの事
729 	 一樹の蔭
742 	 垣根の穴
752 	 隣家の三毛
758 	 時の通路
806 	 一刻の猶予
842 	 家の内
858 	 彼の書生
861 	 以外の人間
892 	 前の書生
958 	 おさんの隙
1029 	 おさんの三
1046 	 胸の痞
1068 	 家の主人
1089 	 主人の方
1121 	 鼻の下
1130 	 吾輩の顔
1192 	 自分の住家
1208 	 吾輩の主人
1249 	 家のもの
1281 	 うちのもの
1300 	 彼の書斎
1326 	 本の上
1341 	 皮膚の色
1402 	 本の上
1411 	 彼の毎夜
1516 	 以外のもの
1588 	 主人の傍
1608 	 彼の膝
1610 	 膝の上
1659 	 経験の上
1665 	 飯櫃の上
1671 	 炬燵の上
1700 	 ここのうち
1702 	 うちの小供
1704 	 小供の寝床
1747 	 彼等の中間
1773 	 小供の一人
1826 	 例の神経
1830 	 性の主人
1839 	 次の部屋
1919 	 自分の勝手
1953 	 吾輩の方
2000 	 台所の板の間
```