N-gramモデル作ってみた
ちょっと必要になったので。
今までテキストデータを使った研究をしていた時には、一つのドキュメントにそれなりの文字数が使われていたので、
形態素解析(chasen・MeCab)→助詞等を削る→各ドキュメントを単語のfeatureベクトルに
という流れが常套手段だったわけですが、Twitterの分析をしていると、140文字という制約、新語、誤字脱字等の問題があり、この方法ではちょっと難しいかなぁと感じました。
そこで、今回はN-gramを使って各文章のfeautureベクトルを作って分析してみようと思いました。
N-gramモデルに関しては、大学院の授業で多少学んでいたので、そこまで時間はかかりませんでした。
自分用にカスタマイズしてある部分が多いので、無駄が多いです。また、所属に関係する部分は取り除いたりしたので、プログラムが多少崩れています。
使っているデータは以下のような形
データの中身 A_Koide0519 \t 2011/10/02 12:11:30 \t きゃりーなんちゃらってなんだ? ・ ・ ・
出力はtweetの各文字列の出現頻度(ラベルリスト)、WORDID、Tweet集合、Timestampとなっています。(ここまで分割した深い意味はありません)
# -*- coding:utf-8 -*- require 'pp' n = ARGV[0] #N Dn = n.to_i name = ARGV[1] #ファイル名 name3 = ARGV[2] #print time stamp name4 = ARGV[3] #print wordlist name5 = ARGV[4] #print wordid name6 = ARGV[5] #print tweet VecA = [] VecB = [] VecC = [] s = name.to_s def readValue(name) i = 0 File.open(name) do |f| f.each_line do |line| if i != 0 col = line.chomp.split("\t") c = col[1].gsub("/"," ").gsub(":"," ").split(' ') day = Time.local(c[0],c[1],c[2],c[3],c[4],c[5]) s = day.to_i VecA[i-1] = s VecB[i-1] = col[2].upcase #文字列を統一するため end i += 1 end end return i-1 end def calNgram() k = 0 hash = Hash.new() mat = Array.new(VecB.size) mat2 = Array.new(VecB.size) for i in 0..VecB.size-1 mat[i] = Array.new(1);mat2[i] = Array.new(1) end for i in 0..VecB.size-1 list = VecB[i].split(//) if list.size < Dn next end 0.upto(list.size - Dn) do |c| word = list[c..c+Dn-1].join('') if VecC.index(word) == nil VecC[k] = word mat[i].push(k+1) hash.store(word,1) k += 1 else n = VecC.index(word) if hash.key?(word) == true p = hash[word] hash.store(word,p+1) else mat[i].push(n+1) hash.store(word,1) end end end col = hash.values for j in 0..col.size-1 mat2[i].push(col[j]) end hash.clear() end return mat,mat2 end def printValue(name3,name4,name5,name6) File.open(name3,"w") do |name3| for i in 0..VecA.size-1 name3.puts VecA[i] end end File.open(name4,"w") do |name4| name4.printf("%d %d 1\n",Dm,VecC.size) for i in 0..MatA.size-1 name4.printf("%d ",MatA[i].size-1) for j in 1..MatA[i].size-1 name4.printf("%d:%d ",MatA[i][j],MatB[i][j]) end name4.puts "1 1\n" end end File.open(name5,"w") do |name5| for i in 0..VecC.size-1 name5.printf("%d %s\n",i+1,VecC[i]) end end File.open(name6,"w") do |name6| for i in 0..VecB.size-1 name6.printf("%s\n",VecB[i]) end end end Dm = readValue(name) MatA,MatB = calNgram() printValue(name3,name4,name5,name6)
使い方は
ruby ngram.rb N ファイル名 N.tmp N.lbl N.wid & 第一引数にNを指定すればそれに合わせて出力してくれます。 結果 ラベルリスト tweet数 文字列数 各tweetの異なり文字列数 1:1 2:1 3:1 … ・ ・ ・ WORDID(tri-gramの場合) 1 きゃり 2 ゃりー 3 りーな ・ ・ ・
という感じ。核はcalNgramメソッドになります。
何か分析結果が形になれば、またお知らせしたいですね。