From 82ca1c125085588fb32d9ca14a4f3a7b34b411ea Mon Sep 17 00:00:00 2001 From: Thomas Naude Date: Wed, 17 Aug 2022 15:50:14 +0200 Subject: [PATCH] Issue with v2v bin operation and indexes (SciRuby#501) --- lib/daru/helpers/array.rb | 13 ++++ lib/daru/maths/arithmetic/vector.rb | 2 +- spec/maths/arithmetic/vector_spec.rb | 88 +++++++++++++++++----------- 3 files changed, 67 insertions(+), 36 deletions(-) diff --git a/lib/daru/helpers/array.rb b/lib/daru/helpers/array.rb index b76f58696..0af26779c 100644 --- a/lib/daru/helpers/array.rb +++ b/lib/daru/helpers/array.rb @@ -36,5 +36,18 @@ def array_of?(array, match) !array.empty? && array.all? { |el| match === el } # rubocop:disable Style/CaseEquality,Performance/RedundantEqualityComparisonBlock end + + def sort_composite_data(array) + array.sort + rescue ArgumentError, TypeError => e + case e.to_s + when /comparison of Symbol with String failed/, + /comparison of Symbol with \d+ failed/, + /comparison of String with :.* failed/, + /comparison of Integer with :.* failed/, + /no implicit conversion from nil to integer/ + array.sort_by(&:to_s) + end + end end end diff --git a/lib/daru/maths/arithmetic/vector.rb b/lib/daru/maths/arithmetic/vector.rb index 39b293155..ca857dd79 100644 --- a/lib/daru/maths/arithmetic/vector.rb +++ b/lib/daru/maths/arithmetic/vector.rb @@ -92,7 +92,7 @@ def v2o_binary(operation, other) def v2v_binary(operation, other, opts = {}) # FIXME: why the sorting?.. - zverok, 2016-05-18 - index = (@index.to_a | other.index.to_a).sort + index = ArrayHelper.sort_composite_data(@index.to_a | other.index.to_a) elements = index.map do |idx| this = self.index.include?(idx) ? self[idx] : nil diff --git a/spec/maths/arithmetic/vector_spec.rb b/spec/maths/arithmetic/vector_spec.rb index 2d3289db6..c513f5268 100644 --- a/spec/maths/arithmetic/vector_spec.rb +++ b/spec/maths/arithmetic/vector_spec.rb @@ -1,48 +1,68 @@ describe Daru::Vector do - before :each do - @dv1 = Daru::Vector.new [1,2,3,4], name: :boozy, index: [:bud, :kf, :henie, :corona] - @dv2 = Daru::Vector.new [1,2,3,4], name: :mayer, index: [:obi, :wan, :kf, :corona] - @with_md1 = Daru::Vector.new [1,2,3,nil,5,nil], name: :missing, index: [:a, :b, :c, :obi, :wan, :corona] - @with_md2 = Daru::Vector.new [1,2,3,nil,5,nil], name: :missing, index: [:obi, :wan, :corona, :a, :b, :c] + let(:dv1) { described_class.new([1, 2, 3, 4], name: :boozy, index: [:bud, :kf, :henie, :corona]) } + let(:dv2) { described_class.new([1, 2, 3, 4], name: :mayer, index: [:obi, :wan, :kf, :corona]) } + let(:with_md1) do + described_class.new([1, 2, 3, nil, 5, nil], name: :missing, index: [:a, :b, :c, :obi, :wan, :corona]) + end + let(:with_md2) do + described_class.new([1, 2, 3, nil, 5, nil], name: :missing, index: [:obi, :wan, :corona, :a, :b, :c]) end - context "#+" do + describe "#+" do it "adds matching indexes of the other vector" do - expect(@dv1 + @dv2).to eq(Daru::Vector.new([nil,8,nil,5,nil,nil], name: :boozy, index: [:bud,:corona,:henie,:kf,:obi,:wan])) + expect(dv1 + dv2).to eq( + Daru::Vector.new([nil, 8, nil, 5, nil, nil], name: :boozy, index: [:bud, :corona, :henie, :kf, :obi, :wan]) + ) end it "adds number to each element of the entire vector" do - expect(@dv1 + 5).to eq(Daru::Vector.new [6,7,8,9], name: :boozy, index: [:bud, :kf, :henie, :corona]) + expect(dv1 + 5).to eq(Daru::Vector.new([6, 7, 8, 9], name: :boozy, index: [:bud, :kf, :henie, :corona])) end it "does not add when a number is being added" do - expect(@with_md1 + 1).to eq(Daru::Vector.new([2,3,4,nil,6,nil], name: :missing, index: [:a, :b, :c, :obi, :wan, :corona])) + expect(with_md1 + 1).to eq( + Daru::Vector.new([2, 3, 4, nil, 6, nil], name: :missing, index: [:a, :b, :c, :obi, :wan, :corona]) + ) end it "puts a nil when one of the operands is nil" do - expect(@with_md1 + @with_md2).to eq(Daru::Vector.new([nil,7,nil,nil,nil,7], name: :missing, index: [:a, :b, :c, :corona, :obi, :wan])) + expect(with_md1 + with_md2).to eq( + Daru::Vector.new([nil, 7, nil, nil, nil, 7], name: :missing, index: [:a, :b, :c, :corona, :obi, :wan]) + ) + end + + context 'when vectors have numeric and non-numeric indexes' do + let(:dv1) { described_class.new([1, 2, 3]) } + let(:dv2) { described_class.new([1, 2, 3], index: [:a, :b, :c]) } + + it "appropriately adds vectors with numeric and non-numeric indexes" do + expect(dv1 + dv2).to eq(Daru::Vector.new(Array.new(6), index: [0, 1, 2, :a, :b, :c])) + end end - it "appropriately adds vectors with numeric and non-numeric indexes" do - pending "Need an alternate index implementation?" - v1 = Daru::Vector.new([1,2,3]) - v2 = Daru::Vector.new([1,2,3], index: [:a,:b,:c]) + context 'when index contains symbols and strings' do + let(:dv1) { described_class.new([1, 2, 3, 4], name: :boozy, index: [:bud, 'kf', :henie, :corona]) } + let(:dv2) { described_class.new([1, 2, 3, 4], name: :mayer, index: [:obi, :wan, 'kf', :corona]) } - expect(v1 + v2).to eq(Daru::Vector.new([nil]*6, index: [0,1,2,:a,:b,:c])) + it "adds matching indexes of the other vector" do + expect(dv1 + dv2).to eq( + Daru::Vector.new([nil,8,nil,5,nil,nil], name: :boozy, index: [:bud, :corona, :henie, 'kf', :obi, :wan]) + ) + end end end - context "#-" do + describe "#-" do it "subtracts matching indexes of the other vector" do - expect(@dv1 - @dv2).to eq(Daru::Vector.new([nil,0,nil,-1,nil,nil], name: :boozy, index: [:bud,:corona,:henie,:kf,:obi,:wan])) + expect(dv1 - dv2).to eq(Daru::Vector.new([nil,0,nil,-1,nil,nil], name: :boozy, index: [:bud,:corona,:henie,:kf,:obi,:wan])) end it "subtracts number from each element of the entire vector" do - expect(@dv1 - 5).to eq(Daru::Vector.new [-4,-3,-2,-1], name: :boozy, index: [:bud, :kf, :henie, :corona]) + expect(dv1 - 5).to eq(Daru::Vector.new [-4,-3,-2,-1], name: :boozy, index: [:bud, :kf, :henie, :corona]) end end - context "#*" do + describe "#*" do it "multiplies matching indexes of the other vector" do end @@ -52,7 +72,7 @@ end end - context "#\/" do + describe "#\/" do it "divides matching indexes of the other vector" do end @@ -62,55 +82,53 @@ end end - context "#%" do + describe "#%" do end - context "#**" do + describe "#**" do end - context "#exp" do + describe "#exp" do it "calculates exp of all numbers" do - expect(@with_md1.exp.round(3)).to eq(Daru::Vector.new([2.718281828459045, + expect(with_md1.exp.round(3)).to eq(Daru::Vector.new([2.718281828459045, 7.38905609893065, 20.085536923187668, nil, 148.4131591025766, nil], index: [:a, :b, :c, :obi, :wan, :corona], name: :missing).round(3)) end end - context "#add" do - + describe "#add" do it "adds two vectors with nils as 0 if skipnil is true" do - expect(@with_md1.add(@with_md2, skipnil: true)).to eq(Daru::Vector.new( + expect(with_md1.add(with_md2, skipnil: true)).to eq(Daru::Vector.new( [1, 7, 3, 3, 1, 7], name: :missing, index: [:a, :b, :c, :corona, :obi, :wan])) end it "adds two vectors same as :+ if skipnil is false" do - expect(@with_md1.add(@with_md2, skipnil: false)).to eq(Daru::Vector.new( + expect(with_md1.add(with_md2, skipnil: false)).to eq(Daru::Vector.new( [nil, 7, nil, nil, nil, 7], name: :missing, index: [:a, :b, :c, :corona, :obi, :wan])) end - end - context "#abs" do + describe "#abs" do it "calculates abs value" do - @with_md1.abs + with_md1.abs end end - context "#sqrt" do + describe "#sqrt" do it "calculates sqrt" do - @with_md1.sqrt + with_md1.sqrt end end - context "#round" do + describe "#round" do it "rounds to given precision" do - @with_md1.round(2) + with_md1.round(2) end end end