読者です 読者をやめる 読者になる 読者になる

StatModeling Memorandum

StanとRとPythonでベイズ統計モデリングします. たまに書評.

RSRubyでRをRubyから使う

RubyからRを使うRSRubyの使い方を説明します。3年ぐらい前から使っているのですがなかなか高速でいいです。

まずはインストール方法から。環境は以下の通りです。

参考ページはこちら(金子先生のホームページは役立つ情報が満載です)。 rubyとrsrubyは以下のバージョンをインストールしました。

  • ruby 2.0.0p195 (2013-05-14) [i386-mingw32]
  • rsruby (0.5.1.1)
  1. RubyInstallerをinstallします。(official)
  2. Development Kit(DevKit) もinstallします(参考)
    • フォルダ作ってその中に解凍
    • rubyのインストールディレクトリに解凍してできたファイルを上書きコピー
    • 以下のコマンドを実行
      cd C:\ruby\Ruby200
      ruby dk.rb init
      vi config.yml  # rubyのインストール先が正しく認識されなかった時に
      ruby dk.rb install
      
  3. gemでrsrubyをインストール
    gem install rubygems-update
    gem install rsruby -- --with-R-dir=C:/R/R-3.0.1 --with-opt-dir=C:/R/R-3.0.1/share/R --with-R-lib=C:/R/R-3.0.1/bin/i386 --with-R-include=C:/R/R-3.0.1/include
    
  4. 環境変数PATHに「C:\R\R-3.0.1\bin\i386;」を追加

次に使い方です。

require 'rsruby'
require 'rsruby/dataframe'

rr = RSRuby.instance
rr.class_table['data.frame'] = lambda{|x| DataFrame.new(x)}
RSRuby.set_default_mode(RSRuby::CLASS_CONVERSION)


# RubyのArrayを渡す
res = rr.pairwise_t_test([1.1, 2.2, 3.3, 4.4, 7.7, 8.8, 9.9, 11.0], [1,1,1,1,2,2,2,2], {:'p.adjust.method' => 'bonferroni'})
p res
#=> {"method"=>"t tests with pooled SD", "data.name"=>"c(1.1, 2.2, 3.3, 4.4, 7.7, 8.8, 9.9, 11) and c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L)", "p.value"=>[0.0005947649754862591], "p.adjust.method"=>"bonferroni"}
p res["p.value"].first
#=> 0.0005947649754862591


# Rubyでdata.frame (DataFrameクラス) を作る.
df1 = rr.as_data_frame(:x => {'data' => [1.1, 2.2, 3.3, 4.4, 7.7, 8.8, 9.9, 11.0], 'group' => [1,1,1,1,2,2,2,2]})
puts df1.columns.join(", ")
#=> data, group
puts df1.rows.join(", ")
#=> 1, 2, 3, 4, 5, 6, 7, 8


# [オススメしません] Rの関数を対応するRubyの関数で呼び出す.
# Rの関数名の'.'を'_'に置き換える. parameterとしてHashを取る.
df1 = rr.as_data_frame(:x => {'data' => [1.1, 2.2, 3.3, 4.4, 7.7, 8.8, 9.9, 11.0], 'group' => [1,1,1,1,2,2,2,2]})
res = rr.pairwise_t_test(df1.data, df1.group, {:'p.adjust.method'=>'bonferroni'})


# [オススメです] Rの関数をベタ書きで呼び出す.
df1 = rr.as_data_frame(:x => {'data' => [1.1, 2.2, 3.3, 4.4, 7.7, 8.8, 9.9, 11.0], 'group' => [1,1,1,1,2,2,2,2]})
rr.assign('df1', df1)
res = rr.eval_R("pairwise.t.test(df1$data, df1$group, p.adjust.method='bonferroni')")


# Rで作ったdata.frameをRubyで受け取る (Rコードの最後の返り値を受け取る)
df1 = rr.eval_R <<-R_COMMAND
   data <- c(1:4, 7:10) * 1.1
   group <- c(rep(1,4), rep(2,4))
   data.frame(data, group)
R_COMMAND

puts df1.to_s
# =>
#   data group
# 1  1.1     1
# 2  2.2     1
# 3  3.3     1
# 4  4.4     1
# 5  7.7     2
# 6  8.8     2
# 7  9.9     2
# 8 11.0     2



# randomForestを使う例
rr.eval_R <<-R_COMMAND
   library(randomForest)
   data(iris)
   iris.rf <- randomForest(formula = Species ~ ., data = iris)
   save(iris.rf, file = "model.RData")
R_COMMAND

iris_new = rr.as_data_frame(:x => {
   'Sepal.Length' => [6.1, 6.4, 8.0],
   'Sepal.Width'  => [3.3, 3.0, 2.8],
   'Petal.Length' => [1.3, 3.2, 5.5],
   'Petal.Width'  => [0.3, 1.5, 2.2]
})
rr.assign('iris.new', iris_new)

predict = rr.eval_R <<-R_COMMAND
   library(randomForest)
   load("model.RData")
   predict(iris.rf, newdata = iris.new, type = 'prob')
R_COMMAND

p predict
#=> [[0.932, 0.068, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]

ソースコードの通りです。Rのベタ書きをメインに使うのがオススメです。

eval_Rの内側だけRのsyntax highlightして欲しいのですがそのへんに転がっているエディタでは難しいですよね。かといって [オススメしません] のところにあるようにruby上に名前が変換された関数を使おうとすると、昔は関数のオプションが文字列をキーとしたHashだったのに、シンボルをキーとしたHashに変更されていたりして戸惑うことがありました。Rに詳しい人にとって可読性が落ちるというのもいただけない点です。

また、rubyのNArrayはそのまま渡せないのでto_aで配列化する必要があります。NArrayをバリバリ使っている人にはやや残念です。