3. Pythonによる自然言語処理　5-5. 日本語文の感情値分析［日本語評価極性辞書（用言編）］

* 日本語の感情値辞書に関する試行の第 3 弾となります。
* 前回の[「日本語評価極性辞書（名詞編）」](https://qiita.com/y_itoh/items/4693bd8f64ac811f8524)につづいて、本稿では**「日本語評価極性辞書（用言編）」**を利用させて頂くのですが、**用言とは活用する語、すなわち動詞・形容詞・形容動詞の類**です。
* つまり、文章単位の感情分析という用途には難しいものがありますが、処理の手順は前回を踏襲しつつ、当該辞書に関する考察を深めることに努めます。


#⑴ 「日本語評価極性辞書（用言編）」の取得と前処理
####１. 辞書データを Colab 上に読み込む
```python
import pandas as pd

# 全角文字を考慮してカラム名と値の位置ずれを制御
pd.set_option("display.unicode.east_asian_width", True)

# 感情値辞書の読み込み
pndic_1 = pd.read_csv(r"http://www.cl.ecei.tohoku.ac.jp/resources/sent_lex/wago.121808.pn",
                      names=["judge_type_word"])

print(pndic_1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/768cb703-256d-2e2c-6c1b-880c173a5a34.png)

* 収録語数は全 5,280 語ですが、活用のバリエーションで複数行にわたっていることが見て取れます。
* 分析対象のテキストは形態素解析により単語の原形のリストとなりますので、上の例でいえば「颯爽 : ポジ」の 1 組となるように、あらかじめ辞書に対して前処理をする必要があります。

####２. データフレームを区切り文字で展開
* まず `\t` を区切り文字として、ポジ/ネガを含む感情値の部分と、活用を含む語の部分に分割して 2 列に展開します。

```python
pndic_2 = pndic_1["judge_type_word"].str.split('\t', expand=True)
print(pndic_2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/42fc26a6-08a7-920f-2669-3df0370a9093.png)

####３. 感情値ポジ/ネガの不要部分を削除
* ポジ/ネガを含む感情値の列にデータがどのように入っているかを確認します。

```python
judge = pd.Series(pndic_2[0])
judge.value_counts()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/3d566c72-75c3-587d-36c4-9a56d88882e9.png)

* 当該辞書は、感情値判定として「ポジ/ネガ」に「評価/経験」の別を組み合わせた 4 パターンが採用されています。
* 文章の感情分析に必要なのは「ポジ/ネガ」のみですので、「評価/経験」の別を示す（ ）囲みの部分を削除します。

```python
pndic_2[0] = pndic_2[0].str.replace(r"\（.*\）", "", regex=True)
print(pndic_2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/95ebae8f-16c0-f750-a5a4-866948e9a449.png)

####４. 用言部分の登録内容を確認
* 2 列目の用言部分に活用などの情報が盛り込まれているわけですが、感情値を取得するための参照元とするには、それらを語の原形のかたちに収斂する必要があります。
* それに先立ってデータの状況を把握するために、半角スペースを区切り文字として複数列に展開します。

```python
df_temp = pndic_2[1].str.split(" ", expand=True)
print(df_temp)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/b4fafa4f-9827-8a74-8877-7628a2f2f118.png)

* 当該のカラム内には最多で 9 つまでの語が入っていることが分かります。次いで、展開された各カラム毎に None でないレコード数を確認します。

```python
df_temp.info()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/2768af67-122b-1a1f-4b6e-ffd854a3d4da.png)

* 全 5,280 語中 75% に当たる 3,972 語が、語幹につづく活用形などを少なくとも 1 語以上もっています。
* 試みに、活用部分を 7 語以上もつ 3 レコードの内容を見てみましょう。まずカラム [7] に入っている 3 語を取得します。

```python
v = pd.Series(df_temp[7])
v.value_counts()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/0fd6832d-3748-1bb9-d9d6-b71c4cac376e.png)

* 次いで、これらの登録内容をすべて出力します。

```python
print(df_temp[df_temp[7] == "痛い"])
print(df_temp[df_temp[7] == "ぬ"])
print(df_temp[df_temp[7] == "だ"])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/26f51010-7a6e-1137-e3e1-b8b9790c49b5.png)

* ほんの一部を例示したに過ぎませんが、こうした成句が 1 レコードとしてポジ/ネガの感情値を付与されているという実態があります。
* 例えば「目/の/中/へ/いれる/て/も/痛い/ない」の各語に対して一様に感情値として [ポジ] を付与するわけにはいきませんし、かといって語幹に当たる「目」を以てこの成句の意味を代表させることもできません。
* あるいは、成句の中には部分的に ポジ/ネガ を併せ持つものもあるかも知れません。
* そこで、苦肉の策ではありますが、➀活用部分を 3 つ以上もつものは除外し、形態素解析結果にもとづく参照先としての必要上 ➁語幹のみを用いることとします。

####５. 辞書データのスクリーニング
* 用言部分のデータフレーム `temp_df` に、感情値 ポジ/ネガ の列を結合します。

```python
pndic_3 = pd.concat([df_temp, pndic_2[0]], axis=1)
print(pndic_3)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/2d956031-0bda-7d40-fb62-c839f10c3b2f.png)

* 活用部分を 3 つ以上もつものは除外しますので、カラム [3] がNoneのレコードを残してそれ以外を削除した結果、レコード数は 4,875 行となりました。

```python
pndic_4 = pndic_3[pndic_3[3].isnull()]
print(pndic_4)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/1b1f31f2-1826-9a25-706b-3ba0cb633cb1.png)

* 「語幹」と「ポジ/ネガ」の 2 カラムを取得して、つまりそれ以外のカラムを削除します。

```python
pndic_5 = pndic_4[0]
print(pndic_5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/5790a64e-810e-a05d-af91-594763bc46f6.png)

* 活用のバリエーションで語幹を共通するものは重複レコードとなっていますので、重複のない一意なデータに変換します。

```python
pndic_6 = pndic_5.drop_duplicates(keep='first')
pndic_6.columns = ["word", "judge"]
print(pndic_6)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/a2d1ef03-a972-c897-85a7-59bd6c241502.png)


####６. 辞書データを dict 型に変換
* 感情値「ポジ/ネガ」を数値 [ポジ : +1, ネガ : -1] に置換します。

```python
pndic_6["judge"] = pndic_6["judge"].replace({"ポジ":1, "ネガ":-1})
print(pndic_6)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/b4f08a40-624e-9c4d-7629-984428173c36.png)

* `dict()` で列 `"word"` をキー、列 `"judge"` を値とする辞書型に変換し、感情値を取得する参照元とします。

```python
import numpy as np

keys = pndic_6["word"].tolist()
values = pndic_6["judge"].tolist()
dic = dict(zip(keys, values))

print(dic)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/d6a0eeef-d905-e182-8086-1a095ad8d037.png)



#⑵ 分析対象となるテキストの前処理

* 分析対象となるテキストをはじめとして、以降の処理は前回・前々回の[「単語感情極性値対応表」](https://qiita.com/y_itoh/items/8e63334058659fe2b4d3)及び[「日本語評価極性辞書（名詞編）」](https://qiita.com/y_itoh/items/4693bd8f64ac811f8524)を踏襲します。

####１. テキストを指定
```python
text = 'スパゲッティの全国の輸入量が、ことし10月までに過去最多となり、税関は新型コロナウイルスの感染拡大の影響で増えた、いわゆる『巣ごもり需要』が背景にあるでのはないかとしています。横浜税関によりますと、全国の港や空港から輸入されたスパゲッティの量はことし10月末の時点でおよそ14万2000トンでした。これは、3年前の1年間の輸入量を4000トンほど上回り、過去最多となりました。また、マカロニもことし10月までの輸入量が1万1000トン余りに上り、過去最多だった4年前の1年間の輸入量とほぼ並んでいるということです。'

lines = text.split("。")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/dccdf656-7ce9-2eeb-b73b-f60c462db348.png)

####２. 形態素解析のインスタンスを生成

* MeCab をインストールします。

```python
!apt install aptitude
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
!pip install mecab-python3==0.7
```

* MeCab をインポートし、出力モードを `'-Ochasen'` としてインスタンスを生成します。
* 一連の処理には関係しませんが、例として 1 行目の形態素解析結果を示します。

```python
import MeCab
mecab = MeCab.Tagger('-Ochasen')

# 1行目の形態素解析結果を例示
print(mecab.parse(lines[0]))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/effab13c-174a-c637-ad32-cec39761f7bb.png)

####３. 形態素解析に基づいて文章単位にリスト化
```python
# 形態素解析に基づいて単語を抽出
word_list = []
for l in lines:
    temp = []
    for v in mecab.parse(l).splitlines():
        if len(v.split()) >= 3:
            if v.split()[3][:2] in ['名詞','形容詞','動詞','副詞']:
                temp.append(v.split()[2])
    word_list.append(temp)

# 空の要素を削除
word_list = [x for x in word_list if x != []]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/a02d3b23-c1ca-c9b1-1a13-1c2d4a2ed6b7.png)



#⑶ 感情極性値による文章のポジネガ判定

* 文章ごとに単語とその**感情極性値**を取得してデータフレームに出力してみます。

```python
result = []
# 文単位の処理
for sentence in word_list:
    temp = []
    # 語単位の処理
    for word in sentence:
        word_score = []
        score = dic.get(word)
        word_score = (word, score)
        temp.append(word_score)       
    result.append(temp)

# 文毎にデータフレーム化して表示
for i in range(len(result)):
    df_ = pd.DataFrame(result[i], columns=["word", "score"])
    print(lines[i], '\n', df_.where(df_.notnull(), None), '\n')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/cbe844ee-8bff-566b-4930-9e459f693991.png)

* 全 4 行の結果を左から順に下表に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/61c2df2d-691c-da77-d457-f4749d989ed1.png)

* ほぼ全般に None となっており、用言に限定した辞書ということで多くの語が未登録となります。
* 文章ごとに平均値を求めるまでもないので、以降の処理は当該辞書のポジ/ネガ構成比を確認するにとどめ、小結として感情値辞書に望まれる要件について検討します。

#⑷ 「日本語評価極性辞書（用言編）」のポジ/ネガ構成比

```python
# ポジティブの語数
keys_pos = [k for k, v in dic.items() if float(v) == 1]
cnt_pos = len(keys_pos)
# ネガティブの語数
keys_neg = [k for k, v in dic.items() if float(v) == -1]
cnt_neg = len(keys_neg)

print("ポジティブ の割合:", ('{:.3f}'.format(cnt_pos / len(dic))), "(", cnt_pos, "語)")
print("ネガティブ の割合:", ('{:.3f}'.format(cnt_neg / len(dic))), "(", cnt_neg, "語)")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/52dd7e62-2cd7-bff7-0cc4-3d915bfde4b4.png)

#⑸ 感情値辞書 及び感情分析ツールの要件【考察】
* ここまで、英語・日本語を対象とした感情値辞書 及び感情値分析ツールを見てきました。それぞれの基本的な仕様や特徴などを下表にまとめます。

|対応言語|名称|種別|収録語数|収録内容|感情極性値|特記事項|
|---|---|---|---|---|---|---|
|英語|[AFFIN-111](https://www.npmjs.com/package/afinn-111)|感情値辞書|2,477|名詞・形容詞・動詞・副詞・感嘆詞など|INTEGER [-4, +4]|ポジ : ネガ = 3.5 : 6.5、ニュートラル 1 語のみ|
|英語|[VADER](https://www.codeproject.com/Articles/5269445/Using-Pre-trained-VADER-Models-for-NLTK-Sentiment)|感情値分析ツール|7,520|名詞・形容詞・動詞・副詞・感嘆詞など、スラング、顔文字|FLOAT [-1, +1]|否定による極性の反転、極性値の増幅処理|
|日本語|[単語感情極性値対応表](http://www.lr.pi.titech.ac.jp/~takamura/pndic_ja.html)|感情値辞書|55,125|名詞・動詞・副詞・形容詞・助動詞|FLOAT [-1, +1]|ポジ : ネガ = 1 : 9 でネガティブに著しい偏り|
|日本語|[日本語評価極性辞書（名詞編）](https://www.cl.ecei.tohoku.ac.jp/index.php?Open%20Resources%2FJapanese%20Sentiment%20Polarity%20Dictionary)|感情値辞書|13,314|名詞|STRING [p, n, e]|ポジ/ネガ比率ほぼ均衡、ごく少数の重複を含む|
|日本語|[日本語評価極性辞書（用言編）](https://www.cl.ecei.tohoku.ac.jp/index.php?Open%20Resources%2FJapanese%20Sentiment%20Polarity%20Dictionary)|感情値辞書|5,280|用言|STRING [ポジ, ネガ]|全体の3/4程度が「語幹+活用語」の形式をもつ|

* 感情分析に用いる辞書の仕様として、次の 3 点が挙げられます。
    * **一意姓** : <font color="DarkOrange">見出し語は、重複のない一意(ユニーク)</font>の構成となっていること。
    * **単語の原形** : 日本語文は形態素解析により取得される単語の原形(終止形)を分析対象とするため、<font color="DarkOrange">見出し語の語形は原形(終止形)</font>に統一されていること。
    * **ポジ/ネガ構成比** : 辞書中のポジ/ネガ比率に著しい偏りがある場合、その傾向が分析結果に反映されてしまう可能性があるため、収録語彙数の<font color="DarkOrange">ポジ/ネガの割合が均衡</font>していること。
* 感情分析は本来、その文章が何をどう語っているか、つまり意味や文脈を理解した上で判定がなされるべきものです。従って、ポジネガを規定した辞書を補完しつつ文脈の中での意味をとらえるには以下 2 つの処理が必要になります。
    * **極性の反転** : 否定詞や接続詞によって<font color="DarkOrange">極性を反転させる処理</font>。
    * **極性値の増幅** : ポジネガの程度を表す「副詞」、ポジネガの強度を表す「感嘆符」やその数などによって<font color="DarkOrange">極性値を増幅させる処理</font>。
* なお、**顔文字**もまた感情やその強度を表すものです。ちなみに VADER の辞書には、例えば、笑顔「:) 2.0」「:> 2.1」、笑い「:-D 2.3」「XD 2.7」、しかめっ面「:( -1.9」「:[ -2.0」、怒り「:@ -2.5」「>:( -2.7」、驚き「>:O -1.2」「:O -0.4」、泣き「:'( -2.2」、うれし泣き「:') 2.3」などのように 250 語以上の顔文字が収録されています。