rubyにおける統計Rの利用とOpenStruct_rクラス

2015/06/10

 以下で取り上げるruby用ライブラリの本体は、 ostruct_r.rb です。rubyが動作する環境であれば、OSを問わずに使えます。

 このライブラリをインストール/アンインストールするための setup.rb, unset.rb も一緒に取得したい時は、 ostruct_r.zip をダウンロードして下さい。

 ostruct_r.rb, setup.rb がカレントディレクトリにある状態で、
 ruby setup.rb [enter] と入力すればインストールされます。


《目次》


<はじめに>

 rubyのrsrubyというライブラリを用いると、統計Rの様々な機能をrubyで利用できます。

 この場合、統計Rの処理結果は、Hashで得られます。

 Hashは便利な機構ですが、xx[“statistic”][“W”] のように記述して値を取り出す必要があります。括弧を多用します。

 これを xx.statistic.W のように記述できれば、少し簡単になります。

 そこで、OpenStruct_rというクラスをつくりました。OpenStructクラス(rubyに標準で用意されているもの)の継承クラスです。

 以下で、これについて記します。


1. OpenStructクラスについて

 ruby には、OpenStructというクラスがあります。弾力的に要素を設定できる構造体類似のクラスです。

 xxがOpenStructクラスのオブジェクトである場合、初期設定の段階で要素を定める他に、あとから自由に設けることもできます。たとえば、次のように書くことができます。

require "ostruct"
xx = OpenStruct.new
xx.name = "Tom"
xx.age = 4
xx.type = "cat"
p xx.name

 OpenStructオブジェクトを生成する時に、次のように初期値としてHashを与えることができます。

xx = OpenStruct.new({:name => "Tom", :age => 4})
p xx.name

 HashをOpenStructオブジェクトに変換すると、構造体のようにして扱えるようになります。括弧を多用するHashに比べて、簡単に記述できるようになります。

 ただし、rubyが各種オブジェクトについて予め用意する method などを要素名として使うことはできません。

 また、要素名の中にスペースあるいはプラス・マイナスなどの演算記号を含めることもできません。Hashに比べてそうした一定の制約があります。


2. 統計Rを利用するためのrsruby

 ここで、rubyから統計Rを利用するためのrsrubyというライブラリを使うことを考えます。

 rsrubyのインストールや解説について、私が参考にしたサイトは次のとおり。

[R] RSRuby: RをRubyから使う

RSRuby Reference Manual (PDF file)

 rsrubyは、統計Rの検定結果等をHashで返します。たとえば、次のような感じです。

require "rsruby"
r = RSRuby.instance
hs = r.wilcox_test([1,2,3], [4,5,6])
p hs

 上は、統計Rの wilcox.test を呼び出す例です。rsrubyでは ‘.’ を ‘_’ に変更して、wilcox_test と記述します。

 この統計検定は、二つの標本の母集団に違いがあるかどうかを検証します。標本の母集団が正規分布にあることを前提にしません(ノンパラメトリック検定)。

 結果は下のとおりです。

{"statistic"=>{"W"=>0.0},
    "parameter"=>nil,
    "p.value"=>0.1,
    "null.value"=>{"location shift"=>0.0},
    "alternative"=>"two.sided",
    "method"=>"Wilcoxon rank sum test",
    "data.name"=>"1:3 and 4:6"}

 先に述べたOpenStructクラスは、初期値をHashで与えることができます。なので、上の検定結果(Hash)を初期値としてOpenStructオブジェクトを生成できます。

 Hashの場合、hs[“statistic”][“W”] とか hs[“p.value”] などとして値を取り出しますが、OpenStructオブジェクトにしておけば、

  p xx.statistic.W
  p xx.p.value

 上のようにして値を取り出せるようになるのでは、と期待されます。

 しかし、残念ながらそうはなりません。次のような点が問題になります。

 こうした点を何とかしようと思って OpenStruct_r という継承クラスをつくりました。


3. OpenStruct_rクラスの特徴

 OpenStruct_rクラスの特徴は、次のとおりです。

○ 子Hashへの対応

 初期値としてHashを与えたとき、そのHashが入れ子になっていると、子HashもOpenStruct_rオブジェクトに変換します。

○ 要素名の頭文字を大文字に変換する場合あり

 初期値として与えられるHashのkeyが、OpenStruct_rオブジェクトの要素名になるわけですが、その要素名が既にメソッド名として用いられてしまっている場合、頭文字のアルファベットを大文字に変換します。

 統計Rでは method というのがよく出てきます。検定方法を示すキーワードです。一方、rubyではこれを要素名にすることができません。ですが、先頭を大文字にして Method にすれば問題ないようです。

 そこで、このような場合に要素名の頭文字を大文字にすることにしました。正確にいうと、頭文字の大文字/小文字を反転させますが、小文字に変換するケースは、実際にはないと思います。

○ 要素名に ‘.’ がある場合への対応

 統計Rでは p.value のように ‘.’ を含む名前がよく出てきます。

 そこで、要素名に ‘.’ が出てきた時は、子オブジェクトを設けるようにしました。p.valueの場合、まず p というオブジェクトを設けて、その下に value という子オブジェクトを設けます。こうすることで ‘.’ をそのまま使えるようになります。

 ’.’ を ‘_’ に変換することも考えましたが、しのびないのでやめました。

 xxがOpenStruct_rオブジェクトの場合、xx.p.value のようにして値を参照できます。

○ 要素名にスペースなどが含まれる場合への対応

 統計検定結果のHashには、location shift のようにスペースを含むものがあります。これをそのまま要素名として使うことはできないので、スペースを ‘_’ に変換します。

 スペースの他に、プラス・マイナスなどの演算記号があってもトラブルになります。そうした記号も ‘_’ に変換します。

○ 初期化の時にHashでなく配列を与えた場合

 rubyに標準で用意されているOpenStructクラスの場合、初期化の時に、Hashでなく配列を与えることもできます。

 [[key1, value1], [key2, value2], ……] のような配列の配列を与えると、それに対応したオブジェクトが生成されます。

 しかし、OpenStruct_rクラスでは、このような配列を初期化の時に与えると、単純にOpenStructのinitializeを呼び出すだけです。

 生成されるのはOpenStruct_rオブジェクトですが、これまで述べてきた特徴を持たず、OpenStructオブジェクトと同じ中身のものになるので注意して下さい。

 OpenStruct_rクラスの特徴は以上のとおりです。

 ここで OpenStruct_rクラスを利用するサンプルを掲げてみます。

−−−−−−−− ここから
require "rsruby"
require "ostruct_r"

r = RSRuby.instance
hs = r.wilcox_test([1,2,3], [4,5,6])
xx = OpenStruct_r.new(hs)

p xx.statistic.W
p xx.parameter
p xx.p.value
p xx.null.value.location_shift
p xx.alternative
p xx.Method
p xx.data.name
−−−−−−−− ここまで

 頭文字が大文字になるケースは、実際にはあまりないと思いますが、少し煩わしい感じがします。

 素直にHashを用いるのがいいと感じるかどうか、好みということになるでしょうか。


4. すべての要素と値の組を取り出すためのallメソッド

 OpenStruct_rクラスに、allというメソッドを追加しました。オブジェクトに記録されている中身を一覧するのに用います。

 ary = xx.all とすれば、aryは [[name1, value1], [name2, value2], ……] の形の配列になります。

 これまで掲げてきた wilcox.test の結果でいえば、次のようになります。

[["xx.statistic.W", 0.0],
 ["xx.parameter", nil],
 ["xx.p.value", 0.1],
 ["xx.null.value.location_shift", 0.0],
 ["xx.alternative", "two.sided"],
 ["xx.Method", "Wilcoxon rank sum test"],
 ["xx.data.name", "1:3 and 4:6"]]

 上の “xx. ……” のxxを zz にしたい時は、allメソッドに、引数 “zz” を渡します。  ary = xx.all("zz") とすれば、xxでなくzzになります。引数を省略するとxxになります。

 それから、all() は、ブロック付きで呼び出すこともできます。例えば、次のようにします。

xx.all do |name, val|
  printf("%s\t", name)
  p val
end

 all() をブロック付きで呼び出した場合、その戻り値は不定です。


5. OpenStruct_rオブジェクトの制約

 OpenStruct_rオブジェクトに、いきなり子要素の付いた要素を持ち出すとエラーが発生します。たとえば、次のような場合です。

xx = OpenStruct_r.new
xx.p.value = 0.05

 上と同じことをするには、下のように初期値をHashで与えることになるでしょうか。

xx = OpenStruct_r.new({:p=>{:value=>0.05}})

 もう一つの制約は、要素名に数字を用いるのは避けた方がいいということです。たとえば、次のようなケースです。

xx = OpenStruct_r.new({'3'=>{'14'=>0.0}})
p xx.3.14

 上は、初期化のところではエラーになりませんが、値を参照するところでエラーが発生します。

〜 いに上 〜


「プログラミング雑記帳」に戻る

トップページへ