Skip to content

Commit

Permalink
Added functionality to recognize 2D named objects
Browse files Browse the repository at this point in the history
  • Loading branch information
nessche committed Nov 2, 2012
1 parent 35e40fd commit 9f0dad6
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/rserve.rb
Expand Up @@ -8,6 +8,7 @@ module Rserve

require 'rserve/withnames'
require 'rserve/withattributes'
require 'rserve/with2dnames'


require 'rserve/protocol'
Expand Down
6 changes: 6 additions & 0 deletions lib/rserve/rexp.rb
Expand Up @@ -401,6 +401,12 @@ def to_ruby

v.names=v.attributes['names']
end
if v.attributes and v.attributes.has_name? 'dim' and v.attributes.has_name? 'dimnames' and v.attributes['dim'].size == 2
# Maybe here we should check that dim, dimnames and the payload are consistent
v.extend Rserve::With2DNames
v.sizes = v.attributes['dim']
v.names = v.attributes['dimnames']
end
end

# Hack: change attribute row.names according to spec
Expand Down
38 changes: 38 additions & 0 deletions lib/rserve/with2dnames.rb
@@ -0,0 +1,38 @@
module Rserve

module With2DNames
attr_reader :row_names, :column_names, :row_size, :column_size

def sizes=(sizes)
raise ArgumentError, "sizes must be of size 2" unless (sizes.size == 2)
raise ArgumentError, "mismatch between provided size info and actual number of elements" unless self.size == (sizes[0] * sizes[1])
@row_size = sizes[0]
@column_size = sizes[1]

end

def names=(names)
raise ArgumentError, "sizes must be of size 2" unless (names.size == 2)
raise ArgumentError, "mismatch between provided size info and actual number of elements" unless ((@row_size == names[0].size) and (@column_size == names[1].size))
@row_names = names[0]
@column_names = names[1]
end

def two_d_at(i, j)
index_i = (i.is_a? Integer) ? i : @row_names.index(i)
index_j = (j.is_a? Integer) ? j : @column_names.index(j)
self[index_i + index_j * row_size]
end

def two_d_named?
@row_names and @column_names
end

def column(j)
index_j = (j.is_a? Integer) ? j : @column_names.index(j)
end

end


end
38 changes: 38 additions & 0 deletions spec/rserve_rexp_to_ruby_spec.rb
Expand Up @@ -5,6 +5,7 @@
before do
@r=Rserve::Connection.new
end

after do
@r.close
end
Expand Down Expand Up @@ -89,5 +90,42 @@
df.attributes['class'].should=='data.frame'

end

context "when passing a 2d object" do

let :two_d_object do
col_names = Rserve::REXP::String.new(%w((Intercept) x1 x2 x3), nil)
row_names = Rserve::REXP::String.new(%w(1 2 3), nil)
names_list = Rserve::Rlist.new([row_names, col_names])
names_vector = Rserve::REXP::GenericVector.new(names_list)
dim_array = [3,4]
dimensions = Rserve::REXP::Integer.new(dim_array)
attr_payload = Rserve::Rlist.new([dimensions, names_vector], %w(dim dimnames))
attr_list = Rserve::REXP::List.new(attr_payload)
payload = [true, true, true, true, true, true, false, true, true, false, false, true]
Rserve::REXP::Logical.new(payload, attr_list)
end

before do
@two_d_array = two_d_object.to_ruby
end

it "should return a 2d object as an array with Rserve;;With2DNames" do
@two_d_array.should be_an Array
@two_d_array.should be_a Rserve::With2DNames
end

it "should set the row and column labels" do
@two_d_array.row_names.should == %w(1 2 3)
@two_d_array.column_names.should == %W((Intercept) x1 x2 x3)
end

it "should set the row and column sizes" do
@two_d_array.row_size.should == 3
@two_d_array.column_size.should == 4
end

end

end
end
102 changes: 102 additions & 0 deletions spec/rserve_with2dnames_spec.rb
@@ -0,0 +1,102 @@
require File.expand_path(File.dirname(__FILE__)+"/spec_helper.rb")
require '../lib/rserve/rexp/string'
require '../lib/rserve/rexp/integer'
require '../lib/rserve/rexp/list'


describe Rserve::With2DNames do

before do

# when a 2d object is returned by R as an array
# the elements are listed column by column
@array = [1,5,9,2,6,10,3,7,11,4,8,12]
# corresponds to a matrix like
# 1 2 3 4
# 5 6 7 8
# 9 10 11 12
@array.extend Rserve::With2DNames
end

describe "sizes" do

context "when passed the correct values" do

it "should set the size values for rows and columns" do
@array.sizes = [3,4]
@array.row_size.should == 3
@array.column_size.should == 4
end

end

context "when passed the wrong values" do

it "should throw if the object passed does not have two elements" do
expect{@array.sizes = [5]}.to raise_error ArgumentError
end

it "should throw if the sizes passed do not match the array size" do
expect{@array.sizes = [1,5]}.to raise_error ArgumentError
end

end

end

describe "names" do

context "when passed the correct values" do

before do
@array.sizes = [3,4]
end

it "should set the names for rows and columns" do
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
@array.row_names.should == %w(r1 r2 r3)
@array.column_names.should == %w(c1 c2 c3 c4)
end

end

context "when passed the wrong values" do

it "should throw if the object passed does not have two elements" do
expect{@array.names = [%w(r1 r2 r3)]}.to raise_error ArgumentError
end

it "should throw if the sizes passed do not match the array size" do
expect{@array.names = [%w(r1 r2 r3 r4),%w(c1 c2 c3 c4)]}.to raise_error ArgumentError
end

end

end

describe "two_d_at" do

before do
@array.sizes = [3,4]
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
end


it "should return the correct values using integer indices" do
@array.two_d_at(0,0).should == 1
@array.two_d_at(2,0).should == 9
@array.two_d_at(0,3).should == 4
@array.two_d_at(2,3).should == 12
end

it "should return the correct values using names" do
@array.two_d_at("r1","c2").should == 2
@array.two_d_at("r2","c1").should == 5
end


end



end

0 comments on commit 9f0dad6

Please sign in to comment.