言語処理１００本ノック２０２０やってみた part4


#はじめに
[言語処理100本ノック 2020 (Rev 1)](URL:https://nlp100.github.io/ja/)の[第4章: 形態素解析](URL:https://nlp100.github.io/ja/ch04.html)の自分なりの解答です。
どんどん雑になってきてる
* 環境
 * google colab

#目次
###[第4章: 形態素解析](URL:https://nlp100.github.io/ja/ch04.html)

[30. 形態素解析結果の読み込み](#30-形態素解析結果の読み込み)
[31. 動詞](#31-動詞)
[32. 動詞の原形](#32-動詞の原形)
[33. 「AのB」](#33-aのb)
[34. 名詞の連接](#34-名詞の連接)
[35. 単語の出現頻度](#35-単語の出現頻度)
[36. 頻度上位10語](#36-頻度上位10語)
[37. 「猫」と共起頻度の高い上位10語](#37-猫と共起頻度の高い上位10語)
[38. ヒストグラム](#38-ヒストグラム)
[39. Zipfの法則](#39-zipfの法則)

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

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

#30. 形態素解析結果の読み込み
>形態素解析結果（neko.txt.mecab）を読み込むプログラムを実装せよ．ただし，各形態素は表層形（surface），基本形（base），品詞（pos），品詞細分類1（pos1）をキーとするマッピング型に格納し，1文を形態素（マッピング型）のリストとして表現せよ．第4章の残りの問題では，ここで作ったプログラムを活用せよ．

```:イメージ
[
  [{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...],　１文
  [{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...],
  .
  .
  [{surface:hoge, base:hoge, pose:hoge ,pose1:hoge},{surface:hoge, base:hoge, p...]
]

```


```py
import MeCab

#改行ごとに区切る
def split_sentence(sentence):
  li = sentence.split('\n')
  return li

def split_words(words):
  li = []
  for i in words:
    li.append(i.split('\t'))
  return li

def get_element(morphoed_sentence):
  li =[]
  for i in morphoed_sentence:
    if i[0] == 'EOS':
      break  
    pos = i[4].split('-')
    #print(i)
    #print(pos)
    if len(pos) == 1:
      dic = {'surface':i[0], 'base':i[3], 'pos':pos[0] ,'pos1':pos[0]}
    elif len(pos) >= 2:
      dic = {'surface':i[0], 'base':i[3], 'pos':pos[0] ,'pos1':pos[1]}
    li.append(dic)
  return li

#１文づつ入れていく
def get_morpho(sentence):
  # 形態素解析
  sentence =  tagger.parse(sentence)
  # 各形態素ごとにリスト化　
  sentence = split_sentence(sentence)
  # 各形態素の要素のリスト化　['坊主', 'ボーズ', 'ボウズ', '坊主', '名詞-普通名詞-一般', '', '', '1']
  sentence = split_words(sentence)
  #指定要素の取得
  elements_list = get_element(sentence)
  return elements_list

#1行ごとにリスト化
def load_text(path):
  '''
  path   : file path
  return : 行ごとのリスト
  '''
  li = []
  with open(path,'r') as neko:
    for sentence in neko:
      sentence = sentence.strip('\r\n')
      #空行を避ける
      if sentence == '':
        continue
      li.append(sentence)
    return li
#neko.txt.mecabを読み込む
path = 'neko.txt.mecab'

def processor(path):
  li = []
  sentence = load_text(path)
  for i in sentence:
    li.append(get_morpho(i))
  return li


res = processor(path)
for i in range(5):
  print(res[i])
```

#31. 動詞
>動詞の表層形をすべて抽出せよ．


```py
for i in res:
  for j in i:
    if j["pos"] == "動詞":
      print(j["surface"])
```
```:出力（途中から)
行く
し
廻っ
する
ある
択ば
出
来
ある
```

#32. 動詞の原形
>動詞の原形をすべて抽出せよ．

```py
for i in res:
  for j in i:
    if j["pos"] == "動詞":
      print(j["base"])
```

```:途中から（３２と見比べて）
行く
為る
回る
為る
有る
選ぶ
出る
来る
有る
```
#33. 「AのB」
>2つの名詞が「の」で連結されている名詞句を抽出せよ．

#####コメント
「の」がきた時にその前の単語と後の単語が`名詞`だとそれを抽出する。
各文の単語の+1,-1としてて`out of range`とかになるかなと思いましたが文構造的にならないと思ったのでこのようにしました.

```py

for ind,i in enumerate(res):
  #print(i)
  for index,j in enumerate(i):
    #print(index)
    #print(j)
    if j['surface'] == 'の'  and res[ind][index-1]['pos'] == '名詞' and res[ind][index+1]['pos'] == '名詞':
      print('{}の{}'.format(res[ind][index-1]['surface'],res[ind][index+1]['surface']))
```

```:出力
掌の上
書生の顔
ものの見
はずの顔
顔の真中
穴の中
書生の掌
掌の裏
```

#34. 名詞の連接
>名詞の連接（連続して出現する名詞）を最長一致で抽出せよ．

#####コメント
一文ごとに名詞のインデックスをリスト`(noun_li)`に追加する、それ以外は−１を追加
連結の操作はコードみてください

最後に`noun_li`をリフレッシュしてなくてしばらく迷走してた。前も同じようなことした気がする


```py:コメントアウトの出力文も合わせて出力すると処理がわかりやすい

#noun_liをリフレッシュする
noun_li =[]
noun = ''
for ind,i in enumerate(res): #各行を送り出す   ind:行数
#  print(i)
  for index,j in enumerate(i): #各行の要素を調べる index:要素のある場所
#    print(j)
    if j['pos'] == '名詞':
      noun_li.append(index)
    else:
      noun_li.append(-1)
#  print('-------------------------------')
  noun_li.append("EOL")
#  print(noun_li)
  for k in noun_li:
    if k != 'EOL':
      if k > 0:
        noun += res[ind][k]['surface']
      else:
        if len(noun) > 0:
          print(noun)
          noun = ''
    else:
        if len(noun) > 0:
          print(noun)
          noun = ''
  noun_li.clear()
```

```:出力（途中から）
いたずら
両人
揃い
事
最初
うち
各自任意
行動
```

#35. 単語の出現頻度
>文章中に出現する単語とその出現頻度を求め，出現頻度の高い順に並べよ．

```py
def word_counter(data):
  dic = {}
  for i in data:
    for j in i:
      key = j['surface']
      dic[key] = dic.get(key,0) + 1
  return dic

li = []
word_counter_dic = word_counter(res)
for word, count in sorted(word_counter_dic.items(),key=lambda x:-x[1]):
  li.append('{} : {}'.format(word,count))

for i in range(10):
  print(li[i])
```

```:出力
の : 9541
。 : 7486
て : 7418
に : 7060
、 : 6773
は : 6501
と : 6156
を : 6119
が : 5394
で : 4543
```

#36. 頻度上位10語
>出現頻度が高い10語とその出現頻度をグラフ（例えば棒グラフなど）で表示せよ．

######コメント
日本語は文字化けするからいろいろ処理が必要、追い詰められたらやります

内包表記使えば自動で欲しい番目までリスト化できるの失念してた

```py
import matplotlib.pyplot as plt

label = words[0:10]
left = [1,2,3,4,5,6,7,8,9,10]
height = counts[0:10]
plt.bar(left, height, tick_label=label, align="center")
```

![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419864/5ce99c3c-0d6b-9e97-ed8e-1183ec13bdb1.png)


* [【Python】matplotlibを日本語化する](URL:https://qiita.com/maroKanatani/items/3b080c639395bba7795a)
* [Pythonでデータサイエンス matplotlib で棒グラフを描く](URL:https://pythondatascience.plavox.info/matplotlib/%E6%A3%92%E3%82%B0%E3%83%A9%E3%83%95)
* [pythonのmatplotlibの使い方をまとめてみた](URL:https://qiita.com/renesisu727/items/24fc4cd8fa2635b00a0d)

#37. 「猫」と共起頻度の高い上位10語
>「猫」とよく共起する（共起頻度が高い）10語とその出現頻度をグラフ（例えば棒グラフなど）で表示せよ．

#####コメント
共起する単語の定義は文の中に`猫`があるモノの単語とします

```py
import matplotlib.pyplot as plt
import japanize_matplotlib

#猫と共起する単語とその頻度の辞書を返す関数
def neko_co(data):
  neko_co_occurence ={}
  for sentence in data:
    if '猫' in [words['surface'] for words in sentence]:
      #print(sentence)
      for word in [words['surface'] for words in sentence]: # word は　key 
        #print(word)
        neko_co_occurence[word] = neko_co_occurence.get(word,0) + 1
  return neko_co_occurence

neko_co_dic = neko_co(res)
print(neko_co_dic)
del neko_co_dic['猫']

neko_co_word,neko_co_count = [],[]
for word, count in sorted(neko_co_dic.items(),key=lambda x:-x[1]):
  neko_co_word.append(word)
  neko_co_count.append(count)

label = neko_co_word[0:10]
left = [i+1 for i in range(10)]
height = neko_co_count[0:10]
plt.bar(left, height, tick_label=label, align="center")
print(neko_co_word)
print(neko_co_count)
```

![ダウンロード (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419864/0176d56b-b522-4823-f387-27b25f7bc9a0.png)


```
{'吾輩': 55, 'は': 262, '猫': 237, 'で': 181, 'ある': 96, '。': 200,

['の', 'に', 'は', 'て', '、', 'と', 'を', '。', 'で', 'が', 'も', 'た', 'ある', 'し', 'ない', 'だ', 'な', 'から', '事', '吾輩',
[377, 264, 262, 240, 240, 221, 208, 200, 181, 169, 133, 117, 96, 96, 88, 66, 62, 61, 58, 55, 
```

#38. ヒストグラム
>単語の出現頻度のヒストグラムを描け．ただし，横軸は出現頻度を表し，1から単語の出現頻度の最大値までの線形目盛とする．縦軸はx軸で示される出現頻度となった単語の異なり数（種類数）である．

#####コメント
あってるのかわからない
１、２とかの数が多すぎて他の頻度が潰れてる. 
横軸の１棒の範囲が１０００になってるからだと思う

```py
import matplotlib.pyplot as plt
#1-9541
x = re_counts 
plt.hist(x)
plt.show()
```

![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419864/721acdf7-cfa7-b52f-f54d-671f633d98bb.png)



#39. Zipfの法則
>単語の出現頻度順位を横軸，その出現頻度を縦軸として，両対数グラフをプロットせよ．

```py
#rank r
#freaquence f
# words words
r = [i+1 for i in range(len(counts))]
f = counts

import matplotlib.pyplot as plt
import japanize_matplotlib
fig, ax = plt.subplots()
ax.plot(r, f,'.')
plt.scatter(r,f,s=0.5)
ax.set_yscale('log')  # y軸をlogスケールで描く
ax.set_xscale('log')  # x軸をlogスケールで描く
plt.show()

```
![ダウンロード (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419864/4351060b-d287-18cc-682b-489ef7cfffc2.png)


* [ジップの法則](URL:http://www.comm.tcu.ac.jp/~math/hnakai/infomath/sherlockholmes/zip.html)
* [[Pythonによる科学・技術計算] 対数グラフ，可視化，matplotlib](URL:https://qiita.com/sci_Haru/items/68bd9a05d99598d445b0)


#感想
ゴリ押しでなんとかなったような気もするけど自分の回答が効率的なのかどうかよくわからないので他の人のもみてみようと思う