From: Alex Gutteridge Date: 2006-10-17T10:48:21+09:00 Subject: Re: [ANN] RSRuby 0.4 On 16 Oct 2006, at 22:37, hadley wickham wrote: >> 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 => # 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