俺もデータサイエンティストやってみます

データサイエンティストがなにかわかってもないです。

アーティストみたいなもんじゃないんですか?知らんけど

経緯

Pythonでネットニュース記事の文章を大量抽出する - 自由研究をします。

Pythonで取得したデータを整形する - 自由研究をします。

一昨日、昨日、と「デマになりえそうな文章」を把握するために「人の目を引きやすい文章とはどんなものか」を知りたいと考え、大衆紙のネット記事を抽出していました。 今回はそれらを元にGoogleの感情分析API「Natural Language API」を使用して分析していきます。

cloud.google.com

今回はなんかソレっぽいことやってるんでこれで僕もこれからはデータサイエンティスト名乗れたりせんかなって思ってます。データ分析とかまったく知らんしグラフの見方の知識は小学校の算数でやったことしか知らんので雰囲気でやっていくことしかできませんけど。

書いた

import requests 
import os
from dotenv import load_dotenv

load_dotenv()

GOOGLE_LANG_API_KEY  = os.environ['GOOGLE_LANG_API_KEY']

#APIキーを入力
key = GOOGLE_LANG_API_KEY

#感情分析したいテキスト
text = "降板したショーンK氏の後任として、夜のニュース番組「ユアタイム」(フジテレビ系)にレギュラー出演することになったモーリー・ロバートソン氏。 今回、モーリー氏にショーンK氏についての取材を申し込むと、真意を説明したいと自ら執筆した原稿が編集部に届いた。いち早くショーンK氏、そして日本メディアの“デタラメ”に気づいていたモーリー氏が、緊急出演の舞台裏や問題の本質を、包み隠さずに明かす――。◆ ◆ ◆ さる3月、文春砲が撃たれ、第二弾に被弾した「ショーンK」を名乗り続けていた人物は屈服、J-WAVEで嗚咽しながらのお詫びをする。そのお詫び放送を銀座の端にあるレストランで聴いた。最後に流れた番組のお知らせは、J-WAVEが「うちは特に悪くありませんから」と幕引きする内容だった。ここにおいて「ショーンK」は使い捨てられた。 同じ頃、ぼくは『ユアタイム』出演日数が劇的に増えた。それは想定内だった。だが、続けて予期しなかった事態が連続する。東スポ、日刊ゲンダイ、週刊ポスト、アサヒ芸能などにゴシップ記事が書かれたのだ。None など、お約束のいじりである。「ショーンK」の経歴詐称が発覚し、フジテレビは動揺しきっていた。これ以上炎上したくない、という無理もないロジックで、今度は「後釜」であるぼくが詮索された。ぼくの学歴は輝かしいが、詐称はない。1981年に東京大学とハーバード大学に同時合格し、東大に1学期だけ通った後で休学(その後退学)し、ハーバードに入学、卒業したことは周知の事実だ。実は『よくひとりぼっちだった』というタイトルで合格までの経緯を自叙伝に著し、他ならぬ文藝春秋から1984年に発表している。当時、5万部を超えるノンフィクション部門のベストセラーにもなった。 だが「ショーンK」で動揺したフジテレビ側は不安を拭えず、「東大とハーバード以外に合格したすべての大学の合格通知のコピーを提出してください」 と過剰な身体検査を要求。その後も東スポに引用されたぼくの過去のツィートが追求の対象になった。 そのツィートはこういう内容だ。「コカインはやったことがあります。最初で最後にやったのはアメリカで1985年頃でした。鼻からです。オバマより回数は少ないはず」 これは2015年に、日本の薬物報道の不毛さを揶揄するべく書き込んだものだったが、スキャンダルを洗い出そうとする東スポのたゆまぬ努力で検索に引っかかった。フジテレビ側は、「コカインをい<e3><81><a4>やったのか。何度やったのか。当時のアメリカの法律でこれはどれぐらいの量刑となる罪だったのか」 というディテールを提出せよ、と再三連絡してきた。1985年、つまり31年前のマサチューセッツ州のコカインに関する州法をオフィス・モーリーのチームで調べて回答文を送信した。微罪であり、アメリカ在住時の行為で、一度きりであり、しかも多分時効だ。だがそれでもフジテレビ側の疑心暗鬼は静まらず、「ほかに何かありませんか」 という問い合わせが相次いだ。 過剰な身体検査はとどまるところを知らず、最終的には「ニコ生」での配信を自粛もしくは停止してほしいという、ぼくからすると荒唐無稽な要求まで飛び出した。せっかく手にした地上波のチャンスをふいにするのは残念だったが、「ニコ生」に有料登録までしてくれた2000人以上のユーザーを裏切るつもりはない。潔く「それでは番組を降板させていただきます」 と交渉する一歩手前で、案外すんなりと「ニコ生」への自粛要請は立ち枯れた。勘ぐった言い方をするなら、「ショーンK」に続いてぼくまでが降板したら番組が立ちいかなくなる。強気の交渉をすることで、フジテレビとの関係は正常化した。"

#APIのURL
url = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key=' + key

#基本情報の設定 JAをENにすれば英語のテキストを解析可能
header = {'Content-Type': 'application/json'}
body = {
    "document": {
        "type": "PLAIN_TEXT",
        "language": "JA",
        "content": text
    },
    "encodingType": "UTF8"
}

#json形式で結果を受け取る。
response = requests.post(url, headers=header, json=body).json()

#分析の結果をコンソール画面で見やすく表示
print(response["documentSentiment"])

結果

{'magnitude': 6.8, 'score': -0.1}

例によって文春記事のひとつを適当に抜粋して感情分析にかけました。

実装の参考→PythonでGoogle Natural Language APIを叩いて感情分析 | AVILEN AI Trend

感情分析をかけた記事→ショーンKの後任、モーリー・ロバートソンが書いた「ショーンK問題の真相」 | 文春オンライン

結果の見方はGoogleのドキュメントを参考にしました。

cloud.google.com

「感情分析のレスポンスフィールド」の項以下を参照

score: -1.0(ネガティブ)~1.0(ポジティブ)のスコアで感情が表されます。これは、テキストの全体的な感情の傾向に相当します。
magnitude: 指定したテキストの全体的な感情の強度(ポジティブとネガティブの両方)が 0.0~+inf の値で示されます。score と違って、magnitude は正規化されていないため、テキスト内で感情(ポジティブとネガティブの両方)が表現されるたびにテキストの magnitude の値が増加します。そのため、長いテキスト ブロックで値が高くなる傾向があります。

要はscoreが文章全体の感情の種類を(ネガティブかポジティブか。この場合では怒りとか悲しみとかはネガティブに分類され、喜びとか嬉しさがポジティブに分類されるみたい)表わしていて、magnitudeが文章の総合的な感情の大きさを表わしてるっぽい。

ここでのポイントが、magnitudeはあくまでも総合的な値であって、文章が長くなればなるほど必然と文中の感情ポイントは加算されていくので結果的な値はふくれあがっていくのが普通っぽい。

そしてscoreは要は文章内の平均的な感情のスコアであって、

多分オタクの長文映画感想とかは「ありえんよさみ!w 無限に泣きをしまつ。○○が尊すぎて鉄板ネタに大声でゲラゲラ笑ってる(真顔) そして一瞬で鬱になった」みたいな感情の高低が激しい文だと実際の内容はかなり感情濃いめなのにscoreの値は割と落ちついた結果になってる...みたいなのになるのかもしれない

ちなみに感情分析にさっきの即席オタク構文をぶっこんだら

{'magnitude': 0.6, 'score': 0}

って出た。やっぱりscoreは0だしさっき推測した判定っぽくなってる。まあでもこれはmagnitudeの値との差異を見れば理解できそう。

magnitudeがめちゃくちゃ高いのにscoreが0に近かったりしたら「あ~、もしかして感情の起伏が激しい文章なのかな」とか推測できる。

んで

https://raw.githubusercontent.com/iinoten/tweet-test-bot/master/replaced_netnewsdata.json

整形して400行くらいになった文春の記事を全部分析にかけた。一応感情分析APIで、文章をそれぞれ文節に分けていっこいっこスコアを出してくれる機能もあるんだけど、それをすると結果を表示させるのに処理がめんどくさそうなので文書全体を一括で処理してやってみる。

import requests 
import os
import json
from dotenv import load_dotenv

load_dotenv()
GOOGLE_LANG_API_KEY  = os.environ['GOOGLE_LANG_API_KEY']

json_open = open('./replaced_netnewsdata.json', 'r')
json_load = json.load(json_open)
news_data_array = json_load['data']
 
#APIキーを入力
key = GOOGLE_LANG_API_KEY

#APIのURL
url = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key=' + key

analysed_data_json = []

for index, news_text in enumerate(news_data_array):
  #基本情報の設定 JAをENにすれば英語のテキストを解析可能
  header = {'Content-Type': 'application/json'}
  body = {
      "document": {
          "type": "PLAIN_TEXT",
          "language": "JA",
          "content": news_text
      },
      "encodingType": "UTF8"
  }
  #json形式で結果を受け取る。
  response = requests.post(url, headers=header, json=body).json()
  #分析の結果をコンソール画面で見やすく表示
  print(response["documentSentiment"])
  analysed_data_json.append({
    "text": news_text,
    "documentSentiment": response["documentSentiment"]
  })
print (analysed_data_json)
with open('./analysed_netnewsdata.json', 'w') as f:
    json.dump({"analysed_data":analysed_data_json}, f, ensure_ascii=False, indent=4)

analysed_netnewsdata.jsonに保存するようにした。

結果は

{
            "text": "「シン・ゴジラ」が...(長くなるので省略)",
            "documentSentiment": {
                "magnitude": 8.1,
                "score": 0
            }
}

てな感じで収めた。

https://raw.githubusercontent.com/iinoten/tweet-test-bot/master/analysed_netnewsdata.json

↑見たかったらどうぞ。

これで記事全体の傾向とかを読みとれるようになるはず。でももうちょっとひとひねりしてわかりやすくビジュアル化したい。

Pythonだったらそれっぽくグラフとか作れるやろ

書いた

てことでググったらでてきた。

www.python.ambitious-engineer.com

matplotlibとかいうので出来るらしい。

pipでの導入→NumPy、pandas、Matplotlib をpipでインストールする方法 | ガンマソフト株式会社

コードの参考→matplotlib入門 散布図の作成 | Python学習講座

そんで参考にしたコードを実行してグラフを表示しようとしたら「バックグラウンドでそれ用の表ソフト起動しとけや」みたいな事を言われたのでめんどくさいので画像として保存することにした。

参考→matplotlibの図を表示できないエラーを解決する方法 | にわこま ブログ

書いたやつ

import matplotlib.pyplot as plt
import json
 
json_open = open('./analysed_netnewsdata.json', 'r')
json_load = json.load(json_open)
analysed_news_data_array = json_load['analysed_data']

# 対象データ
x = []
y = []

for index, analysed_result in enumerate(analysed_news_data_array):
  x.append(analysed_result["documentSentiment"]["magnitude"])
  y.append(analysed_result["documentSentiment"]["score"])
 
# figureを生成する
fig = plt.figure()
 
# axをfigureに設定する
ax = fig.add_subplot(1, 1, 1)
 
# axesに散布図を設定する
ax.scatter(x, y, c='b', s=1)
 
# 表示する
plt.savefig("scalar_plot_graph.jpg")

それで出来た散布図

f:id:ten0313:20200923033719j:plain

縦軸がscore、横軸がmagnitude。最頻値とか中央値とか平均値とかわかるもん?数学とかわかる人は表とか見ただけで。俺はさっぱりわからん。

とりあえずscoreが0なのが多いのんな~ってこととmagnitudeが10を超えるものって少ないんだな~ってことくらい。文章の文字数とかをグラフに入れこんでない状態だから次やる時はそこらへんも含めて見ていきたいな。

てかこれそもそも散布図が正しい表示方法なのかもよくわからん。