rsruby 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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