rsruby 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ /*
2
+ * == Author
3
+ * Alex Gutteridge
4
+ *
5
+ * == Copyright
6
+ *Copyright (C) 2006 Alex Gutteridge
7
+ *
8
+ * The Original Code is the RPy python module.
9
+ *
10
+ * The Initial Developer of the Original Code is Walter Moreira.
11
+ * Portions created by the Initial Developer are Copyright (C) 2002
12
+ * the Initial Developer. All Rights Reserved.
13
+ *
14
+ * Contributor(s):
15
+ * Gregory R. Warnes <greg@warnes.net> (RPy Maintainer)
16
+ *
17
+ *This library is free software; you can redistribute it and/or
18
+ *modify it under the terms of the GNU Lesser General Public
19
+ *License as published by the Free Software Foundation; either
20
+ *version 2.1 of the License, or (at your option) any later version.
21
+ *
22
+ *This library is distributed in the hope that it will be useful,
23
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
+ *Lesser General Public License for more details.
26
+ *
27
+ *You should have received a copy of the GNU Lesser General Public
28
+ *License along with this library; if not, write to the Free Software
29
+ *Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30
+ */
31
+
32
+ #ifndef R_RUBY_MAIN
33
+ #define R_RUBY_MAIN
34
+
35
+ #include "ruby.h"
36
+
37
+ #include "R.h"
38
+ #include "Rdefines.h"
39
+ #include "Rinternals.h"
40
+ #include "Rdefines.h"
41
+ #include "Rdevices.h"
42
+
43
+ #include "signal.h"
44
+
45
+ #include "R_eval.h"
46
+ #include "Converters.h"
47
+
48
+ #define MAXIDSIZE 256
49
+
50
+ #define NO_CONVERSION 0
51
+ #define VECTOR_CONVERSION 1
52
+ #define BASIC_CONVERSION 2
53
+ #define CLASS_CONVERSION 3
54
+ #define PROC_CONVERSION 4
55
+
56
+ #define TOP_MODE 4
57
+
58
+ /* Missing definitions from Rinterface.h or RStartup.h */
59
+ # define CleanEd Rf_CleanEd
60
+ extern void CleanEd(void);
61
+ extern int R_CollectWarnings;
62
+ # define PrintWarnings Rf_PrintWarnings
63
+ extern void PrintWarnings(void);
64
+
65
+ void Init_rsruby();
66
+
67
+ void init_R(int argc, char *argv[0]);
68
+ void r_finalize(void);
69
+
70
+ static SEXP RecursiveRelease(SEXP obj, SEXP list);
71
+ static void Robj_dealloc(VALUE self);
72
+
73
+ VALUE shutdown(VALUE self);
74
+ VALUE get_fun(VALUE self, VALUE name);
75
+ VALUE rr_init(VALUE self);
76
+
77
+ VALUE RObj_lcall(VALUE self, VALUE args);
78
+ VALUE RObj_to_ruby(VALUE self, VALUE args);
79
+
80
+ #endif
data/lib/rsruby.rb ADDED
@@ -0,0 +1,361 @@
1
+ #== Synopsis
2
+ #
3
+ #This class provides the ability to embed a full R
4
+ #interpreter inside a running Ruby script. R methods can
5
+ #then be called from the Ruby script and data passed between the
6
+ #R interpreter and the Ruby script. The code is based on a conversion
7
+ #of the RSPerl[http://www.omegahat.org/RSPerl/] and RPy[http://rpy.sourceforge.net/]
8
+ #modules which provide similar (and more) functionality for Perl and Python
9
+ #respectively.
10
+ #
11
+ #The main RSRuby class has Singleton module mixed in. This ensures that only
12
+ #one R interpreter is running in a script at any one time and that the
13
+ #interpreter can always be found. The embedded R interpreter is started by
14
+ #calling RSRuby.instance (See the RSRuby class for details). The returned
15
+ #RSRuby object represents the R interpreter and R functions are called by
16
+ #calling methods on this object.
17
+ #
18
+ #== Usage
19
+ #
20
+ # require 'rsruby'
21
+ #
22
+ # r = RSRuby.instance
23
+ # puts r.sum(1,2,3)
24
+ #
25
+ #Converting between R and Ruby data types is handled in a similar way to
26
+ #RPy and is explained in more detail in the manual (see Converters.c for the
27
+ #gory details).
28
+ #
29
+ #The default conversion system mapping between R types and Ruby classes is
30
+ #summarised here:
31
+ #
32
+ # - R Logicals (true/false) <=> Ruby true/false
33
+ # - R Integers <=> Ruby Fixnum/Bignum
34
+ # - R Real <=> Ruby Float
35
+ # - R Complex <=> Ruby Complex
36
+ # - R String <=> Ruby String
37
+ # - R Vector <=> Ruby Array or Hash
38
+ # - R List <=> Ruby Array or Hash
39
+ # - R Array <=> Ruby Array or Hash
40
+ # - R 'other' <=> Ruby RObj
41
+ #
42
+ #While this generally works fine for simple data structures, more complicated
43
+ #structures can loose information when converted to Ruby. Clever use of the
44
+ #more complicated custom conversion modes can usually avoid this.
45
+ #
46
+ #As shown above, calling R methods can be done by calling the same method
47
+ #on the RSRuby object. Some R methods contain '.' and other characters
48
+ #forbidden in Ruby method names. To call these methods substitute '_' for '.':
49
+ #
50
+ #Sample usage (to call R method 'as.list'):
51
+ #
52
+ # require 'rsruby'
53
+ #
54
+ # r = RSRuby.instance
55
+ # puts r.as_list([1,2,3])
56
+ #
57
+ #If a Hash is the last argument to an RSRuby method call then the hash is
58
+ #interpreted as a list of named arguments to the corresponding R function.
59
+ #Argument names can be given as Strings or Symbols. Argument order is not
60
+ #maintained in this case (because order is not maintained in Hashes). If
61
+ #named arguments and order are required then there is the lcall syntax where
62
+ #arguments are given as an array of two member arrays (where each two member
63
+ #array represents a name/argument pair). lcall is a method defined in the
64
+ #RObj class which represents functions and other non-convertible R objects.
65
+ #
66
+ #Calling an R method with no arguments returns the method object itself - it
67
+ #does not call the method. This may be changed in the future as it is somewhat
68
+ #confusing. However, the returned object can then be called or 'lcall'ed as
69
+ #required. It is also possible to return R variables in this way:
70
+ #
71
+ #E.g.
72
+ #
73
+ # require 'rsruby'
74
+ #
75
+ # r = RSRuby.instance
76
+ # norm = r.rnorm
77
+ # norm_dist = norm.call(100)
78
+ #
79
+ # r.plot(norm_dist,
80
+ # {:ylab => "TestY"})
81
+ # sleep(2)
82
+ #
83
+ #
84
+ #The final way to execute R code is to just evaluate a complete R expression
85
+ #The eval_R method provides this functionality. Pass a String to the method to
86
+ #see it evaluated by R:
87
+ #
88
+ # require 'rsruby'
89
+ #
90
+ # r = RSRuby.instance
91
+ # r.eval_R("a=43")
92
+ # puts r.a
93
+ #
94
+ #As an alternative to the method syntax, R objects and functions can be
95
+ #returned using []. This is required for weird R methods such as '$' or '[[':
96
+ #
97
+ # require 'rsruby'
98
+ #
99
+ # r = RSRuby.instance
100
+ # r.eval_R("a=43")
101
+ # puts r['a']
102
+ #
103
+ #R libraries can be loaded using the library method. Just provide the library
104
+ #name as a String:
105
+ #
106
+ # require 'rsruby'
107
+ #
108
+ # r = RSRuby.instance
109
+ # r.library("library_name")
110
+ #
111
+ #== Author
112
+ #Alex Gutteridge
113
+ #
114
+ #== Copyright
115
+ #Copyright (C) 2006 Alex Gutteridge
116
+ #
117
+ #The Original Code is the RPy python module.
118
+ #
119
+ #The Initial Developer of the Original Code is Walter Moreira.
120
+ #Portions created by the Initial Developer are Copyright (C) 2002
121
+ #the Initial Developer. All Rights Reserved.
122
+ #
123
+ #Contributor(s):
124
+ #Gregory R. Warnes <greg@warnes.net> (RPy Maintainer)
125
+ #
126
+ #This library is free software; you can redistribute it and/or
127
+ #modify it under the terms of the GNU Lesser General Public
128
+ #License as published by the Free Software Foundation; either
129
+ #version 2.1 of the License, or (at your option) any later version.
130
+ #
131
+ #This library is distributed in the hope that it will be useful,
132
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
133
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
134
+ #Lesser General Public License for more details.
135
+ #
136
+ #You should have received a copy of the GNU Lesser General Public
137
+ #License along with this library; if not, write to the Free Software
138
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
139
+
140
+ require 'rsruby/robj'
141
+ require 'rsruby.so'
142
+ require 'singleton'
143
+
144
+ require 'complex'
145
+
146
+ class RException < RuntimeError
147
+ end
148
+
149
+ class RSRuby
150
+
151
+ include Singleton
152
+
153
+ #Constants for conversion modes
154
+ TOP_CONVERSION = 4
155
+ PROC_CONVERSION = 4
156
+ CLASS_CONVERSION = 3
157
+ BASIC_CONVERSION = 2
158
+ VECTOR_CONVERSION = 1
159
+ NO_CONVERSION = 0
160
+ NO_DEFAULT = -1
161
+
162
+ alias c_initialize initialize
163
+
164
+ #Create a new RSRuby interpreter instance. The Singleton design pattern
165
+ #ensures that only one instance can be running in a script. Further
166
+ #calls to RSRuby.instance will return the original instance.
167
+ def initialize()
168
+
169
+ #Initialize in C
170
+ c_initialize
171
+
172
+ @@default_mode = NO_DEFAULT
173
+
174
+ @@class_table = {}
175
+ @@proc_table = {}
176
+
177
+ #Setup R object cache
178
+ @@cache = {}
179
+ @@cache['get'] = self.get_fun('get')
180
+
181
+ #Get constants
182
+ @@cache['TRUE'] = self.__getitem__('T')
183
+ @@cache['FALSE'] = self.__getitem__('F')
184
+ @@cache['NA'] = self.eval_R('NA')
185
+ # @@cache['NAN'] = self.eval_R('as.double(NA)')
186
+ @@cache['NaN'] = self.eval_R('NaN')
187
+
188
+ #help!
189
+ @@cache['helpfun'] = self.with_mode(NO_CONVERSION, self.__getitem__('help'))
190
+
191
+ #Catch errors
192
+ self.eval_R("options(error=expression(NULL))")
193
+ #disable errors
194
+ self.options('show.error.messages' => false)
195
+
196
+ end
197
+
198
+ #Handles method name conversion and calling of R functions
199
+ #If called without args the R function/varialbe is returned rather
200
+ #than called.
201
+ def method_missing(r_id,*args)
202
+
203
+ #Translate Ruby method call to R
204
+ robj_name = RSRuby.convert_method_name(r_id.to_s)
205
+
206
+ #Retrieve it
207
+ robj = self.__getitem__(robj_name)
208
+
209
+ #TODO perhaps this is not neccessary - always call these methods
210
+ #use the [] syntax for variables etc...
211
+ if args.length > 0
212
+
213
+ #convert arguments to lcall format
214
+ lcall_args = RSRuby.convert_args_to_lcall(args)
215
+
216
+ #Return result of calling object with lcall
217
+ #formatted args
218
+ return robj.lcall(lcall_args)
219
+
220
+ end
221
+
222
+ return robj
223
+
224
+ end
225
+
226
+ #As method_missing, but only returns the R function/object
227
+ #does not call it.
228
+ def [](r_id)
229
+
230
+ #Translate Ruby method call to R
231
+ robj_name = RSRuby.convert_method_name(r_id.to_s)
232
+
233
+ #Retrieve it
234
+ robj = self.__getitem__(robj_name)
235
+
236
+ #And return it
237
+ return robj
238
+
239
+ end
240
+
241
+ #Takes an #RObj representing an R function and sets the 'wrapping'
242
+ #mode for that function. Implemented for compatibility with RPy.
243
+ def with_mode(mode,func)
244
+ func.wrap = mode
245
+ return func
246
+ end
247
+
248
+ #Converts method names from Ruby compatible style into R style.
249
+ def RSRuby.convert_method_name(name)
250
+ if name.length > 1 and name[-1].chr == '_' and name[-2].chr != '_'
251
+ name = name[0..-2]
252
+ end
253
+ name.gsub!(/__/,'<-')
254
+ name.gsub!(/_/, '.')
255
+ return name
256
+ end
257
+
258
+ #Converts an array of arguments into lcall format. If the last element
259
+ #of the array is a Hash then the contents are interpreted as named
260
+ #arguments. lcall format is an Array of two member Arrays. Each two
261
+ #member array corresponds to a name/argument pair.
262
+ def RSRuby.convert_args_to_lcall(args)
263
+
264
+ lcall_args = []
265
+
266
+ args.each_with_index do |arg,i|
267
+ unless arg.kind_of?(Hash) and i == args.length-1
268
+ lcall_args.push(['',arg])
269
+ else
270
+ arg.each do |k,v|
271
+ lcall_args.push([k.to_s,v])
272
+ end
273
+ end
274
+ end
275
+
276
+ return lcall_args
277
+
278
+ end
279
+
280
+ #Sets the default conversion mode for RSRuby. The constants defined
281
+ #in #RSRuby should be used
282
+ def RSRuby.set_default_mode(m)
283
+ if m < -1 or m > TOP_CONVERSION
284
+ raise ArgumentError, "Invalid mode requested"
285
+ end
286
+ @@default_mode = m
287
+ end
288
+ #Returns the current default conversion mode as an Integer.
289
+ def RSRuby.get_default_mode
290
+ @@default_mode
291
+ end
292
+
293
+ #TODO - input/output setting methods don't work atm
294
+ def RSRuby.set_rsruby_input(m)
295
+ @@rsruby_input = m
296
+ end
297
+ def RSRuby.get_rsruby_input
298
+ @@rsruby_input
299
+ end
300
+
301
+ def RSRuby.set_rsruby_output(m)
302
+ @@rsruby_output = m
303
+ end
304
+ def RSRuby.get_rsruby_output
305
+ @@rsruby_output
306
+ end
307
+
308
+ def RSRuby.set_rsruby_showfiles(m)
309
+ @@rsruby_showfiles = m
310
+ end
311
+ def RSRuby.get_rsruby_showfiles
312
+ @@rsruby_showfiles
313
+ end
314
+
315
+ #Returns the current class table Hash for RSRuby.
316
+ def class_table
317
+ @@class_table
318
+ end
319
+
320
+ #Sets the RSRuby class table Hash.
321
+ def class_table=(h)
322
+ @@class_table = h
323
+ end
324
+
325
+ #Returns the current proc table Hash for RSRuby.
326
+ def proc_table
327
+ @@proc_table
328
+ end
329
+
330
+ #Sets the RSRuby proc table Hash.
331
+ def proc_table=(h)
332
+ @@proc_table = h
333
+ end
334
+
335
+ #Evaluates the given string in R. Returns the result of the evaluation.
336
+ def eval_R(s)
337
+ self.eval(self.parse(:text => s))
338
+ end
339
+
340
+ #Wraps the R help function.
341
+ def help(*args)
342
+ helpobj = @@cache['helpfun'].call(args)
343
+ self.print(helpobj)
344
+ end
345
+
346
+ :private
347
+ def __getitem__(name)
348
+
349
+ #Find the identifier and cache (unless already cached)
350
+ unless @@cache.has_key?(name)
351
+ @@cache[name] = @@cache['get'].lcall([['',name]])
352
+ end
353
+
354
+ #Retrieve object from cache
355
+ robj = @@cache[name]
356
+
357
+ return robj
358
+
359
+ end
360
+
361
+ end
@@ -0,0 +1,77 @@
1
+ #== Synopsis
2
+ #
3
+ #This is an extended #ERObj class inspired by the example given in the RPy
4
+ #manual used for R data frames.
5
+ #As with ERobj, methods caught by method_missing are converted into attribute
6
+ #calls on the R dataframe it represents. The rows and columns methods give
7
+ #access to the column and row names.
8
+ #
9
+ #== Usage
10
+ #
11
+ #See examples/dataframe.rb for examples of usage.
12
+ #
13
+ #--
14
+ #== Author
15
+ #Alex Gutteridge
16
+ #
17
+ #== Copyright
18
+ #Copyright (C) 2006 Alex Gutteridge
19
+ #
20
+ # The Original Code is the RPy python module.
21
+ #
22
+ # The Initial Developer of the Original Code is Walter Moreira.
23
+ # Portions created by the Initial Developer are Copyright (C) 2002
24
+ # the Initial Developer. All Rights Reserved.
25
+ #
26
+ # Contributor(s):
27
+ # Gregory R. Warnes <greg@warnes.net> (RPy Maintainer)
28
+ #
29
+ #This library is free software; you can redistribute it and/or
30
+ #modify it under the terms of the GNU Lesser General Public
31
+ #License as published by the Free Software Foundation; either
32
+ #version 2.1 of the License, or (at your option) any later version.
33
+ #
34
+ #This library is distributed in the hope that it will be useful,
35
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
37
+ #Lesser General Public License for more details.
38
+ #
39
+ #You should have received a copy of the GNU Lesser General Public
40
+ #License along with this library; if not, write to the Free Software
41
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
42
+ #++
43
+
44
+ require 'rsruby'
45
+ require 'rsruby/erobj'
46
+
47
+ class DataFrame < ERObj
48
+
49
+ #Returns an array of the row names used in the R data frame.
50
+ def rows
51
+ return @r.attr(@robj, 'row.names')
52
+ end
53
+
54
+ #Returns an array of the column names used in the R data frame.
55
+ def columns
56
+ cols = @r.colnames(@robj)
57
+ cols = [cols] unless cols.class == 'Array'
58
+ return cols
59
+ end
60
+
61
+ def method_missing(attr)
62
+ attr = attr.to_s
63
+ mode = RSRuby.get_default_mode
64
+ RSRuby.set_default_mode(RSRuby::BASIC_CONVERSION)
65
+ column_names = @r.colnames(@robj)
66
+ if attr == column_names or column_names.include?(attr)
67
+ RSRuby.set_default_mode(mode)
68
+ return @r['$'].call(@robj,attr.to_s)
69
+ end
70
+
71
+ #? Not sure what here...
72
+ RSRuby.set_default_mode(mode)
73
+ return super(attr)
74
+
75
+ end
76
+
77
+ end