CSVさえあればいい！形態素解析→単語感情極性対応表で超お手軽ネガポジ分析♪ (with Rails)

この記事は[フロムスクラッチ Advent Calendar 2016](http://qiita.com/advent-calendar/2016/fromscratch)の1日目の記事です。

いえーい！何書いていいかわからなーい！！時間なーい！
よくあるやつでいい！と言われたので、最近やったこと書きます(-人-)

__記事の通りにやると！CSVだけあればネガポジ分析結果をRailsで作れます！__
__コピペで大丈夫です！！__
...たぶん動くはず...動作確認甘いかも..その場合コメント下さいませ...絶対なんとかします..

# 書くこと
先週くらいに社員管理ツールの紹介コンテンツとして、
社員管理ツールから出力したcsvを元にrailsで分析して、
___社員ネガポジランキング___なるものを発表してみました。

やり方は超単純！なので、プログラム初心者でも
簡単に同じことできるような記事を書いてみます♪
何かに使って頂ければ(ﾉ・_・)

# 手順
* 形態素解析用ライブラリ(natto)を設定する
* 対象データのcsvと単語感情極性対応表をrailsでDBに簡単に取り込む
* 形態素解析して点数つけてDBに登録してみる

みたいな感じでやってきます。

処理時間はrubyなのでちょっとかかりますが、
手を動かす時間は少ない！！はず。
rails new hogehogeは省きますのでそこまではやっておいてくださいませ。

ちなみにrails初心者ですので
もしよければ間違いとか改善とかコメント頂ければ嬉しくて涙しますm(_ _)m

# 解説
## 形態素解析用ライブラリ(natto)を設定する for Mac
### MeCabインストール

※ファイル名がないやつはterminalから実行してください。
※rake 〇〇とかrails 〇〇とかbundle 〇〇は作ったrailsアプリのGemfileとかあるルート階層でお願いします。

```
brew install mecab
brew install mecab-ipadic
```

gemにnattoを追加(Gemfileに追記します)

```:Gemfile
gem 'natto'
```

gemをインストール

```
bundle --path vendor/bundler
# bundleだけでもOK
```

## 対象データのcsvと単語感情極性対応表をrailsでDBに簡単に取り込む

配置するcsvの一行目はカラム名にして下さいね★
seeds.rbで辞書データと分析対象のデータを取り込みます。
※どんなcsvでもいけるようにするため、かつ、
　最短手数でやるためmodelとか明示的に作らず進めます(*_ _)

```ruby:seeds.rb
require 'csv'

def download_dic(dic_file_path)
  url = 'http://www.lr.pi.titech.ac.jp/~takamura/pubs/pn_ja.dic'
  open(url, "r:cp932") do |file|
    open(dic_file_path, "w+b") do |out|
      # ヘッダだけ追加
      out.write('word,word_kana,part_of_speech,score')
      out.write(file.read.gsub!(':', ','))
    end
  end
end

# db/initial_dataには対象のデータのCSVおいておこう
# csvのファイル名は複数形！

csv_file_fir = 'db/initial_data'
# FileUtils.mkdir_p(csv_file_fir) unless File.directory?(csv_file_fir)

# 辞書がなければ取得
dic_file_base_name = 'pn_dics'
dic_file_path = "#{csv_file_fir}/#{dic_file_base_name}.csv"
download_dic(dic_file_path) unless File.exist?(dic_file_path)

connection = ActiveRecord::Base.connection

# initial_data以下のcsv分テーブル作成
Dir.glob("#{csv_file_fir}/*.csv") do |csv|
  table_name = File.basename(csv, '.csv')
  next if connection.table_exists?(table_name)

  target_csv = CSV.table(csv)
  # もしカラムを分けたければ1行目を取得して判定など、今回不要
  connection.create_table(table_name) do |t|
    target_csv.headers.each do |col_name|
      # dictのスコアカラムだけfloat
      if table_name == dic_file_base_name && col_name == :score
        t.float col_name
      else
        t.string col_name
      end
    end
    # 結果カラムを追加(dictとまとめても良かったけどカラム名分けれるように)
    t.float :score if table_name != dic_file_base_name
  end

  # データを投入(新規テーブルの場合だけ)
  class_name = table_name.singularize.camelcase
  self.class.const_set class_name, Class.new(ActiveRecord::Base)
  klass = table_name.singularize.classify.constantize

  target_csv.each do |row|
    klass.find_or_create_by!(row.to_hash)
  end
end
```

そして実行！

```
rake db:seed
```

長くかかるからログ出した方が楽しいかも(>_<)

## 形態素解析して点数つけてDBに登録してみる
rakeタスク作って採点→登録をしましょう！

今回はusers.csvをおいておいた想定で、
userにまつわる全文字列情報を形態素解析して、スコアを登録する、
という感じの処理にしておきます。

他のファイルの場合やカラム指定の場合も使いまわせるように書いておきます。

まず、rakeタスク「score_user」を作ります。

```
rails g task score_user
```

点数付けのコードを書きます。

```lib/tasks/score_user.rake
namespace :score_user do
  desc '全ユーザーの点数付け'
  task :full_cols => :environment do
    # 動的に作っていますが、modelを作成しておいてもOK
    self.class.const_set 'User', Class.new(ActiveRecord::Base)
    User = 'User'.classify.constantize
    self.class.const_set 'PnDic', Class.new(ActiveRecord::Base)
    PnDic = 'PnDic'.classify.constantize

    User.all.each do |user|
      # 全フィールドを結合しています。ここで対象カラムを絞って結合すると良い感じです。
      full_text = user.attributes.values.join(",")
      require 'natto'

      mecab = Natto::MeCab.new
      # 形態素解析した文字リストを作ります
      list = []
      mecab.parse(full_text) do |n|
        list.push(n.surface) # if n.feature.match('名詞')
      end

      score = list.reduce(0.0){|sum, l|
        # 点数が付く場合、かつ点数がプラスの時だけを加算していみます
        pn = PnDic.find_by(word: l)
        sc = pn.score if pn.present? && pn.score > 0
        sum + (sc || 0.0)
      }
      user.score = score # ワードカウントで割り算してもOK -> / list.length
      user.save!
    end
  end
end
```

taskを実行すると、スコアが付きます！

```
rake score_user:full_cols
```

確認してみましょう！

```
rails db
```

```sql
select * from users;
```

とか、

```
rails c
[1] pry(main)> self.class.const_set 'User', Class.new(ActiveRecord::Base)
[2] pry(main)> User = 'User'.classify.constantize   
[3] pry(main)> p User.first.score
```

csvを配置して記事の通りに書くだけで
railsの機能を活用してお手軽にネガポジ分析できました★

ぜひ色々応用してみて下さい。

# 結論！
__めりくりまであと25日！(あってる？)__