続・ひらがなの状態遷移図
前回のGraphvizでの状態遷移図の出力時に
:rankdir => "LR"
というオブションをつけていたのだが、これをやると帰ってこない(がんばって配置しようとして終わらない?)らしくこれを外せばよさげ。
以上のことを踏まえて青空文庫からとってこれるだけの文章を集めて状態遷移表(図)を作る。
前準備としてSiteSuckerをもちいてサイト全体をローカルに引っ張ってくる。
青空文庫にあるテキストデータは実際には
http://www.aozora.gr.jp/cards/****/files/*****.html
にあるHTMLファイルにあるので、これを解析すればよさそう。特に今回は”ひらがな”だけなのでHTMLをパースする必要もなくただひらがなだけ集めてくればいい。
注意点として青空文庫のHTMLファイルはすべてSJISになっているらしく、今回の環境ではUTF-8でやったのでこの変換作業が必要になる。
手順としては
- htmlファイルの探索
- 漢字/カタカナを含むSJISのhtmlファイルをEUC-JPのひらがなからなるファイルに変換
- EUC-JP -> UTF-8への変換
- それぞれのファイル(文章)から平仮名のみを抽出->配列の生成
- あるひらがなからあるひらがなへの遷移数をカウント
- すべての文章の遷移数を合計、そこから遷移確率の算出
- グラフに出力
それぞれ1から3までをhira_analyze.rb, 4から6までをhira_map_generator.rb,7をhira_graph.rbが担当。
2ではkakasi, 3ではnkf, 4ではmojiモジュール,7ではGraphvizを使ってこれを行った。
ちなみにGraphvizで図を出力する際にものすごい時間がかかったので1%以上の遷移確率がないエッジについては省略した。
それでもできた画像は32767x4291、56.7MB。そして生成に106ぷんかかった。
一応これがzip圧縮したファイル
require 'nkf' p "***** searching for directory *****" data_dir = "./cards" kakasi_output_dir = "./kakasi_euc_out" utf8_file_dir = "./utf8_output" origin_file_array = Array.new Dir.foreach(data_dir) do |ent| # ./cards/**** if ent.to_s[0].chr == "." then next end child_dir = data_dir + "/" + ent if !File.directory? child_dir then next end Dir.foreach(child_dir) do |child_ent| # ./cards/----/**** unless child_ent == "files" then next end files_dir_path = child_dir + "/" + child_ent Dir.foreach(files_dir_path) do |file_ent| #./cards/----/files/**** if file_ent.to_s[0].chr == "." then next end unless file_ent.include?(".html") then next end child_ent_path = files_dir_path + "/" + file_ent origin_file_array << child_ent_path end end end p "Total #{origin_file_array.length} files found" p "***** kakasi proccessing *****" p " converting sjis kanji, katakana included text to euc hiragana text" unless File.exists?(kakasi_output_dir) then Dir.mkdir(kakasi_output_dir) end origin_file_array.each do |file_to_kakasi| filename = File.basename(file_to_kakasi) output_file_name = kakasi_output_dir + "/" + filename print "kakasi processing : #{file_to_kakasi} > #{output_file_name}" system("kakasi -JH -KH -i sjis -o euc < #{file_to_kakasi} > #{output_file_name}") print " ... done\n" end p "done kakasi : kanji katakana sjis -> hiragana euc converting" p "***** euc->utf8 converting *****" unless File.exists?(utf8_file_dir) then Dir.mkdir(utf8_file_dir) end Dir.foreach(kakasi_output_dir) do |file| orig_file_path = kakasi_output_dir + "/" + file orig_file_name = File.basename(file) if orig_file_name[0].chr == "." then next end utf8_file_path = utf8_file_dir + "/" + orig_file_name print "processing #{orig_file_path} -> #{utf8_file_path}" File.open(orig_file_path) do |eucfile| tmp = eucfile.read utf8_tmp = NKF.nkf('-w',tmp) File.open(utf8_file_path,"w") do |utf8_file| utf8_file.puts utf8_tmp end end print " .. done\n" end
$KCODE = "UTF8" require "rubygems" require "moji" require 'graphviz' data_dir = "./utf8_output" # hiraganas = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよわをんぁぃぅぇぉゃゅょがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽっ".split(//) $hira_to_num = Hash.new i = 0 hiraganas.each do |char| $hira_to_num[char] = i i+=1 end $hiragana_num = hiraganas.length p $hiragana_num def file_to_hiragana_list(file_path) data_array = nil; File.open(file_path) do |file| data = file.read data_array = data.split("") data_array.delete_if do |char| if !Moji.type?(char, Moji::ZEN_HIRA) then true else false end end end return data_array end def count_in_array(array) return_array = Array.new($hiragana_num){|i| Array.new($hiragana_num) {|i| 0.0 } } if array == nil then return return_array end limit = array.length() -1 0.upto(limit) do |n| current_char = array[n] next_char = array[n+1] current_num = $hira_to_num[current_char] next_num = $hira_to_num[next_char] if current_num == nil || next_num == nil then next end i = return_array[current_num][next_num] i += 1 return_array[current_num][next_num] = i end return return_array end def merge_array(base_array, array_to_add) limit = $hiragana_num - 1 0.upto(limit) do |n| 0.upto(limit) do |m| base_array[n][m] += array_to_add[n][m] end end end def generate_p_array(array) p_array = count_in_array(nil) limit = $hiragana_num -1 0.upto(limit) do |n| total = 0 0.upto(limit) do |m| total += array[n][m] end 0.upto(limit) do |m| if total == 0 then p_array[n][m] = 0 else p_array[n][m] = array[n][m] / total end end end return p_array end p "***** HIRAGANA RIST *****" p $hira_to_num p "***** Text -> hash in hash *****" hira_map = count_in_array(nil) i = 0 Dir.foreach(data_dir) do |entry_name| if entry_name[0].chr == "." then next end target_file_path = data_dir + "/" + entry_name print "processing : #{target_file_path}\n" print "array -> hiragana list " array = file_to_hiragana_list(target_file_path) print "-> counter hash " count_array = count_in_array(array) print "-> merging hash " merge_array(hira_map, count_array) p "-> done" i+=1 end p "***** saving counter *****`" File.open("counter.yml","w") do |file| file.write YAML.dump(hira_map) end p " ... done" p " ***** generating probability array *****" p_array = generate_p_array(hira_map) p "***** Probability array is like this *****" p p_array p "***** YAMLizing map *****" File.open("hiragana_map","w") do |file| file.write YAML.dump(p_array) file.flush end p "DONE ALL #{i} files"
$KCODE = "UTF8" require "rubygems" require "moji" require "graphviz" $hiraganas = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよわをんぁぃぅぇぉゃゅょがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽっ".split(//) $hira_to_num = Hash.new i = 0 $hiraganas.each do |char| $hira_to_num[char] = i i+=1 end $hiragana_num = $hiraganas.length p_array = Array.new File.open("./hiragana_map") do |file| tmp_str = file.read p_array = YAML.load(tmp_str) end GraphViz::new("G", { :type => "digraph", :use => "dot", :output => "png", :charset => "utf8", :file => "hiragana.png" }){|g| g.node[:fontname] = "osaka" g.node[:style] = "filled" g.edge[:fontname] = "osaka" limit = $hiragana_num - 1 $hiraganas.each do |char| n = $hira_to_num[char] origin = g.add_node(char) 0.upto(limit) do |m| target_char = $hiraganas[m] target = g.add_node(target_char) if p_array[n][m] > 0.01 then g.add_edge(origin, target, :label => p_array[n][m].to_s ) end end end }.output()