RSRuby allows the user to embed a full R interpreter into a Ruby
script. This allows the script to call any R function and convert the
result back into Ruby. From the R website: 'R is a free software
environment for statistical computing and graphics.'. Running a
students t-test (or any other R function) is as simple as:
require 'rsruby'
r=RSRuby.instance #Create R interpreter
ttest = r.t_test([1,2,3]) #Convert [1,2,3] to R. Run t.test
function and convert result back to Ruby
puts ttest['p.value'] #Prints out p.value statistic from the
ttest object
That looks really great.
However, I have a couple of points about the conversion:
- R Logicals (true/false) <=> Ruby true/false
- R Integers <=> Ruby Fixnum/Bignum
- R Numeric <=> Ruby Float
- R String <=> Ruby String
These guys are all vectors in R - do you mean vectors of length one
are converted to the corresponding Ruby primitives? (Also,
confusingly, in R a string is called a character vector of length 1)
Yes you are quite correct. In the basic (default) conversion mode vectors of length one are converted to Ruby primitives. However, in the 'vector' conversion mode an Array of length one is returned instead (closer to R semantics). E.g.
irb(main):001:0> require 'rsruby'
=> true
irb(main):002:0> r = RSRuby.instance
=> #<RSRuby:0xb7d11220>
irb(main):003:0> r.sum(1,2,3).class
=> Fixnum
irb(main):004:0> RSRuby.set_default_mode(RSRuby::VECTOR_CONVERSION)
=> 1
irb(main):005:0> r.sum(1,2,3).class
=> Array
See the RPy manual for a fuller discussion of the conversion modes. RSRuby pretty much uses an identical scheme.
- R Vector <=> Ruby Array (homogeneous)
There isn't really a vector "class" in R, independent of the things
listed above.
Yes - this is a simplification and indeed the documentation is slightly misleading here. In basic mode R vectors/lists of length > 1 that don't have a 'names' attribute are converted to Arrays.
- R List <=> Ruby Hash
Whereas if a 'names' attribute is included then the vector/list is converted to a Hash
I think a better mapping would be to Ara's ArrayFields class. Lists
in R can be accessed by name or by position.
I tentatively agree. My goal for this release was to implement the RPy conversion routines and test suite as faithfully as possible. In RPy named lists/vectors are converted to Python Dictionaries which are (exactly?) equivalent to Ruby Hashes so I kept to that scheme.
As a Ruby programmer first and an R programmer second my general philosophy was too try and force R concepts into Ruby (hence converting lists to Hashes) rather than vice-versa. Using something like ArrayFields gets us closer to R but at the cost of moving away from 'canonical' (i.e. standard library) Ruby. Clearly this is a balancing act between getting as close to R semantics as possible without moving too far from 'normal' Ruby. For me that balance point may lie closer to Ruby than for other users.
One final point: The RSRuby conversion routines can be customised by the user using the 'proc' and 'class' conversion modes. These conversion modes are quite powerful and are designed to allow the user to implement custom routines for any R/Ruby interconversion they want. Implementing a list <-> ArrayFields converter in the current system is (moderately) trivial. The code below implements the R -> Ruby side. One would need to write a suitable to_r method for Array to do the conversion the other way - left as an exercise for the reader 
require 'rubygems'
require_gem 'arrayfields'
require 'rsruby'
test_proc = lambda{|x| #This lambda function is called on each object returned by R
r = RSRuby.instance
names = r.attr(x,'names') #It simply tests whether the 'names' attribute is present
if names.nil?
return false #returns false if names are not set
else
return true #returns true if they are
end
}
conv_proc = lambda{|x| #If the above function returns true then this conversion routine is used
r = RSRuby.instance #instead of the inbuilt RSRuby ones.
names = r.attr(x,'names') #Retrieve the names
hash = x.to_ruby #Convert the object (x) to Ruby - results in a Hash
array = #But we want an ArrayField
array.fields = names #Set the field names for the ArrayField
names.each do |field| #Set the ArrayField values according to the values in the Hash
array[field] = hash[field]
end
return array #Return the Array
}
r = RSRuby.instance #Start R
r.t_test.autoconvert(RSRuby::PROC_CONVERSION) #Set the t.test method to use proc conversion
r.proc_table[test_proc] = conv_proc #Setup the proc table. conv_proc is run if test_proc returns
#true
ttest = r.t_test([1,2,3]) #Call t.test function - returns list in R
puts ttest.class #Normally list <=> Hash, but here it's an array!
ttest.each_pair do |field,val| #But not just any array - an array with fields!
puts "#{field} - #{val}"
end
puts ttest[1..3] #That retains order!
Dr Alex Gutteridge
Post-Doctoral Researcher
Bioinformatics Center
Institute for Chemical Research
Kyoto University
Gokasho, Uji, Kyoto 611-0011
Japan
···
On 16 Oct 2006, at 22:37, hadley wickham wrote: