Hatena::ブログ(Diary)

umzwkynの日記

2010-05-27

形態素解析, 語のtf-idf値を求める

20:51

calculate.rb

# -*- coding: utf-8 -*-
# 任意の対数の底で計算する
def log_n(x,n) # n:底
  return (Math::log(x))/(Math::log(n.to_f))
end

jlma.rb

# 受け取った文字列を形態素解析して、結果を配列に入れて返す関数
def jlma_ch(ch) # Japanese language morphological analysis
  mecab = MeCab::Tagger.new()
  node = mecab.parseToNode(ch)
  ary = Array.new
  while node do
    ary.push("#{node.feature}".split(","))
    # ary.push( "#{node.surface}\t#{node.feature}")
    node = node.next
  end
  arysize = ary.size
  return ary.slice(1..(arysize-2))
end

# 受け取った形態素解析の結果のうち、必要な情報だけを保存しておく関数
def jlma601(ary)
  ra = Array.new
  ary.each{|a|
    a1 = a[1]
     if ((a[0] == "名詞")|(a[0] == "形容詞")) then
       if((a1 != "非自立")&&
          (a1 != "接尾")&&
          (a1 != "数")#&&
          #(a1 != "")#&& # 必要があれば条件追加 
          #(a1 != "")#&&
          #(a1 != "")#&&
          #(a1 != "")#&&
) then
       ra.push([a[6],a[0],a[1]]) # [形態素原形, 品詞, さらに細かい分類]
       end
     end
  }
  return ra
end

# jlma_ch → jilma601 の順に適用させる関数
def jlma(ch)
  return jlma601(jlma_ch(ch))
end

tf_idf.rb

# -*- coding: utf-8 -*-
require 'calculate.rb' # 計算のためのプログラム

# 与えられたハッシュ{key:単語, value:出現回数} から、各単語のtf値を求める関数
# r_hash(空のハッシュ)に{key:単語, value:tf値}を格納する
def tf(hash,num,r_hash) # numは,hashに現れる語の種類の数
  hash.each_pair{|k,v|
    tf = v / num.to_f
    r_hash.store(k,tf)
  }
  return r_hash
end

def idf(file_num,word_num)
  n = file_num.to_f / word_num
  return 1.0+log_n(n,2)
end

docMatrix.rb

# -*- coding: utf-8 -*-
### 使用する際に気をつけること
# 1. 読み込むDIRECTORY名を任意に変更する(18行目)
# 2. 結果出力ファイルも任意に変更する(14行目)
# 3. 結果をExcelで読み込みたいときは、$ nkf -s --overwrite 結果ファイル名
#    (文字コードをutf-8からShift-jisに変更)

### 別ファイルをインクルード ### 
require 'jlma.rb' # 形態素解析をするためのプログラム
require 'tf_idf.rb' # ti,idf,tf-idfの値を計算するプログラム
require 'calculate.rb' # 計算のためのプログラム

# 結果を出力するファイル
w_file = open("result.txt","w")

# ディレクトリ内のすべてのファイル名を読み込む
@filename = Array.new # ファイル名を格納するArray
DIRECTORY = "ディレクトリ名"
Dir::foreach(DIRECTORY) {|f|
  if ((f != "." ) && (f != "..")) then
    @filename.push([DIRECTORY+f,f])
  end
}
@f_num = @filename.length

#### プログラム本体 ####
# 以下のようにマークしておく
# tf計算のために使用 → #tf
# idf計算のために使用 → #idf

@tf = Hash.new #tf

@idf = Hash.new #idf
@idf.default = 0.0 #idf
@word_number = Hash.new #idf{key:ファイル名, value:単語の種類数}
@word_number.default = 0.0

@tfidf = Hash.new

@filename.each{|tempfile|
  openfile = tempfile[0] # パスも含めてすべて表示
  fname = tempfile[1] # ファイル名のみ
  puts fname + ":tf_value"+"..."
  # 現在のファイルで使用するHashの定義
  h = Hash.new #tf
  h.default = 0.0
  # 現在のファイルで現れる単語の総数
  temp_num = 0.0
  # ファイル読み込み
  file = open(openfile)
  text = file.read
  # 形態素解析の結果を得る
  jlma = jlma(text)
  # 現在のファイルに出現した語の頻度を格納する #tf
  # 全ファイルの出現した語の頻度を格納する #idf
  jlma.each{|aj|
    word = aj[0]
    # 出現したら、回数を1増やす (出現回数上書きのため)
    case h[word]  
    when 0.0
      # tf計算のため
      temp_num += 1.0
      time_temp = 1.0
      # idf計算のため
      time_idf = @word_number[word] + 1.0
      @word_number.store(word,time_idf)
    else
      # tf計算のため
      temp_num += 1.0
      time_temp = h[word] + 1.0
    end
 
    # key:語が格納された配列、value:出現回数
    h.store(word,time_temp) #tf
  }
  file.close
  
  # tf-value を求める
  h_r = Hash.new
  # 現在のファイルでの単語の出現回数の結果から、tfを計算し、結果をh_rに入れる
  temp = tf(h,temp_num,h_r)
  # ファイル名、そのファイルの各単語のtf値のハッシュを格納
  @tf.store(fname,temp)
}

puts "idf_value ..."
@word_number.each{|w,v|
  @idf.store(w,idf(@f_num,v))
}

puts "tf-idf_value ..."

=begin
# 結果をこの先の計算でも使う場合
@tf.each{|file,h|
  tfidf_h = Hash.new
  @idf.each{|word,idf|
    if (h[word] != nil) then
      tfidf = h[word]*idf
    else
      tfidf = 0.0
    end
    tfidf_h.store(word,tfidf)
  }
  @tfidf.store(file,tfidf_h)
}
=end

# ファイルに書き出す

### 行列を転置した状態で出力
# 1行目
w_file.print("\t")
n = 1
idfsize = @idf.size
@idf.each_key{|word|
  w_file.print(word)
  if n != idfsize then w_file.print("\t") end
  n += 1
}
w_file.print("\n")
# 2行目以降
@tf.each{|file,h|
  w_file.print(file+"\t")
  counter = 0
  @idf.each{|word,idf|
    counter += 1
    if (h[word] != nil) then
      tfidf = h[word]*idf
    else
      tfidf = 0.0
    end
    w_file.print(tfidf)
    if counter!=idfsize then w_file.print("\t") end
  }
  w_file.print("\n")
}

w_file.close



=begin
@tfidf.each{|f,v|
  puts "*ファイル名:" + f
  v.each{|w,vv|
    puts w + ":" + vv.to_s
  } 
}
=end

=begin
@idf.each{|w,v|
  puts w + ":" + v.to_s
}
=end

任意の対数の底で計算する

13:26

# 任意の対数の底で計算する
def log_n(x,n) # n:底
  return (Math::log(x))/(Math::log(n.to_f))
end

テキストファイルの文字コードを調べる

11:53

nkfという、古い漢字コード変換プログラムを利用します。

ファイルの文字コードを調べる

$ nkf --guess hoge.txt

文字コードの変換

$ nkf -w --overwrite hoge.txt

2010-05-21

RubyでMeCab

18:59

Macで, MeCabRuby から使いたい。

こちらのサイトを参考に MeCabインストール.

一緒に rb-mecabインストール.

http://son-son.sakura.ne.jp/programming/tips_macportsmecab.html

sudo port install mecab
sudo port install mecab-ipadic-utf8
sudo port install rb-mecab

辞書の設定

sudo emacs /opt/local/etc/mecabrc

で、以下の1行を追記

dicdir = /opt/local/lib/mecab/dic/ipadic-utf8

以下の処理も忘れずに。

;dicdir =  /opt/local/lib/mecab/dic/ipadic

サンプルプログラムを書いて実行したところ, ちゃんと動いてる!

複数のPCを使っているときに便利なツール

12:55

研究室で2台のPCを使っているので、Dropboxを使い始めました。

すごーく便利☆

Snow LeopardでTeX

12:38

MacのマシンにTeXを入れる際に参考にさせていただいたのが、Snow LeopardでTeXというサイトです。

上手くいなかない時は、pTeX.appの初期設定を何度か試したり、再起動してみるといいです。

まずは、パワポ等の資料作成に便利なLaTeXiTをこれから使います。

2010-05-19

RとMeCabで遊ぶ。その1

18:15

言語処理の基礎を学ぼう!ということで、Rによるテキストマイニング入門を読んでいます。

その記録をメモ。

◇ R のインストール

こちらから、ダウンロード。

インストール後は、ニューの[ファイル]-[ディレクトリの変更]から、作業用ディレクトリを変更しておく。

◇ MeCabをインストール

こちらからmecab-0.98をダウンロード。

どこでも実行できるように、PATHを通しておく

  • 変数名:mecab
  • 変数値:C:\Program Files\MeCab\bin

分からない場合はこちらを参考。

◇ RMeCabのインストール

詳細は、こちらのサイトを参照。

本で紹介されていたサイトからはダウンロードできなかったので(20100519現在)こちらのサイトから、RMeCab_0.89.zip をダウンロード

Rを起動→メニューの[パッケージ]-[ローカルにあるzipファイルのインストール]を選んでインストール。

ただし、ReMacabInstall.batを実行すると、以下のエラーメッセージが発生。とりあえず無視。

アクセスが拒否されました。
      0個のファイルをコピーしました。
ECHOは<OFF>です。

RMeCabをRで使う際には、メニューの[パッケージ]-[パッケージの読み込み]からRMeCabを選ぶ必要がある。

とりあえず今日はここまで。