2012年1月10日火曜日

Rubyでコーパス言語学(3)

ノードや属性の一覧を取得する

XMLファイルに含まれるノードや属性の一覧を取得するには次のようにする。

data = Hash.new{|h, k| h[k] = Hash.new(0)}
doc.xpath('//*').each do |node|
  node.attributes.keys.each do |attr|
    data[node.node_name][attr] += 1
  end
end
data.each do |k1, v1|
  puts k1
  v1.each do |k2, v2|
    puts "  #{k2}: #{v2}"
  end
end

このコードでは、ノードや属性のリストを保存するためにハッシュを使用している。RubyのHashは、Hash.newの後に{|h,k| h[k] = ...}や()をつけることによって初期値を...に指定することができる。xpathの*はワイルドカードで、全てのノードを意味する。attributesはノードの属性をハッシュで返す。keysはvaluesと対をなすHashのメソッドで、キー名のリスト(Array)を返す。node_nameはノード名を返す。950101.xmlに対してこのプログラムを実行した結果は次の通りである。

sentence
  S-ID: 1134
  KNP: 1134
  MOD: 1109
  MEMO: 14
chunk
  id: 10268
  link: 10268
  rel: 10268
tok
  id: 26739
  read: 26739
  base: 26739
  pos: 26739
  ctype: 26739
  cform: 26739

属性の値の一覧を取得する

ある属性の値の一覧を取得するには、次のようにする。例えば、pos属性の場合。

puts doc.xpath('//@pos').map{|attr| attr.value}.uniq.sort

950101.xmlに対する結果は以下の通りである。

判定詞
副詞
助動詞
助詞-副助詞
助詞-接続助詞
助詞-格助詞
助詞-終助詞
(以下略)

基本形から活用形のリストを取得する

例えば、判定詞の「だ」の活用形リストを取得する場合は次のようにする。

data = Hash.new(0)
words = doc.xpath('//tok[@base="だ" and @pos="判定詞"]')
words.each do |word|
  items = [word.text, word.attr('ctype'), word.attr('cform')]
  data[items] += 1
end
puts data.map{|rec| rec.join("¥t")}.sort

結果は以下の通り。

じゃ 判定詞 ダ列タ系連用ジャ形 1
だった 判定詞 ダ列タ形 37
だろう 判定詞 ダ列基本推量形 1
で 判定詞 ダ列タ系連用テ形 53
であった 判定詞 デアル列タ形 3
であって 判定詞 デアル列タ系連用テ形 1
であり 判定詞 デアル列基本連用形 10
である 判定詞 デアル列基本形 14
であろう 判定詞 デアル列基本推量形 1
でした 判定詞 デス列タ形 4
でしょう 判定詞 デス列基本推量形 2
です 判定詞 デス列基本形 15
な 判定詞 ダ列基本連体形 7
の 判定詞 ダ列特殊連体形 10

無理やり正規表現検索

検索したい表現の語の切れ目とか分からないときに、とりあえず正規表現で検索してマッチする語の組み合わせをリストアップする方法。ここでは「とは」の例を示す。もっとうまいやり方があるかも。

re = /とは/
list = []
doc.xpath('//sentence').each do |sen|
  pos1 = []
  sen.text.scan(re){|s| pos1 << [$~.begin(0), $~.end(0)]}
  next if pos1 == []
  tokens = sen.xpath('.//tok')
  pos2 = [0]
  tokens.each{|tok| pos2 << pos2[-1] += tok.text.size}
  items = pos1.map{|b1,e1|
    b2 = pos2.find_index{|n| n > b1}
    e2 = pos2.find_index{|n| n >= e1}
    tokens[b2..e2]
  }
  list += items
end
data = Hash.new(0)
list.each do |items|
  key = items.map{|item| "#{item.text}(#{item.attr('pos').split('-').last})"}
  data[key] += 1
end
puts data.map{|rec| rec.join("¥t")}

pos1とpos2はpart_of_speechとは全く関係なく、正規表現とマッチした文字列の位置(position)を記録しておくための変数。xpathで全てのsentence要素のリストを取得し、それの文のtextに順番に正規表現検索をかけていって、マッチしたらその部分のノードのリストを取得する。正規表現のマッチはscanメソッドで行い、マッチング結果を格納する$~という特別な変数を使ってマッチした部分の開始位置と終了位置を取得する。次にxpathでそのsentenceの中のtok要素を全て取得し、それらを順番に数えていって正規表現とマッチした部分と一文字でも重複するノードがあれば拾って行く。結果は以下の通り。

あと(副詞) は(副助詞) 2
えと(普通名詞) は(副助詞) 1
こと(形式名詞) は(副助詞) 12
と(格助詞) は(副助詞) 10
もともと(副詞) は(副助詞) 1

0 件のコメント:

コメントを投稿