2017年7月29日に行われた2017年度隅田川花火大会。 7月27日午前9時から花火大会翌日の30日午前9時までの隅田川花火大会に関するツイートの時系列での感情分析結果はこんな感じでした。
ポジティブなツイートほど1に近く,ネガティブなツイートほど-1に近づきます。 なかなかネガティブですね! 以下で分析方法を解説します!
雨の中開催された隅田川花火大会
早速ですが花火大会当日前後の天気を見てみましょう。Pythonを使って気象庁のホームページからデータを引っ張ってきました。以下のサイトを参考にしています。
27
|
28
|
29
|
|
---|---|---|---|
昼(06:00-18:00)
|
曇
|
曇時々晴
|
曇後一時雨、雷を伴う
|
夜(18:00-翌日06:00)
|
曇時々雨
|
曇
|
雨後一時曇、雷を伴う
|
花火大会前々日から不安定な天気,そして当日はバッチリと雨が降っていたようですね。それでももちろん花火大会は(強風がない限り)開催されます。 花火大会当日の東京の天気予報が出た頃からTwitter上では様々な発言が飛び交いました。想像に難くないと思いますが概ね以下のようなものです。
- 天候不良による花火大会の中止を危惧する声
- 天候不良による花火大会の中止を期待する声
- 雨天時の開催の是非に関するお知らせ
- 脳内妄想ツイート
理系単科大学生である僕のタイムライン上では2番と4番が多かったですね。そもそも触れてない人が殆どでしたが。 それでは,隅田川花火大会前々日から当日までのツイッター民の感情はどのように推移したのでしょうか?
花火大会に関するツイートをしたアカウントの一つ一つを見て行くのはTwitter APIを用いなければならず,その仕様上時間がかかりすぎるためTwitter上の全体的な傾向のみを分析することにします。
実行環境
・ conda 4.3.23
・ python 3.6.0
・ jupyter notebook 4.3.1 その他,numpyやpandasなどのパッケージ,および後に特筆するGetOldTweets-pythonを用いました.最後のコレは上記に環境での使用に当たって注意が必要です.
Twitterからツイートを検索し取得する
Twitter社は,プログラムを用いてTwitter上のデータを収集するためのAPIを公開しています。一回に取得できるツイート数に制限があったり短時間に何回も取得を行えなかったりと使い勝手がよくないと言われています。(こちらがデータをタダで使おうとしてるからですが.)
またこのAPIの最も致命的な点は7日前までのツイートしか取得できないのです.。8月5日を過ぎてしまったためTwitter APIを用いて隅田川花火大会前後のツイートを収集することができません!
そこでHTTPリクエストを用いてツイートを取得するGetOldTweets-pythonを用いました。参考にしたサイト及びGetOldTweets-pythonのgitリポジトリは以下の通りです。
・ stackoverflow.com
・ github.com
なお,このGetOldTweets-pythonはpython3で廃止となった(厳密にはurllibパッケージに統合された)urllib2を用いていますので,python3.6.0の本環境で使用するためにurllib2を使用している部分を書き換えて使用しました。python3以前なら問題ないと思います(検証はしてません)GitHubからクローンしたgot3フォルダを作業ディレクトリに持って来てimportすると使用できます。
それでは,2017-7-27から2017-7-29までの隅田川花火大会に関するツイートを収集しましょう!
以下のメソッドを活用してクエリを作成します。
- setUsername (str): An optional specific username from a twitter account. Without “@”.
- setSince (str. “yyyy-mm-dd”): A lower bound date to restrict search.
- setUntil (str. “yyyy-mm-dd”): An upper bound date to restrist search.
- setQuerySearch (str): A query text to be matched.
- setTopTweets (bool): If True only the Top Tweets will be retrieved.
- setNear(str): A reference location area from where tweets were generated.
- setWithin (str): A distance radius from “near” location (e.g. 15mi).
- setMaxTweets (int): The maximum number of tweets to be retrieved. If this number is >- >- unsetted or lower than 1 all possible tweets will be retrieved.
このライブラリの一日は朝9時始まりらしいので指定期間は 2017-7-27 から 2017-7-30, 検索文字列は “#隅田川花火大会 OR 隅田川花火大会 OR (隅田川 AND 花火) OR (隅田川 AND 花火大会)“ と指定します。
英語では,語句の区切りに空白文字を使用しますが日本語では使用しないため文字列検索がどういった挙動を示すかわかりません。そのため大事を取って広めに指定しました。条件に合致するすべてのツイートを集めるため setMaxTweets() は無指定で行きました。
以下スクリプトです.
リクエストが処理されるまで2時間ほどかかりました。せっかく取得したものを失うのが怖いので早急にcsvファイルに退避させます。1日ごとに抜き出した方がいいかもしれません.僕は1時間半越えたあたりまで処理したところを2回失敗してます(泣
pandas.DataFrame() にデータを詰めて処理していきます。
ここで df_tweets.head() にて冒頭を確認したかったのですがたった5ツイートの中に検閲対象のものがありました。 投稿時間について降順に格納されていました。またもしかしたらと思ってidも抽出しましたが,いらない気もするので消します。また,indexになっている投稿時間を datatime 型に変換し,昇順にします。
これで,7月27日から7月29日までの隅田川花火大会に関するツイートが時系列順に揃いました! 引き続き,感情分析を行う準備をしましょう。
ツイートの形態素解析及び印象の評価
取得したツイートはただの文字の集合であるため扱いづらいです。 そこで,ツイートを形態素解析して単語ごとに分け,基本形表記に変換します。
そして得られた単語一つ一つの「ポジティブさ」または「ネガティブさ」を評価します。 形態素解析にはMeCabを使い,単語の「ポジティブさ」の評価はPN Tableという辞書を用いました。PN Tableは単語の印象が-1から+1の実数で表された辞書で,+1に近いほど「ポジティブな」印象,逆に-1に近いほど「ネガティブな」印象を持つとしています。
こういった「ポジティブ」,「ネガティブ」といった性質を極性といい,極性の値をPN値と呼びます。 文章の感情分析には以下のサイトを参考にしました。また,リンク先のPN Tableファイルの文字コードはUTF-8ではないのでnkfコマンドなどを用いてutf-8に変換しておくことをお勧めします。
必要な処理は以下です
- テキスト(文章)を形態素解析し,単語の集合を得る。
- 単語ごとのPN値を求める。
- 各ツイートの平均のPN値を求める。
上記のサイトを参考に実装しました。
では,さきほど抽出したデータ一件一件に対して処理を適用していきましょう。PN Tableに含まれない語句は考慮の対象外とし,PN Tableに含まれる語句が一つもない場合,そのツイートのPN値は0となります。
えられたPN値の平均値のリストを対応するツイートと統合します。
ツイートの感情の可視化
この三日間(7/27-29)の隅田川花火大会に関するツイートのPN値の平均を出してみます。
取り得る値は-1から1までの実数値なのですが,少々低過ぎやしないですかね? しかし,これはあくまでも単語の印象を元に算出した値です。 そもそも言語が崩壊しているようなツイート,広告ツイートなど予め除外した方がいいツイートはたくさんあるでしょうし,ネットスラングに対する辞書の不完全さも正確な評価が難しい理由の一つでしょう。 また,ツイート数も日によって偏りがあります。
最後に,ツイートの極性の時系列での推移がわかるようにデータを可視化しましょう! 最初に日付はpandasのTimeStampとして格納していたので楽ですね!
こうやって見るとなんか,うーん... ネガティブなツイートが終始多いですね,特に前日にかけてのが.これ,雨が降ったことに対する「ざまぁ!!」も負の印象として扱われているのでしょう。 日頃のタイムライン上でも,ポジティブな内容のツイートって少ないのではないでしょうか? 最後までご覧頂き,ありがとうございました!