前のページまでの作業で日本語評価極性辞書(名詞編)を用いて感情分析を行うための準備が整いました.ここでは「今日の勝利で1位に躍進して優勝が濃厚になったことが競争相手にとっては痛手となった」という文章について感情分析を行なってみます.なお,すでに説明した通り「1位」の「1」には全角文字を利用していることに注意してください.
関数 get_sentiment()
を作成して単語(形態素)ごとに感情を取得します.その結果,「勝利」「1位」「躍進」「優勝」がポジティブ,「競争相手」がニュートラル,「痛手」がネガティブであることがわかりました.また「濃厚」は「?p?n」であることからポジティブかネガティブかは曖昧です.
import MeCab as mc
import pandas as pd
import numpy as np
import os
import unicodedata
def get_sentiment(dic_term, text):
"""
形態素解析した後に,単語ごとに感情分析を行い,辞書のリスト形式で返す
"""
# t = mc.Tagger() # for Windows
# t = mc.Tagger('-r /dev/null -d /opt/homebrew/lib/mecab/dic/ipadic') # for macOS
t = mc.Tagger('-r /dev/null -d /opt/homebrew/lib/mecab/dic/mecab-ipadic-neologd') # for macOS
text = unicodedata.normalize('NFKC', text) # 形態素解析を実行する前に半角文字へ正規化する
node = t.parseToNode(text)
words = []
while(node):
if node.surface != "": # ヘッダとフッタを除外
pos = node.feature.split(",")[0] # pos (part_of_speech) 品詞
lemma = node.feature.split(",")[6] # lemma 原型
sentiment = dic_term.get(lemma, np.nan) ## 原型から感情を参照していることに注意
w = {
'surface': node.surface, # 表層形
'lemma': lemma,
'pos': pos,
'sentiment': sentiment
}
words.append(w)
node = node.next
if node is None:
break
return words
# 日本語評価極性辞書(名詞編)
path_dic = os.path.sep.join(['dic', 'pn.csv.m3.120408.trim'])
df_pncsv = pd.read_csv(path_dic, sep="\t", names=["term", "sentiment", "semantic_category"])
df_pncsv['term'] = [unicodedata.normalize('NFKC', t) for t in df_pncsv['term'].tolist()] # 内包表記を使って半角文字に正規化する
# 辞書形式データの作成
dic_term = df_pncsv.set_index('term')['sentiment'].to_dict()
# dic_term = df_pncsv[(df_pncsv.sentiment=="p") |
# (df_pncsv.sentiment=="e") |
# (df_pncsv.sentiment=="n")
# ].set_index('term')['sentiment'].to_dict()
sent = "今日の勝利で1位に躍進して優勝が濃厚になったことが競争相手にとっては痛手となった" # 1位の「1」は全角文字であることに注意
results = get_sentiment(dic_term, sent)
df = pd.DataFrame(results)
df
分析結果を集計し,得られた感情項目の個数をカウントします.
df.groupby('sentiment')['surface'].count().reset_index()
上の結果を割合で表示すると,ポジティブな用語 (p) が57%含まれる一方で,ニュートラル (e) やネガティブ (n) は14%であることがわかりました.
# グループ化してカウント
grouped = df.groupby('sentiment')['surface'].count()
# 全体の合計で割って割合を計算
total_count = grouped.sum() # 欠損値を除いたデータ数
percentage = grouped / total_count * 100
result1 = percentage.reset_index()
result1.columns = ['sentiment', 'percentage']
result1
もしも感情が得られたなかった単語(形態素)の個数もカウントしたいという場合には,次のようにするとよいでしょう.具体的には欠損値を 'nan'
という文字列に置き換えることで,欠損値のデータもカウントされるようにしています.
df['sentiment_nan'] = df['sentiment'].fillna('nan')
df.groupby('sentiment_nan')['surface'].count().reset_index()
欠損値も含めて割合を求めてみましょう.すると全体の71%の形態素からは感情が得られていないこともわかります.
# グループ化してカウント
grouped_nan = df.groupby('sentiment_nan')['surface'].count()
# 全体の合計で割って割合を計算
total_count_nan = grouped_nan.sum() ## 欠損値も含めた全データ数
percentage_nan = grouped_nan / total_count_nan * 100
result_nan = percentage_nan.reset_index()
result_nan.columns = ['sentiment_nan', 'percentage']
result_nan
また,ここではmecab-ipadic-NEologd 辞書を用いて形態素解析を実行しました.IPA 辞書を利用した場合には,MeCabの形態素解析にって「1位」や「競争相手」が「1」と「位」や「競争」と「相手」に分割されることから,感情分析の結果も異なる結果になることに注意してください.