rserve-client 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/History.txt CHANGED
@@ -1,3 +1,20 @@
1
+ === 0.1.9 / 2010-06-04
2
+
3
+ * Documentation:
4
+ * Added commentaries on examples/regression.txt
5
+ * Examples.txt on main directory renamed to Introduction.txt
6
+ * Added examples extracted from RinRuby documentation, adapted to rserve-client. Modified Connection#assign to allow ruby objects
7
+ * Modified lowless example, omiting use of Rserve::REXP::Wrapper
8
+ * Bug fixes:
9
+ * WithNames#put changes names, not values.
10
+ * Negative integer conversion works on i686 and x86_64
11
+ * New features
12
+ * Added WithNames[]= method for key assign.
13
+ * Correct handling of data.frames with to_ruby : row.names returned as a array of (1..x) value if [NA,-x] returned from Rserve
14
+ * REXP#to_ruby returns object extended with WithAttributes and WithNames when applicable
15
+ * REXP could process nested arrays properly
16
+
17
+
1
18
  === 0.1.8 / 2010-06-01
2
19
 
3
20
  * Bug fix: attributes for generic vectors (data.frames) stored correctly. A Double vector without attribute dim raises an error on #to_ruby
@@ -1,5 +1,4 @@
1
- = Examples for Rserve
2
-
1
+ = Introduction to Ruby Rserve Client
3
2
  <em>Based on original Java version [http://www.rforge.net/Rserve/example.html]</em>
4
3
 
5
4
  Rserve itself is provided as a regular R package and can be installed as such. The actual use is not performed by the library command, but by starting the Rserve executable (Windows) or typing R CMD Rserve on the command line (all others). By default Rserve runs in local mode with no enforced authentication. Once the Rserve is running any applications can use its services.
@@ -16,15 +15,15 @@ The code has the same effect as typing R.version.string in R. In the first line
16
15
 
17
16
  The following code fragment illustrates the use of slightly more complex native Ruby types:
18
17
 
19
- d = c.eval("rnorm(100)").as_doubles
18
+ d = c.eval("rnorm(100)").as_floats
20
19
 
21
20
 
22
- The single line in Ruby provides an array of 100 doubles representing random numbers from the standard normal distribution. The numeric vector in R is automatically converted into an array of floats. In cases where no native Ruby type exists, Rserve Ruby client defines its own classes such as Rlist or Logical (Ruby's boolean type has no support for NA missing values, therefore it cannot be used to directly represent logical type in R). This approach makes the use of Rserve very easy. As a first more practical example we want to calculate a Lowess smoother through a given set of points. The Ruby application lets the user specify the data allowing interactive changes of the points, displays a regular scatter plot and needs coordinates of the smoother to be obtained from R. One way of obtaining such a result would be to construct a long string command of the form lowes(c(0.2,0.4,...), c(2.5,4.8,...)) and using the eval method to obtain the result. This is somewhat clumsy, because the points usually already exist in an array in the Ruby application and the command string must be constructed from these. An alternative involves constructing objects in R directly. The following code shows the full Lowess example:
21
+ The single line in Ruby provides an array of 100 ruby floats (double precision, on C) representing random numbers from the standard normal distribution. The numeric vector in R is converted into an array of floats. In cases where no native Ruby type exists, Rserve Ruby client defines its own classes such as Rlist or Logical (Ruby's boolean type has no support for NA missing values, therefore it cannot be used to directly represent logical type in R). This approach makes the use of Rserve very easy. As a first more practical example we want to calculate a Lowess smoother through a given set of points. The Ruby application lets the user specify the data allowing interactive changes of the points, displays a regular scatter plot and needs coordinates of the smoother to be obtained from R. One way of obtaining such a result would be to construct a long string command of the form lowes(c(0.2,0.4,...), c(2.5,4.8,...)) and using the eval method to obtain the result. This is somewhat clumsy, because the points usually already exist in an array in the Ruby application and the command string must be constructed from these. An alternative involves constructing objects in R directly. The following code shows the full Lowess example:
23
22
 
24
23
  require 'rserve'
25
24
 
26
- data_x=Rserve::REXP::Wrapper.wrap(10.times.map{|i| rand(i)})
27
- data_y=Rserve::REXP::Wrapper.wrap(10.times.map{|i| rand(i)})
25
+ data_x=10.times.map{|i| rand(i)}
26
+ data_y=10.times.map{|i| rand(i)}
28
27
  c = Rserve::Connection.new();
29
28
  c.assign("x", data_x);
30
29
  c.assign("y", data_y);
data/Manifest.txt CHANGED
@@ -1,10 +1,14 @@
1
- Examples.txt
1
+ .autotest
2
2
  History.txt
3
+ Introduction.txt
3
4
  Manifest.txt
4
5
  README.txt
5
6
  Rakefile
7
+ data/gettysburg.txt
8
+ examples/gettysburg.rb
6
9
  examples/hello_world.rb
7
10
  examples/lowless.rb
11
+ examples/regression.rb
8
12
  lib/rserve.rb
9
13
  lib/rserve/connection.rb
10
14
  lib/rserve/engine.rb
@@ -0,0 +1,25 @@
1
+ THE GETTYSBURG ADDRESS:
2
+
3
+ Four score and seven years ago our fathers brought forth on this
4
+ continent a new nation, conceived in liberty and dedicated to the
5
+ proposition that all men are created equal. Now we are engaged in
6
+ a great civil war, testing whether that nation or any nation so
7
+ conceived and so dedicated can long endure. We are met on a great
8
+ battlefield of that war. We have come to dedicate a portion of
9
+ that field as a final resting-place for those who here gave their
10
+ lives that that nation might live. It is altogether fitting and
11
+ proper that we should do this. But in a larger sense, we cannot
12
+ dedicate, we cannot consecrate, we cannot hallow this ground.
13
+ The brave men, living and dead who struggled here have consecrated
14
+ it far above our poor power to add or detract. The world will
15
+ little note nor long remember what we say here, but it can never
16
+ forget what they did here. It is for us the living rather to be
17
+ dedicated here to the unfinished work which they who fought here
18
+ have thus far so nobly advanced. It is rather for us to be here
19
+ dedicated to the great task remaining before us--that from these
20
+ honored dead we take increased devotion to that cause for which
21
+ they gave the last full measure of devotion--that we here highly
22
+ resolve that these dead shall not have died in vain, that this
23
+ nation under God shall have a new birth of freedom, and that
24
+ government of the people, by the people, for the people shall
25
+ not perish from the earth.
@@ -0,0 +1,24 @@
1
+ # The code and output below demonstrates some of the Ruby Rserve client, using a example from RinRuby. Ruby code counts the number of occurences of each word in Lincoln's Gettysburg Address and filters out those occurring less than three times or shorter than four letters. R code -- through the Rserve library -- produces a bar plot of the most frequent words and computes the correlation between the length of a word and the usage frequency. Finally, the computed correlation is printed by Ruby
2
+
3
+ require "rserve"
4
+ r=Rserve::Connection.new
5
+
6
+ tally = Hash.new(0)
7
+ File.open(File.dirname(__FILE__)+'/../data/gettysburg.txt').each_line do |line|
8
+ line.downcase.split(/\W+/).each { |w| tally[w] += 1 }
9
+ end
10
+ total = tally.values.inject { |sum,count| sum + count }
11
+ tally.delete_if { |key,count| count < 3 || key.length < 4 }
12
+
13
+ r.assign("keys",tally.keys)
14
+ r.assign("counts", tally.values)
15
+
16
+ r.void_eval <<-EOF
17
+ names(counts) <- keys
18
+ barplot(rev(sort(counts)),main="Frequency of Non-Trivial Words",las=2)
19
+ mtext("Among the #{total} words in the Gettysburg Address",3,0.45)
20
+ rho <- round(cor(nchar(keys),counts),4)
21
+ EOF
22
+ puts "Press enter when finished"
23
+ STDIN.gets
24
+ puts "The correlation between word length and frequency is #{r.eval("rho").as_float}."
data/examples/lowless.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rserve'
2
2
 
3
- data_x=Rserve::REXP::Wrapper.wrap(10.times.map{|i| rand(i)})
4
- data_y=Rserve::REXP::Wrapper.wrap(10.times.map{|i| rand(i)})
3
+ data_x=20.times.map{|i| rand(i)}
4
+ data_y=20.times.map{|i| rand(i)}
5
5
  c = Rserve::Connection.new();
6
6
  c.assign("x", data_x);
7
7
  c.assign("y", data_y);
@@ -0,0 +1,44 @@
1
+ # As another example of Ruby Rserve client, extracted from RinRuby documentation, consider the usage of Ruby Rserve client for simple linear regression below. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more eloborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
2
+
3
+ require 'rserve'
4
+ r=Rserve::Connection.new
5
+ n = 10
6
+ beta_0 = 1
7
+ beta_1 = 0.25
8
+ alpha = 0.05
9
+ seed = 23423
10
+
11
+ # You could assign a ruby object to a R variable automaticly. You could see
12
+ # the rules of translation on Rserve::REXP::Wrapper module
13
+ r.assign "x", (1..n).entries
14
+
15
+ # Rserve::Connection.void_eval allows you to send complex R commands
16
+ # without wasting time retrieving the final result
17
+
18
+ r.void_eval <<EOF
19
+ set.seed(#{seed})
20
+ y <- #{beta_0} + #{beta_1}*x + rnorm(#{n})
21
+ fit <- lm( y ~ x )
22
+ est <- round(coef(fit),3)
23
+ pvalue <- summary(fit)$coefficients[2,4]
24
+ EOF
25
+
26
+ # Rserve::Connecttion.eval retrieve a Rserve::REXP object, which could
27
+ # be translated to an appropiate Ruby object using Rserve::REXP.to_ruby method
28
+ #
29
+ est=r.eval("est").to_ruby
30
+
31
+ # fit$coef is a named doubles vector, so the respective Ruby object is an
32
+ # array of rational extended with module WithNames. That allows us
33
+ # to retrieve every element of the vector by its name
34
+
35
+ puts "E(y|x) ~= #{est['(Intercept)'].to_f} + #{est['x'].to_f} * x"
36
+
37
+ # fit$pvalue is a vector of size 1. So, the method #to_ruby
38
+ # retrieves a rational or a Fixnum, according to original representation
39
+
40
+ if r.eval("pvalue").to_ruby < alpha
41
+ puts "Reject the null hypothesis and conclude that x and y are related."
42
+ else
43
+ puts "There is insufficient evidence to conclude that x and y are related."
44
+ end
data/lib/rserve.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'socket'
2
2
 
3
3
  module Rserve
4
- VERSION = '0.1.8'
4
+ VERSION = '0.1.9'
5
5
  end
6
6
 
7
7
 
@@ -167,7 +167,7 @@ module Rserve
167
167
  when REXP
168
168
  assign_rexp(sym,ct)
169
169
  else
170
- raise "Should be String or REXP"
170
+ assign_rexp(sym, Rserve::REXP::Wrapper.wrap(ct))
171
171
  end
172
172
  end
173
173
  def assign_string(sym,ct)
@@ -110,7 +110,9 @@ module Rserve
110
110
  ERR_session_busy=>"session still busy",
111
111
  ERR_detach_failed=>"unable to detach seesion"
112
112
  } # error descriptions
113
-
113
+ # I have to use to support different archs
114
+ MAX_LONG_SIGNED=2**31
115
+ MAX_LONG_UNSIGNED=2**32
114
116
 
115
117
  # writes bit-wise int to a byte buffer at specified position in Intel-endian form
116
118
  # Internal: byte buffer will be the result of unpack("CCCC") an integer.
@@ -160,8 +162,9 @@ module Rserve
160
162
  # make sure that the buffer is big enough
161
163
 
162
164
  def get_int(buf, o)
163
- #return buf.slice(o,4).pack("C*").unpack("i")[0]
164
- return ((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24));
165
+ #return buf.slice(o,4).pack("C*").unpack("l")[0]
166
+ v=((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24))
167
+ v >= MAX_LONG_SIGNED ? v-MAX_LONG_UNSIGNED : v
165
168
  end
166
169
 
167
170
  # converts bit-wise stored length from a header. "long" format is supported up to 32-bit
data/lib/rserve/rexp.rb CHANGED
@@ -11,9 +11,6 @@ module Rserve
11
11
  def initialize(attr=nil)
12
12
  # Sorry for this, but I think is necessary to maintain sanity of attributes
13
13
  raise ArgumentError, "Attribute should be a REXP::List, #{attr.class} provided" unless attr.nil? or attr.is_a? REXP::List
14
-
15
-
16
-
17
14
  @attr=attr
18
15
  end
19
16
  # specifies how many items of a vector or list will be displayed in {@link #toDebugString}
@@ -22,111 +19,164 @@ module Rserve
22
19
  # check whether the <code>REXP</code> object is a character vector (string)
23
20
  # @return <code>true</code> if the receiver is a character vector, <code>false</code> otherwise
24
21
 
25
- def string?;return false;end
22
+ def string?
23
+ false
24
+ end
26
25
 
27
26
  # # check whether the <code>REXP</code> object is a numeric vector
28
27
  # @return <code>true</code> if the receiver is a numeric vector, <code>false</code> otherwise
29
28
 
30
- def numeric?;false;end
29
+ def numeric?
30
+ false
31
+ end
31
32
  # check whether the <code>REXP</code> object is an integer vector
32
33
  # @return <code>true</code> if the receiver is an integer vector, <code>false</code> otherwise
33
- def integer?;false;end
34
+ def integer?
35
+ false
36
+ end
34
37
  # check whether the <code>REXP</code> object is NULL
35
38
  # @return <code>true</code> if the receiver is NULL, <code>false</code> otherwise
36
- def null?;false;end
39
+ def null?
40
+ false
41
+ end
37
42
  # check whether the <code>REXP</code> object is a factor
38
43
  # @return <code>true</code> if the receiver is a factor, <code>false</code> otherwise
39
- def factor?;false;end
44
+ def factor?
45
+ false
46
+ end
40
47
  # check whether the <code>REXP</code> object is a list (either generic vector or a pairlist - i.e. {@link #asList()} will succeed)
41
48
  # @return <code>true</code> if the receiver is a generic vector or a pair-list, <code>false</code> otherwise
42
- def list?;false;end
49
+ def list?
50
+ false
51
+ end
43
52
  # check whether the <code>REXP</code> object is a pair-list
44
53
  # @return <code>true</code> if the receiver is a pair-list, <code>false</code> otherwise
45
- def pair_list?;false;end
54
+ def pair_list?
55
+ false
56
+ end
46
57
  # check whether the <code>REXP</code> object is a logical vector
47
58
  # @return <code>true</code> if the receiver is a logical vector, <code>false</code> otherwise */
48
- def logical?;false;end
59
+ def logical?
60
+ false
61
+ end
49
62
  # check whether the <code>REXP</code> object is an environment
50
63
  # @return <code>true</code> if the receiver is an environment, <code>false</code> otherwise
51
- def environment?;false;end
64
+ def environment?
65
+ false
66
+ end
52
67
  # check whether the <code>REXP</code> object is a language object
53
68
  # @return <code>true</code> if the receiver is a language object, <code>false</code> otherwise
54
- def language?;false;end
69
+ def language?
70
+ false
71
+ end
55
72
  # check whether the <code>REXP</code> object is an expression vector
56
73
  # @return <code>true</code> if the receiver is an expression vector, <code>false</code> otherwise
57
- def expression?;false;end
74
+ def expression?
75
+ false
76
+ end
58
77
  # check whether the <code>REXP</code> object is a symbol
59
78
  # @return <code>true</code> if the receiver is a symbol, <code>false</code> otherwise
60
- def symbol?;false;end
79
+ def symbol?
80
+ false
81
+ end
61
82
  # check whether the <code>REXP</code> object is a vector
62
83
  # @return <code>true</code> if the receiver is a vector, <code>false</code> otherwise
63
- def vector?;false;end
84
+ def vector?
85
+ false
86
+ end
64
87
  # check whether the <code>REXP</code> object is a raw vector
65
88
  # @return <code>true</code> if the receiver is a raw vector, <code>false</code> otherwise
66
- def raw?;false;end
89
+ def raw?
90
+ false
91
+ end
67
92
  # check whether the <code>REXP</code> object is a complex vector
68
93
  # @return <code>true</code> if the receiver is a complex vector, <code>false</code> otherwise
69
- def complex?;false;end
94
+ def complex?
95
+ false
96
+ end
70
97
  # check whether the <code>REXP</code> object is a recursive obejct
71
98
  # @return <code>true</code> if the receiver is a recursive object, <code>false</code> otherwise
72
- def recursive?;false;end
99
+ def recursive?
100
+ false
101
+ end
73
102
  # check whether the <code>REXP</code> object is a reference to an R object
74
103
  # @return <code>true</code> if the receiver is a reference, <code>false</code> otherwise
75
- def reference?;false;end
104
+ def reference?
105
+ false
106
+ end
76
107
 
77
108
  # :section: basic accessor methods
78
109
  # returns the contents as an array of Strings (if supported by the represented object)
79
- def as_strings;raise MismatchException, "String";end
110
+ def as_strings
111
+ raise MismatchException, "String"
112
+ end
80
113
  # returns the contents as an array of integers (if supported by the represented object)
81
114
 
82
- def as_integers; raise MismatchException, "int";end;
115
+ def as_integers
116
+ raise MismatchException, "int"
117
+ end
118
+
83
119
  # returns the contents as an array of doubles (if supported by the represented object)
84
- def as_doubles; raise MismatchException,"double";end;
85
-
120
+ def as_doubles
121
+ raise MismatchException,"double"
122
+ end
123
+
86
124
  # On Ruby, Float are stored in double precision
87
125
  def as_floats
88
126
  as_doubles
89
127
  end
90
128
 
91
129
  # returns the contents as an array of bytes (if supported by the represented object)
92
- def as_bytes; raise MismatchException , "byte";end;
130
+ def as_bytes
131
+ raise MismatchException , "byte"
132
+ end
93
133
  # returns the contents as a (named) list (if supported by the represented object)
94
- def as_list; raise MismatchException,"list";end;
134
+ def as_list
135
+ raise MismatchException,"list"
136
+ end
95
137
  # returns the contents as a factor (if supported by the represented object)
96
- def as_factor; raise MismatchException,"factor";end;
138
+ def as_factor
139
+ raise MismatchException,"factor"
140
+ end
97
141
 
98
142
  # returns the length of a vector object. Note that we use R semantics here, i.e. a matrix will have a length of <i>m * n</i> since it is represented by a single vector (see {@link #dim} for retrieving matrix and multidimentional-array dimensions).
99
143
  # * @return length (number of elements) in a vector object
100
144
  # * @throws MismatchException if this is not a vector object
101
- def length()
102
- raise MismatchException, "vector";
145
+ def length
146
+ raise MismatchException, "vector"
103
147
  end
104
148
 
105
149
  # returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
106
150
  # * @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
107
151
  # * @throws MismatchException if this is not a vector object
108
152
  def na?
109
- raise MismatchException, "vector"
153
+ raise MismatchException, "vector"
110
154
  end
111
155
 
112
156
  # :section: convenience accessor methods
113
157
  # convenience method corresponding to <code>as_integer()[0]</code>
114
158
  # @return first entry returned by {@link #as_integer}
115
159
  def as_integer
116
- as_integers[0]
160
+ as_integers[0]
161
+ end
162
+ def to_i
163
+ as_integers[0]
117
164
  end
118
165
  # convenience method corresponding to <code>asDoubles()[0]</code>
119
166
  # @return first entry returned by {@link #asDoubles}
120
167
  def as_double
121
- as_doubles[0]
168
+ as_doubles[0]
122
169
  end
123
170
  def as_float
124
171
  as_double
125
172
  end
173
+ def to_f
174
+ as_double
175
+ end
126
176
  # convenience method corresponding to <code>asStrings()[0]</code>
127
177
  # @return first entry returned by {@link #asStrings}
128
178
  def as_string
129
- as_strings[0]
179
+ as_strings[0]
130
180
  end
131
181
  # // methods common to all REXPs
132
182
 
@@ -135,15 +185,14 @@ module Rserve
135
185
  # * @return attribute value or <code>null</code> if the attribute does not exist
136
186
 
137
187
  def get_attribute(name)
138
- nil if @attr.nil? or !@attr.list?
139
- @attr.as_list[name]
188
+ has_attribute?(name) ? @attr.as_list[name] : nil
140
189
  end
141
190
 
142
191
  # checks whether this obejct has a given attribute
143
192
  # * @param name attribute name
144
193
  # * @return <code>true</code> if the attribute exists, <code>false</code> otherwise
145
194
  def has_attribute? (name)
146
- return (!@attr.nil? and @attr.list? and !@attr.as_list[name].nil?);
195
+ !@attr.nil? and @attr.list? and !@attr.as_list[name].nil?
147
196
  end
148
197
 
149
198
 
@@ -165,14 +214,14 @@ module Rserve
165
214
  # @return <code>true</code> if this object is of the class <code>klass</code>, <code>false</code> otherwise
166
215
  def inherits?(klass)
167
216
  return false if (!has_attribute? "class")
168
- begin
169
- c = get_attribute("class").as_strings;
170
- if (!c.nil?)
171
- return c.any? {|v| v.equals klass}
172
- end
173
- rescue MismatchException
174
- end
175
- return false;
217
+ begin
218
+ c = get_attribute("class").as_strings;
219
+ if (!c.nil?)
220
+ return c.any? {|v| v.equals klass}
221
+ end
222
+ rescue MismatchException
223
+ end
224
+ false
176
225
  end
177
226
 
178
227
 
@@ -180,7 +229,7 @@ module Rserve
180
229
  # returns representation that it useful for debugging (e.g. it includes attributes and may include vector values -- see {@link #maxDebugItems})
181
230
  # @return extended description of the obejct -- it may include vector values
182
231
  def to_debug_string
183
- (!@attr.nil?) ? (("<"+@attr.to_debug_string()+">")+to_s()) : to_s
232
+ (!@attr.nil?) ? (("<"+@attr.to_debug_string()+">")+to_s()) : to_s
184
233
  end
185
234
 
186
235
 
@@ -196,16 +245,54 @@ module Rserve
196
245
  raise MismatchException, "matrix (dim attribute missing)" if dim.nil?
197
246
  ds = dim.as_integers
198
247
  raise MismatchException, "matrix (wrong dimensionality)" if (ds.length!=2)
199
- m,n = ds[0], ds[1]
248
+ as_nested_array
249
+
250
+ #m,n = ds[0], ds[1]
200
251
  # R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
201
252
  # we need to copy everything, since we create 2d array from 1d array
202
- r=m.times.map {|i| n.times.map {|j| ct[j*n+i]}}
253
+ #r=m.times.map {|i| n.times.map {|j| ct[j*n+i]}}
203
254
  end
204
255
  # Returns a standard library's matrix
205
256
  def as_matrix
206
257
  require 'matrix'
207
258
  Matrix.rows(as_double_matrix)
208
259
  end
260
+ # Returns the content of the REXP as a serie of nested arrays of X dimensions
261
+ def as_nested_array
262
+ ct=as_doubles
263
+ dim = get_attribute "dim"
264
+ raise MismatchException, "array (dim attribute missing" if dim.nil?
265
+ ds = dim.as_integers.reverse
266
+
267
+ split_array(ct,ds)
268
+ end
269
+
270
+ def split_array(ar, dims)
271
+ # puts "#{ar} - #{dims}"
272
+ if dims.size==1
273
+ raise "Improper size ar:#{ar} , dims=#{dims[0]}" if ar.size!=dims[0]
274
+ return ar
275
+ elsif dims.size==2
276
+ # should rearrange values as R do
277
+ out=[]
278
+ ar.each_with_index {|v,i|
279
+ r=(i/dims[1]).to_i;
280
+ c=i%dims[1];
281
+ # p "#{r} : #{c}";
282
+ out[c*dims[0]+r]=v
283
+ }
284
+ raise "out size should equal to ar size" if ar.size!=out.size
285
+ ar=out
286
+ end
287
+ dims_c=dims.dup
288
+ current_dim=dims_c.shift
289
+ current_size=ar.size/current_dim
290
+ #puts "dims: #{dims_c} cs:#{current_size}, cd:#{current_dim}"
291
+ parts=current_dim.times.map do |i|
292
+ split_array(ar[i*current_size, current_size], dims_c)
293
+ end
294
+ parts
295
+ end
209
296
 
210
297
  # :section: tools
211
298
 
@@ -213,25 +300,54 @@ module Rserve
213
300
  # * @param l a (named) list of vectors ({@link REXPVector} subclasses), each element corresponds to a column and all elements must have the same length
214
301
  # * @return a data frame object
215
302
  # * @throws MismatchException if the list is empty or any of the elements is not a vector
216
- def create_data_frame(l)
217
- raise(MismatchException, "data frame (must have dim>0)") if l.nil? or l.size<1
218
- raise MismatchException, "data frame (contents must be vectors)" if (!(l[0].is_a? REXP::Vector))
219
- fe = l[0]
220
- return REXP::GenericVector.new(l,
221
- REXP::List.new(
222
- RList.new(
223
- [
224
- REXP::String.new("data.frame"),
225
- REXP::String.new(l.keys()),
226
- REXP::Integer.new([REXP::Integer.NA, -fe.length()])
227
- ],
228
- ["class", "names", "row.names" ])
229
- )
230
- )
303
+ def self.create_data_frame(l)
304
+ raise(MismatchException, "data frame (must have dim>0)") if l.nil? or l.size<1
305
+ raise MismatchException, "data frame (contents must be vectors)" if (!(l[0].is_a? REXP::Vector))
306
+ fe = l[0]
307
+ return REXP::GenericVector.new(l,
308
+ REXP::List.new(
309
+ Rlist.new(
310
+ [
311
+ REXP::String.new("data.frame"),
312
+ REXP::String.new(l.keys()),
313
+ REXP::Integer.new([REXP::Integer::NA, -fe.length()])
314
+ ],
315
+ ["class", "names", "row.names" ])
316
+ )
317
+ )
231
318
  end
232
319
  # Retrieves the best Ruby representation of data
320
+ #
321
+ # If R object has attributes, the Ruby object is extended with Rserve::WithAttributes.
322
+ # If R object is names, the Ruby object is extended with Rserve::WithNames and
323
+ # their elements can be accessed with [] using numbers and literals.
324
+ #
233
325
  def to_ruby
234
- raise "You should implement this!"
326
+ #pp self
327
+ v=to_ruby_internal
328
+ #p v
329
+ if !v.nil? and !v.is_a? Fixnum and !v.is_a? TrueClass and !v.is_a? FalseClass
330
+ v.extend Rserve::WithAttributes
331
+ v.attributes=attr.to_ruby unless attr.nil?
332
+ if !v.attributes.nil? and v.attributes.has_name? 'names'
333
+ v.attributes['names']=[v.attributes['names']] unless v.attributes['names'].is_a? Array or v.attributes['names'].nil?
334
+
335
+ v.extend Rserve::WithNames
336
+
337
+ v.names=v.attributes['names']
338
+ end
339
+ end
340
+
341
+ # Hack: change attribute row.names according to spec
342
+ if !attr.nil? and attr.as_list.has_name? 'class' and attr.as_list['class'].as_string=='data.frame' and attr.as_list['row.names'].as_integers[0]==REXP::Integer::NA
343
+ v.attributes['row.names']=(1..(-attr.as_list['row.names'].as_integers[1])).to_a
344
+ end
345
+
346
+
347
+ v
348
+ end
349
+ def to_ruby_internal
350
+ raise "You should implement to_ruby_internal for #{self.class}"
235
351
  end
236
352
  end
237
353
  end
@@ -49,9 +49,13 @@ module Rserve
49
49
  t=super
50
50
  t << "{" << @payload.map(&:to_s).join(",") << "}"
51
51
  end
52
- def to_ruby
53
- if !attr.nil? and !attr.as_list['dim'].nil? and attr.as_list['dim'].to_ruby.size==2
54
- as_matrix
52
+ def to_ruby_internal
53
+ if dim
54
+ if dim.size==2
55
+ as_matrix
56
+ else
57
+ as_nested_array
58
+ end
55
59
  else
56
60
  super
57
61
  end
@@ -32,9 +32,6 @@ module Rserve
32
32
  def parent(resolve=true)
33
33
  @eng.get_parent_environment(self,resolve)
34
34
  end
35
- def to_ruby
36
- raise "No idea how to transform #{self.class}"
37
- end
38
35
  end
39
36
  end
40
37
  end
@@ -18,6 +18,9 @@ module Rserve
18
18
  def to_s
19
19
  super+"[#{levels.length}]"
20
20
  end
21
+ def to_ruby_internal
22
+ as_strings
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -23,7 +23,7 @@ module Rserve
23
23
  def as_list
24
24
  @payload
25
25
  end
26
- def to_ruby
26
+ def to_ruby_internal
27
27
  @payload.to_ruby
28
28
  end
29
29
  end
@@ -44,6 +44,18 @@ module Rserve
44
44
  t=super
45
45
  t << "{" << @payload.map(&:to_s).join(",") << "}"
46
46
  end
47
+ def to_ruby_internal
48
+ if dim
49
+ if dim.size==2
50
+ as_matrix
51
+ else
52
+ as_nested_array
53
+ end
54
+ else
55
+ super
56
+ end
57
+ end
58
+
47
59
  end
48
60
  end
49
61
  end
@@ -33,7 +33,7 @@ module Rserve
33
33
  end
34
34
  t+inner
35
35
  end
36
- def to_ruby
36
+ def to_ruby_internal
37
37
  as_list.to_ruby
38
38
  end
39
39
  end
@@ -60,7 +60,7 @@ module Rserve
60
60
  @payload.map {|v| v==FALSE}
61
61
  end
62
62
 
63
- def to_ruby
63
+ def to_ruby_internal
64
64
  if @payload.nil? or @payload.size==0
65
65
  nil
66
66
  elsif @payload.size==1
@@ -9,6 +9,9 @@ module Rserve
9
9
  def to_s
10
10
  super()+"[#{@type}]"
11
11
  end
12
+ def to_ruby_internal
13
+ @type.to_s
14
+ end
12
15
  end
13
16
  end
14
17
  end
@@ -9,10 +9,10 @@ module Rserve
9
9
 
10
10
  # returns the length of the vector (i.e. the number of elements)
11
11
  # @return length of the vector
12
- def length;
13
- end;
14
- def vector?;
15
- true;
12
+ def length
13
+ end
14
+ def vector?
15
+ true
16
16
  end
17
17
  # returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
18
18
  # @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values */
@@ -24,7 +24,7 @@ module Rserve
24
24
  def to_a
25
25
  @payload.map {|v| na?(v) ? nil : v }
26
26
  end
27
- def to_ruby
27
+ def to_ruby_internal
28
28
  if @payload.nil? or @payload.size==0
29
29
  nil
30
30
  elsif @payload.size==1
@@ -49,7 +49,7 @@ module Rserve
49
49
  @ids
50
50
  end
51
51
  def as_strings
52
- @ids.map {|v| @levels[v-index_base]}
52
+ @ids.map {|v| v==REXP::Integer::NA ? nil : @levels[v-index_base]}
53
53
  end
54
54
  def index_at(i)
55
55
  @ids[i]
@@ -1,5 +1,8 @@
1
1
  module Rserve
2
2
  module WithAttributes
3
3
  attr_accessor :attributes
4
+ def has_attribute?(v)
5
+ !@attributes.nil? and !attributes[v].nil?
6
+ end
4
7
  end
5
8
  end
@@ -3,7 +3,7 @@ module Rserve
3
3
  module WithNames
4
4
  attr_reader :names
5
5
  def names=(v)
6
- raise ArgumentError, "#size should be equal to object size" if !v.nil? and v.size!=self.size
6
+ raise ArgumentError, "#{self}: names size #{v.size} should be equal to object size #{self.size}" if !v.nil? and v.size!=self.size
7
7
  raise ArgumentError, "element must be String or nil" unless v.nil? or v.all? {|v1| v1.nil? or v1.is_a? String}
8
8
  @names=v
9
9
  end
@@ -13,8 +13,34 @@ module Rserve
13
13
  @names.push(name)
14
14
  super(v)
15
15
  end
16
+ def pretty_print(q)
17
+ q.group(1,'[|WN|',']') {
18
+ q.seplist(self,nil,:each_with_index) {|v,i|
19
+ if (@names.nil? or @names[i].nil?)
20
+ q.pp v
21
+ else
22
+ q.group {
23
+ q.pp @names[i]
24
+ q.text '='
25
+ q.group(1) { q.pp v }
26
+ }
27
+ end
28
+ }
29
+ }
30
+ end
31
+ def to_s
32
+ out=[]
33
+ self.each_with_index { |v,i|
34
+ if !@names.nil? and !@names[i].nil?
35
+ out.push("#{@names[i]}=#{v}")
36
+ else
37
+ out.push("#{v}")
38
+ end
39
+ }
40
+ "[#{out.join(", ")}]"
41
+ end
16
42
  def inspect
17
- "#<#{self.class}:#{self.object_id} #{super} names:#{@names.inspect}>"
43
+ "#<#{self.class}:#{self.object_id} #{to_s}>"
18
44
  end
19
45
  def clear
20
46
  @names=nil
@@ -53,6 +79,9 @@ module Rserve
53
79
  end
54
80
  sliced
55
81
  end
82
+ def has_name?(v)
83
+ named? and @names.include? v
84
+ end
56
85
  def named?
57
86
  !@names.nil?
58
87
  end
@@ -74,7 +103,7 @@ module Rserve
74
103
  if !@names.nil?
75
104
  pos=@names.index(key)
76
105
  if !pos.nil?
77
- return @names[pos]=value
106
+ return self[pos]=value
78
107
  end
79
108
  end
80
109
  push(value,key)
@@ -89,6 +118,16 @@ module Rserve
89
118
  @names.insert(a,nil)
90
119
  end
91
120
  end
121
+ def []=(i,v)
122
+ case i
123
+ when Integer
124
+ super(i,v)
125
+ when String
126
+ put(i,v)
127
+ else
128
+ raise "Should be Integer or String"
129
+ end
130
+ end
92
131
  def [](v)
93
132
  case v
94
133
  when Integer
@@ -19,6 +19,14 @@ describe Rserve::REXP do
19
19
  @m=@r.eval('matrix(c(1,2,3,4,5,6,7,8,9), 3,3)')
20
20
  @m.as_matrix.should==Matrix[[1,4,7],[2,5,8],[3,6,9]]
21
21
  end
22
+ it "method split_array returns a valid splitted array" do
23
+ @m.as_double_matrix.should==@m.as_nested_array
24
+ @r.void_eval("a=1:16; attr(a,'dim')<-c(2,2,2,2)")
25
+ a=@r.eval("a")
26
+ a.as_nested_array.should==[[[[1.0, 3.0], [2.0, 4.0]], [[5.0, 7.0], [6.0, 8.0]]],
27
+ [[[9.0, 11.0], [10.0, 12.0]], [[13.0, 15.0], [14.0, 16.0]]]]
28
+
29
+ end
22
30
  end
23
31
  describe "common methods" do
24
32
  before do
@@ -46,7 +54,15 @@ describe Rserve::REXP do
46
54
  it "method get_attribute should return correct value for attribute" do
47
55
  @l.get_attribute('names').as_strings.should==['at']
48
56
  end
49
-
57
+ it "method create_data_frame should create a valid data frame" do
58
+ l=Rserve::Rlist.new([Rserve::REXP::Integer.new([1,2,3,4,5,6,7,8,9,10])],['a'])
59
+ @r.assign "b", Rserve::REXP.create_data_frame(l)
60
+ b=@r.eval("b")
61
+ b.attr.as_list['class'].as_string.should=='data.frame'
62
+ b.attr.as_list['row.names'].as_integers.should==[Rserve::REXP::Integer::NA, -10]
63
+ b.attr.as_list['names'].as_strings.should==['a']
64
+
65
+ end
50
66
  end
51
67
 
52
68
  end
@@ -11,20 +11,49 @@ describe "Rserve::REXP#to_ruby" do
11
11
  it "should return an array of Fixnum and nils with vector with two or more elements" do
12
12
  @r.eval("c(1,2,3,NA)").to_ruby.should==[1,2,3,nil]
13
13
  end
14
+
15
+ it "should return an array of Fixnum with an enumeration" do
16
+ @r.eval("1:10").to_ruby.should==[1,2,3,4,5,6,7,8,9,10]
17
+ end
18
+ it "should return an array of String with a factor" do
19
+ @r.eval("factor(c(NA,'a','b','b','c'))").to_ruby.should==[nil]+%w{a b b c}
20
+ end
21
+ it "should return an array of String and nils with a factor with NA" do
22
+ @r.eval("factor(c('a','a','b','b','c'))").to_ruby.should==%w{a a b b c}
23
+ end
24
+
14
25
  it "should return a rational with vector with one element" do
15
26
  @r.eval("c(0.5)").to_ruby.should==1.quo(2)
16
27
  end
17
28
  it "should return an array of rationals with vector with more than one elements" do
18
29
  @r.eval("c(0.5,0.5,NA)").to_ruby.should==[1.quo(2), 1.quo(2),nil]
19
30
  end
31
+ it "should return an object with module WithAttributes included, when attributes are set" do
32
+ @r.void_eval("a<-c(1,2,3);attr(a,'names')<-c('a','b','c');")
33
+ a=@r.eval("a").to_ruby
34
+ a.attributes.names.should==['names']
35
+ a.attributes['names'].should==%w{a b c}
36
+ end
20
37
  it "should return a Ruby Matrix with R matrix" do
21
38
  @r.eval("matrix(c(1,2,3,4),2,2)").to_ruby.should==Matrix[[1,3],[2,4]]
22
39
  end
23
- it "should return a nested array of Ruby Matrixes with vector with more than tree dimensions"
40
+ it "should return a nested array of Ruby Matrixes with vector with more than tree dimensions" do
41
+ @r.void_eval("a<-1:16; attr(a,'dim')<-c(2,2,2,2)")
42
+ @r.eval("a").to_ruby.should==[[[[1.0, 3.0], [2.0, 4.0]], [[5.0, 7.0], [6.0, 8.0]]],
43
+ [[[9.0, 11.0], [10.0, 12.0]], [[13.0, 15.0], [14.0, 16.0]]]]
44
+
45
+ end
24
46
  it "should return a boolean with a logical with one element" do
25
47
  @r.eval("TRUE").to_ruby.should be_true
26
48
  end
27
-
49
+ it "should return an array extended with Rserve::WithNames if vector is named" do
50
+ @r.void_eval("a<-c(1,2,3);names(a)<-c('a','b','c')")
51
+ v=@r.eval('a').to_ruby
52
+ v.names.should==['a','b','c']
53
+ v[0].should==1
54
+ v['a'].should==1
55
+
56
+ end
28
57
  it "should return an array of booleans with a logical with two or more elements" do
29
58
  @r.eval("c(TRUE,FALSE,NA)").to_ruby.should==[true,false,nil]
30
59
  end
@@ -35,10 +64,20 @@ describe "Rserve::REXP#to_ruby" do
35
64
  it "should return an array of strings with a vector with two or more strings" do
36
65
  @r.eval("c('a','b',NA)").to_ruby.should==['a','b',nil]
37
66
  end
38
- it "should return an array extended with Rserve::WithNames for a list" do
67
+ it "should return an array extended with Rserve::WithNames and Rserve::WithAttributes for a list" do
39
68
  expected=[1,2,3].extend Rserve::WithNames
40
69
  expected.names=%w{a b c}
41
- @r.eval('list(a=1,b=2,c=3)').to_ruby.should==expected
70
+ list=@r.eval('list(a=1,b=2,c=3)').to_ruby
71
+ list.names.should==expected.names
72
+ list.attributes['names'].should==%w{a b c}
73
+ end
74
+ it "should return a data.frame as an array with Rserve::WithNames and Rserve::WithAttributes" do
75
+ df=@r.eval('data.frame(a=1:10)').to_ruby
76
+ df['a'].should==[1,2,3,4,5,6,7,8,9,10]
77
+ df.attributes['names'].should==['a']
78
+ df.attributes['row.names'].should==[1,2,3,4,5,6,7,8,9,10]
79
+ df.attributes['class'].should=='data.frame'
80
+
42
81
  end
43
82
  end
44
83
  end
@@ -46,6 +46,11 @@ describe Rserve::Protocol::REXPFactory do
46
46
  v.should be_close(a[i],1e-10)
47
47
  }
48
48
  end
49
+ it "should process integer" do
50
+ la=@r.eval("-10:10")
51
+ la.should be_instance_of(Rserve::REXP::Integer)
52
+ la.as_integers.should==(-10..10).to_a
53
+ end
49
54
  it "should process double vector with NA" do
50
55
  la=@r.eval("c(1,NA)")
51
56
  la.should be_instance_of(Rserve::REXP::Double)
@@ -111,7 +116,12 @@ describe Rserve::Protocol::REXPFactory do
111
116
  la.should be_true
112
117
  la.attr.as_list['names'].to_ruby.should==%w{a b}
113
118
  la.attr.as_list['class'].to_ruby.should=="data.frame"
114
- #la.attr.as_list['row.names'].to_ruby.should==[1,2,3,4,5,6,7,8,9,10]
119
+ la.attr.as_list['row.names'].to_ruby.should==[nil,-10]
120
+ end
121
+ it "should process a nested array" do
122
+ @r.void_eval("c=1:8; attr(c,'dim')<-c(2,2,2)")
123
+ la=@r.eval("c")
124
+ la.attr.as_list['dim'].to_ruby.should==[2,2,2]
115
125
  end
116
126
 
117
127
  it "should retrieve correct lenght for string" do
@@ -36,6 +36,17 @@ describe Rserve::WithNames do
36
36
  @a['a'].should==1
37
37
  @a['b'].should==2
38
38
  end
39
+ it "should set values with numeric indexes" do
40
+ @a[0]=10
41
+ @a.should==[10,2,3,4]
42
+ end
43
+ it "should set values with string indexes" do
44
+ @a['a']=10
45
+ @a.should==[10,2,3,4]
46
+ @a.put('b',20)
47
+ @a.should==[10,20,3,4]
48
+ end
49
+
39
50
  it "should push with or without name" do
40
51
  @a.push(5)
41
52
  @a.names.should==['a','b','c',nil,nil]
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 8
9
- version: 0.1.8
8
+ - 9
9
+ version: 0.1.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - Claudio Bustos
@@ -35,7 +35,7 @@ cert_chain:
35
35
  rpP0jjs0
36
36
  -----END CERTIFICATE-----
37
37
 
38
- date: 2010-06-01 00:00:00 -04:00
38
+ date: 2010-06-04 00:00:00 -04:00
39
39
  default_executable:
40
40
  dependencies:
41
41
  - !ruby/object:Gem::Dependency
@@ -77,18 +77,22 @@ executables: []
77
77
  extensions: []
78
78
 
79
79
  extra_rdoc_files:
80
- - Examples.txt
81
80
  - History.txt
81
+ - Introduction.txt
82
82
  - Manifest.txt
83
83
  - README.txt
84
84
  files:
85
- - Examples.txt
85
+ - .autotest
86
86
  - History.txt
87
+ - Introduction.txt
87
88
  - Manifest.txt
88
89
  - README.txt
89
90
  - Rakefile
91
+ - data/gettysburg.txt
92
+ - examples/gettysburg.rb
90
93
  - examples/hello_world.rb
91
94
  - examples/lowless.rb
95
+ - examples/regression.rb
92
96
  - lib/rserve.rb
93
97
  - lib/rserve/connection.rb
94
98
  - lib/rserve/engine.rb
metadata.gz.sig CHANGED
Binary file