以下で取り上げる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に標準で用意されているもの)の継承クラスです。
以下で、これについて記します。
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に比べてそうした一定の制約があります。
ここで、rubyから統計Rを利用するためのrsrubyというライブラリを使うことを考えます。
rsrubyのインストールや解説について、私が参考にしたサイトは次のとおり。
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 という継承クラスをつくりました。
OpenStruct_rクラスの特徴は、次のとおりです。
初期値として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
のようにスペースを含むものがあります。これをそのまま要素名として使うことはできないので、スペースを ‘_’ に変換します。
スペースの他に、プラス・マイナスなどの演算記号があってもトラブルになります。そうした記号も ‘_’ に変換します。
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を用いるのがいいと感じるかどうか、好みということになるでしょうか。
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() をブロック付きで呼び出した場合、その戻り値は不定です。
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
上は、初期化のところではエラーになりませんが、値を参照するところでエラーが発生します。
〜 いに上 〜