spec/vector_spec.rb in daru-0.0.4 vs spec/vector_spec.rb in daru-0.0.5

- old
+ new

@@ -1,28 +1,47 @@ require 'spec_helper.rb' describe Daru::Vector do - [Array, NMatrix].each do |dtype| + ALL_DTYPES = [:array, :nmatrix] + + ALL_DTYPES.each do |dtype| describe dtype do context "#initialize" do + before do + @tuples = [ + [:a, :one, :foo], + [:a, :two, :bar], + [:b, :one, :bar], + [:b, :two, :baz] + ] + + @multi_index = Daru::MultiIndex.new(@tuples) + end + it "initializes from an Array" do dv = Daru::Vector.new [1,2,3,4,5], name: :ravan, index: [:ek, :don, :teen, :char, :pach], dtype: dtype expect(dv.name) .to eq(:ravan) expect(dv.index).to eq(Daru::Index.new [:ek, :don, :teen, :char, :pach]) end it "accepts Index object" do idx = Daru::Index.new [:yoda, :anakin, :obi, :padme, :r2d2] - dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, index: idx, dtype: dtype expect(dv.name) .to eq(:yoga) expect(dv.index).to eq(idx) end + it "accepts a MultiIndex object" do + dv = Daru::Vector.new [1,2,3,4], name: :mi, index: @multi_index, dtype: dtype + + expect(dv.name).to eq(:mi) + expect(dv.index).to eq(@multi_index) + end + it "raises error for improper Index" do expect { dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, index: [:i, :j, :k] }.to raise_error @@ -30,59 +49,201 @@ idx = Daru::Index.new [:i, :j, :k] dv = Daru::Vector.new [1,2,3,4,5], name: :yoda, index: idx, dtype: dtype }.to raise_error end + it "raises error for improper MultiIndex" do + expect { + dv = Daru::Vector.new [1,2,3,4,5], name: :mi, index: @multi_index + }.to raise_error + end + it "initializes without specifying an index" do dv = Daru::Vector.new [1,2,3,4,5], name: :vishnu, dtype: dtype expect(dv.index).to eq(Daru::Index.new [0,1,2,3,4]) end it "inserts nils for extra indices" do - dv = Daru::Vector.new [1,2,3], name: :yoga, index: [0,1,2,3,4], dtype: Array + dv = Daru::Vector.new [1,2,3], name: :yoga, index: [0,1,2,3,4], dtype: :array expect(dv).to eq([1,2,3,nil,nil].dv(:yoga,nil, Array)) end + + it "inserts nils for extra indices (MultiIndex)" do + dv = Daru::Vector.new [1,2], name: :mi, index: @multi_index, dtype: :array + expect(dv).to eq(Daru::Vector.new([1,2,nil,nil], name: :mi, index: @multi_index, dtype: :array)) + end end context "#[]" do - before :each do - @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, - index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype - end + context Daru::Index do + before :each do + @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, + index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype + end - it "returns an element after passing an index" do - expect(@dv[:yoda]).to eq(1) - end + it "returns an element after passing an index" do + expect(@dv[:yoda]).to eq(1) + end - it "returns an element after passing a numeric index" , :focus => true do - expect(@dv[0]).to eq(1) + it "returns an element after passing a numeric index" do + expect(@dv[0]).to eq(1) + end + + it "returns a vector with given indices for multiple indices" do + expect(@dv[:yoda, :anakin]).to eq(Daru::Vector.new([1,2], name: :yoda, + index: [:yoda, :anakin], dtype: dtype)) + end + + it "returns a vector with given indices for multiple numeric indices" do + expect(@dv[0,1]).to eq(Daru::Vector.new([1,2], name: :yoda, + index: [:yoda, :anakin], dtype: dtype)) + end + + it "returns a vector when specified symbol Range" do + expect(@dv[:yoda..:anakin]).to eq(Daru::Vector.new([1,2], + index: [:yoda, :anakin], name: :yoga, dtype: dtype)) + end + + it "returns a vector when specified numeric Range" do + expect(@dv[3..4]).to eq(Daru::Vector.new([4,5], name: :yoga, + index: [:padme, :r2d2], name: :yoga, dtype: dtype)) + end end - it "returns a vector with given indices for multiple indices" do - expect(@dv[:yoda, :anakin]).to eq(Daru::Vector.new([1,2], name: :yoda, - index: [:yoda, :anakin], dtype: dtype)) + context Daru::MultiIndex do + before do + @tuples = [ + [:a,:one,:bar], + [:a,:one,:baz], + [:a,:two,:bar], + [:a,:two,:baz], + [:b,:one,:bar], + [:b,:two,:bar], + [:b,:two,:baz], + [:b,:one,:foo], + [:c,:one,:bar], + [:c,:one,:baz], + [:c,:two,:foo], + [:c,:two,:bar] + ] + @multi_index = Daru::MultiIndex.new(@tuples) + @vector = Daru::Vector.new Array.new(12) { |i| i }, index: @multi_index, + dtype: dtype, name: :mi_vector + end + + it "returns a single element when passed a row number" do + expect(@vector[1]).to eq(1) + end + + it "returns a single element when passed the full tuple" do + expect(@vector[:a, :one, :baz]).to eq(1) + end + + it "returns sub vector when passed first layer of tuple" do + mi = Daru::MultiIndex.new([ + [:one,:bar], + [:one,:baz], + [:two,:bar], + [:two,:baz]]) + expect(@vector[:a]).to eq(Daru::Vector.new([0,1,2,3], index: mi, + dtype: dtype, name: :sub_vector)) + end + + it "returns sub vector when passed first and second layer of tuple" do + mi = Daru::MultiIndex.new([ + [:foo], + [:bar]]) + expect(@vector[:c,:two]).to eq(Daru::Vector.new([10,11], index: mi, + dtype: dtype, name: :sub_sub_vector)) + end + + it "returns a vector with corresponding MultiIndex when specified numeric Range" do + mi = Daru::MultiIndex.new([ + [:a,:two,:baz], + [:b,:one,:bar], + [:b,:two,:bar], + [:b,:two,:baz], + [:b,:one,:foo], + [:c,:one,:bar], + [:c,:one,:baz] + ]) + expect(@vector[3..9]).to eq(Daru::Vector.new([3,4,5,6,7,8,9], index: mi, + dtype: dtype, name: :slice)) + end end end context "#[]=" do - before :each do - @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, - index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype - end + context Daru::Index do + before :each do + @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, + index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype + end - it "assigns at the specified index" do - @dv[:yoda] = 666 + it "assigns at the specified index" do + @dv[:yoda] = 666 + expect(@dv[:yoda]).to eq(666) + end - expect(@dv[:yoda]).to eq(666) + it "assigns at the specified Integer index" do + @dv[0] = 666 + expect(@dv[:yoda]).to eq(666) + end + + it "sets dtype to Array if a nil is assigned" do + @dv[0] = nil + expect(@dv.dtype).to eq(:array) + end end - it "assigns at the specified Integer index" do - @dv[0] = 666 + context Daru::MultiIndex do + before :each do + @tuples = [ + [:a,:one,:bar], + [:a,:one,:baz], + [:a,:two,:bar], + [:a,:two,:baz], + [:b,:one,:bar], + [:b,:two,:bar], + [:b,:two,:baz], + [:b,:one,:foo], + [:c,:one,:bar], + [:c,:one,:baz], + [:c,:two,:foo], + [:c,:two,:bar] + ] + @multi_index = Daru::MultiIndex.new(@tuples) + @vector = Daru::Vector.new Array.new(12) { |i| i }, index: @multi_index, + dtype: dtype, name: :mi_vector + end - expect(@dv[:yoda]).to eq(666) + it "assigns all lower layer indices when specified a first layer index" do + @vector[:b] = 69 + expect(@vector).to eq(Daru::Vector.new([0,1,2,3,69,69,69,69,8,9,10,11], + index: @multi_index, name: :top_layer_assignment, dtype: dtype + )) + end + + it "assigns all lower indices when specified first and second layer index" do + @vector[:b, :one] = 69 + expect(@vector).to eq(Daru::Vector.new([0,1,2,3,69,5,6,69,8,9,10,11], + index: @multi_index, name: :second_layer_assignment, dtype: dtype)) + end + + it "assigns just the precise value when specified complete tuple" do + @vector[:b, :one, :foo] = 69 + expect(@vector).to eq(Daru::Vector.new([0,1,2,3,4,5,6,69,8,9,10,11], + index: @multi_index, name: :precise_assignment, dtype: dtype)) + end + + it "assigns correctly when numeric index" do + @vector[7] = 69 + expect(@vector).to eq(Daru::Vector.new([0,1,2,3,4,5,6,69,8,9,10,11], + index: @multi_index, name: :precise_assignment, dtype: dtype)) + end end end context "#concat" do before :each do @@ -114,59 +275,346 @@ }.to raise_error end end context "#delete" do - it "deletes specified value in the vector" do - dv = Daru::Vector.new [1,2,3,4,5], name: :a, dtype: dtype + context Daru::Index do + it "deletes specified value in the vector" do + dv = Daru::Vector.new [1,2,3,4,5], name: :a, dtype: dtype - dv.delete 3 + dv.delete 3 + expect(dv).to eq(Daru::Vector.new [1,2,4,5], name: :a) + end + end - expect(dv).to eq(Daru::Vector.new [1,2,4,5], name: :a) + context Daru::MultiIndex do + pending end end context "#delete_at" do - before :each do - @dv = Daru::Vector.new [1,2,3,4,5], name: :a, - index: [:one, :two, :three, :four, :five], dtype: dtype - end + context Daru::Index do + before :each do + @dv = Daru::Vector.new [1,2,3,4,5], name: :a, + index: [:one, :two, :three, :four, :five], dtype: dtype + end - it "deletes element of specified index" do - @dv.delete_at :one + it "deletes element of specified index" do + @dv.delete_at :one - expect(@dv).to eq(Daru::Vector.new [2,3,4,5], name: :a, - index: [:two, :three, :four, :five]), dtype: dtype - end + expect(@dv).to eq(Daru::Vector.new [2,3,4,5], name: :a, + index: [:two, :three, :four, :five]), dtype: dtype + end - it "deletes element of specified integer index" do - @dv.delete_at 2 + it "deletes element of specified integer index" do + @dv.delete_at 2 - expect(@dv).to eq(Daru::Vector.new [1,2,4,5], name: :a, - index: [:one, :two, :four, :five]), dtype: dtype + expect(@dv).to eq(Daru::Vector.new [1,2,4,5], name: :a, + index: [:one, :two, :four, :five]), dtype: dtype + end end + + context Daru::MultiIndex do + pending "Possibly next release" + end end context "#index_of" do - it "returns index of specified value" do - dv = Daru::Vector.new [1,2,3,4,5], name: :a, - index: [:one, :two, :three, :four, :five], dtype: dtype + context Daru::Index do + it "returns index of specified value" do + dv = Daru::Vector.new [1,2,3,4,5], name: :a, + index: [:one, :two, :three, :four, :five], dtype: dtype - expect(dv.index_of(1)).to eq(:one) + expect(dv.index_of(1)).to eq(:one) + end end + + context Daru::MultiIndex do + it "returns tuple of specified value" do + mi = Daru::MultiIndex.new([ + [:a,:two,:bar], + [:a,:two,:baz], + [:b,:one,:bar], + [:b,:two,:bar] + ]) + vector = Daru::Vector.new([1,2,3,4], index: mi, dtype: dtype) + expect(vector.index_of(3)).to eq([:b,:one,:bar]) + end + end end context "#to_hash" do - it "returns the vector as a hash" do - dv = Daru::Vector.new [1,2,3,4,5], name: :a, - index: [:one, :two, :three, :four, :five], dtype: dtype + context Daru::Index do + it "returns the vector as a hash" do + dv = Daru::Vector.new [1,2,3,4,5], name: :a, + index: [:one, :two, :three, :four, :five], dtype: dtype - expect(dv.to_hash).to eq({one: 1, two: 2, three: 3, four: 4, five: 5}) + expect(dv.to_hash).to eq({one: 1, two: 2, three: 3, four: 4, five: 5}) + end end + + context Daru::MultiIndex do + pending + # it "returns vector as a Hash" do + # pending + # mi = Daru::MultiIndex.new([ + # [:a,:two,:bar], + # [:a,:two,:baz], + # [:b,:one,:bar], + # [:b,:two,:bar] + # ]) + # vector = Daru::Vector.new([1,2,3,4], index: mi, dtype: dtype) + # expect(vector.to_hash).to eq({ + # [:a,:two,:bar] => 1, + # [:a,:two,:baz] => 2, + # [:b,:one,:bar] => 3, + # [:b,:two,:bar] => 4 + # }) + # end + end end context "#uniq" do - # TODO + it "keeps only unique values" do + # TODO: fill this in + end end + + context "#cast" do + ALL_DTYPES.each do |new_dtype| + it "casts from #{dtype} to #{new_dtype}" do + v = Daru::Vector.new [1,2,3,4], dtype: dtype + v.cast(dtype: new_dtype) + expect(v.dtype).to eq(new_dtype) + end + end + end + + context "#sort" do + context Daru::Index do + before do + @dv = Daru::Vector.new [33,2,15,332,1], name: :dv, index: [:a, :b, :c, :d, :e] + end + + it "sorts the vector with defaults and returns a new vector, preserving indexing" do + expect(@dv.sort).to eq(Daru::Vector.new([1,2,15,33,332], name: :dv, index: [:e, :b, :c, :a, :d])) + end + + it "sorts the vector in descending order" do + expect(@dv.sort(ascending: false)).to eq(Daru::Vector.new([332,33,15,2,1], name: :dv, index: [:d, :a, :c, :b, :e])) + end + + it "accepts a block" do + str_dv = Daru::Vector.new ["My Jazz Guitar", "Jazz", "My", "Guitar"] + + sorted = str_dv.sort { |a,b| a.length <=> b.length } + expect(sorted).to eq(Daru::Vector.new(["My", "Jazz", "Guitar", "My Jazz Guitar"], index: [2,1,3,0])) + end + + it "places nils near the end of the vector" do + pending + with_nils = Daru::Vector.new [22,4,nil,111,nil,2] + + expect(with_nils.sort).to eq(Daru::Vector.new([2,4,22,111,nil,nil], index: [5,1,0,3,2,4])) + end if dtype == :array + end + + context Daru::MultiIndex do + before do + mi = Daru::MultiIndex.new([ + [:a, :one, :foo], + [:a, :two, :bar], + [:b, :one, :bar], + [:b, :two, :baz], + [:b, :three, :bar] + ]) + @vector = Daru::Vector.new([44,22,111,0,-56], index: mi, name: :unsorted, + dtype: dtype) + end + + it "sorts vector" do + mi_asc = Daru::MultiIndex.new([ + [:b, :three, :bar], + [:b, :two, :baz], + [:a, :two, :bar], + [:a, :one, :foo], + [:b, :one, :bar] + ]) + expect(@vector.sort).to eq(Daru::Vector.new([-56,0,22,44,111], index: mi_asc, + name: :ascending, dtype: dtype)) + end + + it "sorts in descending" do + mi_dsc = Daru::MultiIndex.new([ + [:b, :one, :bar], + [:a, :one, :foo], + [:a, :two, :bar], + [:b, :two, :baz], + [:b, :three, :bar] + ]) + expect(@vector.sort(ascending: false)).to eq(Daru::Vector.new( + [111,44,22,0,-56], index: mi_dsc, name: :descending, dtype: dtype)) + end + + it "sorts using the supplied block" do + mi_abs = Daru::MultiIndex.new([ + [:b, :two, :baz], + [:a, :two, :bar], + [:a, :one, :foo], + [:b, :three, :bar], + [:b, :one, :bar] + ]) + expect(@vector.sort { |a,b| a.abs <=> b.abs }).to eq(Daru::Vector.new( + [0,22,44,-56,111], index: mi_abs, name: :sort_abs, dtype: dtype)) + end + end + end + + context "#reindex" do + context Daru::Index do + before do + @dv = Daru::Vector.new [1,2,3,4,5], name: :dv, index: [:a, :b, :c, :d, :e] + end + + it "recreates index with sequential numbers" do + a = @dv.reindex(:seq) + + expect(a).to eq(Daru::Vector.new([1,2,3,4,5], name: :dv, index: [0,1,2,3,4])) + expect(a).to_not eq(@dv) + end + + it "accepts a new non-numeric index" do + a = @dv.reindex([:hello, :my, :name, :is, :ted]) + + expect(a).to eq(Daru::Vector.new([1,2,3,4,5], name: :dv, index: [:hello, :my, :name, :is, :ted])) + expect(a).to_not eq(@dv) + end + end + + context Daru::MultiIndex do + pending + end + end end + end # checking with ALL_DTYPES + + # works with arrays only + context "#is_nil?" do + before(:each) do + @with_md = Daru::Vector.new([1,2,nil,3,4,nil]) + @without_md = Daru::Vector.new([1,2,3,4,5,6]) + end + + it "verifies missing data presence" do + expect(@with_md.is_nil?) .to eq(Daru::Vector.new([false,false,true,false,false,true])) + expect(@without_md.is_nil?).to eq(Daru::Vector.new([false,false,false,false,false,false])) + end end -end if mri? \ No newline at end of file + + context "#clone_structure" do + context Daru::Index do + it "clones a vector with its index and fills it with nils" do + vec = Daru::Vector.new([1,2,3,4,5], index: [:a,:b,:c,:d,:e]) + expect(vec.clone_structure).to eq(Daru::Vector.new([nil,nil,nil,nil,nil], index: [:a,:b,:c,:d,:e])) + end + end + + context Daru::MultiIndex do + pending + end + end + + context "#nil_positions" do + context Daru::Index do + before(:each) do + @with_md = Daru::Vector.new([1,2,nil,3,4,nil]) + end + + it "returns the indexes of nils" do + expect(@with_md.nil_positions).to eq([2,5]) + end + + it "updates after assingment" do + @with_md[3] = nil + expect(@with_md.nil_positions).to eq([2,3,5]) + end + end + + context Daru::MultiIndex do + pending + end + end + + context "#replace_nils" do + it "replaces all nils with the specified value" do + vec = Daru::Vector.new([1,2,3,nil,nil,4]) + expect(vec.replace_nils(2)).to eq(Daru::Vector.new([1,2,3,2,2,4])) + end + + it "replaces all nils with the specified value (bang)" do + vec = Daru::Vector.new([1,2,3,nil,nil,4]).replace_nils!(2) + expect(vec).to eq(Daru::Vector.new([1,2,3,2,2,4])) + end + end + + context "#type" do + before(:each) do + @numeric = Daru::Vector.new([1,2,3,4,5]) + @multi = Daru::Vector.new([1,2,3,'sameer','d']) + @with_nils = Daru::Vector.new([1,2,3,4,nil]) + end + + it "checks numeric data correctly" do + expect(@numeric.type).to eq(:numeric) + end + + it "checks for multiple types of data" do + expect(@multi.type).to eq(:object) + end + + it "tells NMatrix data type in case of NMatrix wrapper" do + nm = Daru::Vector.new([1,2,3,4,5], dtype: :nmatrix) + expect(nm.type).to eq(:int32) + end + + it "changes type to object as per assignment" do + expect(@numeric.type).to eq(:numeric) + @numeric[2] = 'my string' + expect(@numeric.type).to eq(:object) + end + + it "changes type to numeric as per assignment" do + expect(@multi.type).to eq(:object) + @multi[3] = 45 + @multi[4] = 54 + expect(@multi.type).to eq(:numeric) + end + + it "reports numeric if nils with number data" do + expect(@with_nils.type).to eq(:numeric) + end + + it "stays numeric when nil is reassigned to a number" do + @with_nils[4] = 66 + expect(@with_nils.type).to eq(:numeric) + end + + it "changes to :object when nil is reassigned to anything but a number" do + @with_nils[4] = 'string' + expect(@with_nils.type).to eq(:object) + end + end + + context "#to_matrix" do + before do + @vector = Daru::Vector.new [1,2,3,4,5,6] + end + + it "converts Daru::Vector to a horizontal Ruby Matrix" do + expect(@vector.to_matrix).to eq(Matrix[[1,2,3,4,5,6]]) + end + + it "converts Daru::Vector to a vertical Ruby Matrix" do + expect(@vector.to_matrix(:vertical)).to eq(Matrix.columns([[1,2,3,4,5,6]])) + end + end +end if mri?