Google Natural Language API에서 트윗의 감정 점수를 그래프로 보았습니다.

소개



자연 언어 처리를 조금 만져보고 싶었기 때문에, 간단하게 사용할 수있는 것 같은 Google Cloud Natural Language API 를 사용해 보았습니다. 이 API는 주어진 데이터의 「엔티티 분석」이나 「구문 해석」등 실시할 수 있습니다만, 이번은 확실히 재미있는 것 같은 「감정 분석」을 사용해 보았습니다.

분석할 데이터



어떤 언어 데이터의 감정을 분석할지 헤매었습니다만, 가장 친밀한 자신의 tweet를 사용해 보았습니다. 아마도 tweet이 가장 자연스러운 단어를 사용하는 방법을 가지고 있기 때문에 감정 분석하기에는 상당히 재미 있다고 생각합니다.

tweet 데이터를 얻으려면 다음 코드를 작성했습니다.
require 'twitter'
require 'uri'

class TwitterContent

  def initialize()
    client = Twitter::REST::Client.new do |config|
      config.consumer_key        = "YOUR_CONSUMER_KEY"
      config.consumer_secret     = "YOUR_CONSUMER_SECRET"
      config.access_token        = "YOUR_ACCESS_TOKEN"
      config.access_token_secret = "YOUR_ACCESS_SECRET"
    end

  end

  def get_tweets(count)

    tweets = [@client.user_timeline(count: 1)][0]
    remaining = count
    roop_count = 200

    until remaining == 0 do
      @client.user_timeline(count: roop_count, max_id: tweets.last.id-1).each do |t|
        remaining -= 1
        if remaining == 0
          break
        end

        unless URI.extract(t.text).empty?
          next
        end
        tweets << t

      end

      if remaining < 200
        roop_count = remaining
      end
    end
    tweets.map! {|t| t.text}
  end

end


덧붙여서 나의 트위터는 newspick를 공유하고 있는 것이 많았으므로, url를 포함하고 있는 것은 제외하기로 했습니다.
( #get_tweets 는 tweet를 원하는 수만큼 얻는데 쓴 메소드이지만, 우리하면서 매우 더러운 코드가 되어 있습니다...개량하고 싶다...)

Google Natural Language API



이 API의 사용법은 대부분 문서에 기재되어 있다 빠른 시작 와 같기 때문에 설명은 생략합니다. 데이터를 자바 스크립트로 처리하고 싶었기 때문에 Sinatra를 사용하여 템플릿에 전달하고 있습니다.

감정 분석 API의 반환값으로는 scoremagnitude 가 반환되어 정의는 다음과 같습니다.
  • score : 문서의 전반적인 감정 (-1.0 ~ 1.0 : 네거티브 ~ 긍정)
  • magnitude : 문서에 감정적 인 내용이 얼마나 포함되어 있습니까

  • 본래는 분석 대상의 데이터에 맞추어, 각각의 값을 해석하면 좋은 것 같습니다. 이번은 살짝 개관하고 싶을 뿐이므로 score 만을 이용합니다.
    require 'sinatra'
    require 'sinatra/reloader' if development?
    require 'json'
    
    require './twitter_content'
    require "google/cloud/language"
    
    get '/' do
    
      t = TwitterContent.new()
      tweets = t.get_tweets(500)
    
      language = Google::Cloud::Language.new
      sentiment_scores = []
      tweets.each do |t|
        sentiment_scores << language.analyze_sentiment( content: t, type: :PLAIN_TEXT).document_sentiment.score
      end
    
      scores = sentiment_scores.map! {|s| s.round(1)}
    
      #下記
    end
    
    

    google api 인증에 대한 몇 가지 방법이 있다고 생각하지만 이번에는 서비스 계정으로 인증을 받고 있습니다. 자세한 내용은 여기 .

    그래프용 데이터 전처리



    이번에는 js의 C3.js를 사용하여 그래프 그리기를 시도했습니다. 다음 코드는 그래프를 그리기 위해 값을 전처리합니다. x軸用のカテゴリ カテゴリごとのtweet数 全体に対してのカテゴリごとの割合 를 그래프용으로 처리하고 있을 뿐이므로, 더 스마트가 하는 방법도 있다고 생각합니다.
    get '/' do
    
      #上記
    
      # [-1.0..1.0]を0.1刻みで作成
      categorical = []
      -1.0.step(1.0,0.1).each do |f|
        categorical << f.round(1)
      end
    
      # カテゴリごとの数を集計
      count_hash = Hash.new(0)
      scores.each do |s|
        count_hash[s.to_s] += 1
      end
    
      # カテゴリの値それぞれに値を貼り付ける。なかったら0
      scores_hash = {}
      categorical.each do |c|
        scores_hash[c] = count_hash.has_key?(c.to_s) ? count_hash[c.to_s] : 0
      end
    
      # 全体に関するそれぞれの値の割合
      scores_ratio = []
      scores_sum = scores_hash.values.inject(:+)
      scores_hash.values.each do |v|
        scores_ratio << ((v.to_f / scores_sum.to_f)*100).round(1)
      end
    
    
      @scores = scores_hash.values
      @categorical = categorical
      @count = tweets.count
      @scores_ratio = scores_ratio
    
      erb :index
    
    end
    

    C3.js로 그래프 그리기



    그리고는 view측에서 그래프를 그리는 것 뿐입니다. 이번에는 두 종류를 그려 보았습니다.

    ※C3.js는 D3의 래퍼이므로, D3.js를 넣을 필요가 있습니다만, D3.js v3가 필요합니다. D3.js v4에서는 움직이지 않습니다.

    점수당 수


    <!DOCTYPE html>
    <html>
      <head>
        <link href="/css/c3.css" rel="stylesheet">
        <script src="https://d3js.org/d3.v3.min.js"></script>
        <script src="/js/c3.min.js"></script>
      </head>
      <body>
        <p>分析対象: <%= @count %>つのtweet</p>
        <div id="chart"></div>
        <div id="c2"></div>
        <script>
        window.onload = function(){
          const scores = <%= @scores %>
          const categorical = <%= @categorical %>
          const scores_ratio = <%= @scores_ratio%>
    
          const chart = c3.generate({
            data: {
                 columns: [
                     ['scores'].concat(scores)
                 ],
                 type: 'bar'
             },
             axis: {
               x: {
                   type: 'category',
                   categories: categorical
               },
                  y: {
                    label: {
                      text: '個',
                      position: 'outer-top'
                    }
                  }
            }
          });
        }
        </script>
      </body>
    </html>
    



    전체에 대한 카테고리별 비율



    조금 절대 수만 보아도 잘 모르기 때문에, 그 비율을 보자.
    다음과 같이 변경하면 비율을 쉽게 볼 수 있습니다.
    <!DOCTYPE html>
    <html>
      <head>
        <link href="/css/c3.css" rel="stylesheet">
        <script src="https://d3js.org/d3.v3.min.js"></script>
        <script src="/js/c3.min.js"></script>
      </head>
      <body>
        <p>分析対象: <%= @count %>つのtweet</p>
        <div id="chart"></div>
        <div id="c2"></div>
        <script>
        window.onload = function(){
          const scores = <%= @scores %>
          const categorical = <%= @categorical %>
          const scores_ratio = <%= @scores_ratio%>
    
          const chart = c3.generate({
              data: {
                  columns: [
                      ['scores_ratio'].concat(scores_ratio)
                  ],
                  types: {
                      scores_ratio: 'area'
                  }
              },
              axis: {
                x: {
                  type: 'category',
                  categories: categorical
                },
                y: {
                  label: {
                    text: '%(パーセント)',
                    position: 'outer-top'
                  }
    
                }
              }
          });
    
        }
        </script>
      </body>
    </html>
    



    이번에는 최신 500건의 트윗 중 url을 포함하지 않는 168개의 트윗이 분석 대상이 되었습니다.
    이렇게 개관해 보면, 미묘하게 긍정적인 쪽이 많을까? 라는 느낌이군요. 물론 magnitude 등도 외시하고 있기 때문에 실제로는 뭐라고 말할 수 없습니다만, 대체로도 가시화할 수 있으면 상당히 즐거운 것입니다. 여러분도 꼭 해보세요.

    해봐



    어쩐지 떠올랐던 일이었습니다만, 매우 즐거웠습니다. 하지만 거기에 더해 코드의 쓸 수 없는 것을 실감했습니다. 특히 each 밖에 블록 취급 할 수없는 것이 아마추어 느낌에 빠져 나오고 있군요 ... 데이터 처리 더 잘되고 싶다.

    참고



    D3 Document
    C3 Document
    Google Natural Language API
    Twitter Gem

    좋은 웹페이지 즐겨찾기