From da47cdc78eb403116feb156321eec6ad5b78dd3d Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Wed, 21 Dec 2016 19:34:15 +0100 Subject: [PATCH 01/25] Instances: 1) add support for adding attribute values passed via a hash 2) add testcases for it --- lib/weka/core/instances.rb | 94 +++++++++++++++++++++++++++++++-- spec/core/instances_spec.rb | 101 ++++++++++++++++++++++++++++-------- 2 files changed, 168 insertions(+), 27 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index f098c51..ae5d13d 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -53,8 +53,12 @@ def attributes enumerate_attributes.to_a end - def attribute_names - attributes.map(&:name) + def attribute_names(use_symbol = false) + if use_symbol + attributes.map(&:name).map(&:to_sym) + else + attributes.map(&:name) + end end def add_attributes(&block) @@ -67,7 +71,7 @@ def add_attributes(&block) alias attributes_count num_attributes alias has_string_attribute? check_for_string_attributes - ## Check if the instances has any attribute of the given type + # Check if the instances has any attribute of the given type # @param [String, Symbol, Integer] type type of the attribute to check # String and Symbol argument are converted to corresponding type # defined in Weka::Core::Attribute @@ -189,6 +193,22 @@ def class_attribute_defined? class_index >= 0 end + # Add new instance + # @param [Instance, Array, Hash] instance_or_values the attribute values + # of the instance to be added + # + # @example Passing Instance + # instances.add_instance(instance) + # + # @example Passing an array of attribute values. Note that the attribute + # values must be in the same order as the Instances attributes. + # instances.add_instance([attr1_value, attr2_value, attr3_value, ...]) + # + # @example Passing a hash of attribute values. The keys are the names of + # the attributes and their values are corresponding attribute values. + # hash = { attr1_name: attr1_value, attr2_name: attr2_value, ... } + # instances.add_instance(hash) + # def add_instance(instance_or_values, weight: 1.0) instance = instance_from(instance_or_values, weight: weight) add(instance) @@ -198,10 +218,24 @@ def add_instances(data, weight: 1.0) data.each { |values| add_instance(values, weight: weight) } end + # Retrieve the internal floating point values used to represent + # the attributes. + # + # @param [Array, Hash] values the attribute values whose floating + # point representation should be retrieved. + # + # @return [Array, Hash] an array of the internal floating point + # representation if the input is an Array. Hash otherwise. def internal_values_of(values) - values.each_with_index.map do |value, index| + use_hash = values.is_a?(Hash) + values = attribute_values_from_hash(values) if use_hash + + values = values.each_with_index.map do |value, index| attribute(index).internal_value_of(value) end + + values = attribute_values_to_hash(values) if use_hash + values end def apply_filter(filter) @@ -240,11 +274,29 @@ def attribute_with_name(name) attributes.select { |attribute| attribute.name == name.to_s }.first end + # Wrap the attribute values for the instance to be added with + # an Instance object, if needed. The Instance object is + # assigned with the given weight. + # + # @param [Instance, Array, Hash] instance_or_values either the + # instance object to be added or the attribute values for it. + # For the latter case, we can pass an array or a hash. + # + # @param [Float] weight the weight for the Instance to be added + # + # @return [Instance] the object that contains the given + # attribute values. def instance_from(instance_or_values, weight:) + if instance_or_values.is_a?(Java::WekaCore::Instance) instance_or_values.weight = weight instance_or_values else + + if instance_or_values.is_a?(Hash) + instance_or_values = attribute_values_from_hash(instance_or_values) + end + data = internal_values_of(instance_or_values) # string attribute has unlimited range of possible values. @@ -252,7 +304,7 @@ def instance_from(instance_or_values, weight:) # the attribute before creating the instance data.map!.with_index do |value, index| if value == -1 && attribute(index).string? - attribute(index).add_string_value(instance_or_values[index].to_s) + attribute(index).add_string_value(instance_or_values[index]) else value end @@ -266,6 +318,38 @@ def map_attribute_type(type) return -1 unless Attribute::TYPES.include?(type.downcase.to_sym) Attribute.const_get(type.upcase) end + + # Map a hash whose keys are attribute names and values are attribute + # values into an array containing attribute values in the order + # of the Instances attributes. + # + # @param [Hash] attribute_values a hash whose keys are attribute + # names and values are attribute values. + # + # @return [Array] an array containing attribute values in the + # correct order + def attribute_values_from_hash(attribute_values) + attribute_names(true).inject([]) do |values, attribute_name| + values << attribute_values[attribute_name] + end + end + + # Map an array of attribute values in the same order as Instances + # attributes into a hash whose keys are attribute names and values + # are corresponding attribute values. + # + # @param [Array] attribute_values an array containing the attribute + # values + # + # @return [Hash] a hash as described above + def attribute_values_to_hash(attribute_values) + # TODO: make this pretty + hash = {} + attribute_names(true).each_with_index do |attribute_name, index| + hash[attribute_name] = attribute_values[index] + end + hash + end end Java::WekaCore::Instances.__persistent__ = true diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 35e4e0b..dc4a32e 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -436,31 +436,63 @@ end describe '#add_instance' do - it 'adds an instance from given values to the Instances object' do - data = [:sunny, 70, 80, 'TRUE', :yes] - subject.add_instance(data) - expect(subject.instances.last.to_s).to eq data.join(',') - end + context 'when passing an array of attribute values' do + it 'adds an instance from given values to the Instances object' do + data = [:sunny, 70, 80, 'TRUE', :yes] + subject.add_instance(data) - it 'adds a given instance to the Instances object' do - data = subject.first - subject.add_instance(data) + expect(subject.instances.last.to_s).to eq data.join(',') + end - expect(subject.instances.last.to_s).to eq data.to_s - end + it 'adds a given instance to the Instances object' do + data = subject.first + subject.add_instance(data) - it 'adds a given instance with only missing values' do - data = Weka::Core::DenseInstance.new(subject.size) - subject.add_instance(data) - expect(subject.instances.last.to_s).to eq data.to_s + expect(subject.instances.last.to_s).to eq data.to_s + end + + it 'adds a given instance with only missing values' do + data = Weka::Core::DenseInstance.new(subject.size) + subject.add_instance(data) + expect(subject.instances.last.to_s).to eq data.to_s + end + + it 'adds a given instance with partly missing values' do + data = [:sunny, 70, nil, '?', Float::NAN] + subject.add_instance(data) + + expect(subject.instances.last.to_s).to eq 'sunny,70,?,?,?' + end end - it 'adds a given instance with partly missing values' do - data = [:sunny, 70, nil, '?', Float::NAN] - subject.add_instance(data) + context 'when passing an hash of attribute values' do + it 'adds an instance from given values to the Instances object' do + data = { + outlook: :sunny, + temperature: 70, + humidity: 80, + windy: 'TRUE', + play: :yes + } + subject.add_instance(data) + + expect(subject.instances.last.to_s).to eq data.values.join(',') + end + + it 'adds a given instance with partly missing values' do + data = { + outlook: :sunny, + temperature: 70, + humidity: nil, + windy: '?', + play: Float::NAN + } + subject.add_instance(data) + + expect(subject.instances.last.to_s).to eq 'sunny,70,?,?,?' + end - expect(subject.instances.last.to_s).to eq 'sunny,70,?,?,?' end end @@ -482,11 +514,36 @@ end describe '#internal_values_of' do - it 'returns the internal values of the given values' do - values = [:sunny, 85, 85, :FALSE, :no] - internal_values = [0, 85.0, 85.0, 1, 1] - expect(subject.internal_values_of(values)).to eq internal_values + context 'when passing an array' do + it 'returns an array of internal values of the given values' do + values = [:sunny, 85, 85, :FALSE, :no] + internal_values = [0, 85.0, 85.0, 1, 1] + + expect(subject.internal_values_of(values)).to eq internal_values + end + end + + context 'when passing a hash' do + it 'returns a hash of internal values of the given values' do + values = { + outlook: :sunny, + temperature: 85, + humidity: 85, + windy: :FALSE, + play: :no + } + + internal_values = { + outlook: 0, + temperature: 85.0, + humidity: 85.0, + windy: 1, + play: 1 + } + + expect(subject.internal_values_of(values)).to eq internal_values + end end end From 7a2b732b308496e06d05a1c02ff97ccada96ce6e Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 04:31:51 +0100 Subject: [PATCH 02/25] Instances: 1) refactor #attribute_values_to_hash 2) rewrite comment --- lib/weka/core/instances.rb | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index ae5d13d..5cd55f4 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -55,7 +55,7 @@ def attributes def attribute_names(use_symbol = false) if use_symbol - attributes.map(&:name).map(&:to_sym) + attributes.map { |attribute| attribute.name.to_sym } else attributes.map(&:name) end @@ -195,19 +195,21 @@ def class_attribute_defined? # Add new instance # @param [Instance, Array, Hash] instance_or_values the attribute values - # of the instance to be added + # of the instance to be added. If passing an array, the attribute values + # must be in the same order as the attributes defined in Instances. + # If passing a hash, The keys are the names of the attributes and their + # values are corresponding attributes values. # # @example Passing Instance # instances.add_instance(instance) # - # @example Passing an array of attribute values. Note that the attribute - # values must be in the same order as the Instances attributes. - # instances.add_instance([attr1_value, attr2_value, attr3_value, ...]) + # @example Passing an array of attribute values + # attribute_values = [attr1_value, attr2_value, attr3_value, ...] + # instances.add_instance(attribute_values) # - # @example Passing a hash of attribute values. The keys are the names of - # the attributes and their values are corresponding attribute values. - # hash = { attr1_name: attr1_value, attr2_name: attr2_value, ... } - # instances.add_instance(hash) + # @example Passing a hash of attribute values. + # attribute_values = { attr1_name: attr1_value, attr2_name: attr2_value, ... } + # instances.add_instance(attribute_values) # def add_instance(instance_or_values, weight: 1.0) instance = instance_from(instance_or_values, weight: weight) @@ -319,7 +321,7 @@ def map_attribute_type(type) Attribute.const_get(type.upcase) end - # Map a hash whose keys are attribute names and values are attribute + # convert a hash whose keys are attribute names and values are attribute # values into an array containing attribute values in the order # of the Instances attributes. # @@ -334,7 +336,7 @@ def attribute_values_from_hash(attribute_values) end end - # Map an array of attribute values in the same order as Instances + # convert an array of attribute values in the same order as Instances # attributes into a hash whose keys are attribute names and values # are corresponding attribute values. # @@ -343,12 +345,10 @@ def attribute_values_from_hash(attribute_values) # # @return [Hash] a hash as described above def attribute_values_to_hash(attribute_values) - # TODO: make this pretty - hash = {} - attribute_names(true).each_with_index do |attribute_name, index| - hash[attribute_name] = attribute_values[index] + attribute_names(true).each_with_index.inject({}) do |hash, (attr_name, index)| + hash[attr_name] = attribute_values[index] + hash end - hash end end From 9c76d642b01c12e142baa05c57c2fffd9913557f Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 04:59:09 +0100 Subject: [PATCH 03/25] Instances: minor changes to please rubocop --- lib/weka/core/instances.rb | 39 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 5cd55f4..500d852 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -204,12 +204,12 @@ def class_attribute_defined? # instances.add_instance(instance) # # @example Passing an array of attribute values - # attribute_values = [attr1_value, attr2_value, attr3_value, ...] - # instances.add_instance(attribute_values) + # attr_values = [attr1_value, attr2_value, attr3_value] + # instances.add_instance(attr_values) # # @example Passing a hash of attribute values. - # attribute_values = { attr1_name: attr1_value, attr2_name: attr2_value, ... } - # instances.add_instance(attribute_values) + # attr_values = { attr1_name: attr1_value, attr2_name: attr2_value } + # instances.add_instance(attr_values) # def add_instance(instance_or_values, weight: 1.0) instance = instance_from(instance_or_values, weight: weight) @@ -289,29 +289,16 @@ def attribute_with_name(name) # @return [Instance] the object that contains the given # attribute values. def instance_from(instance_or_values, weight:) - if instance_or_values.is_a?(Java::WekaCore::Instance) instance_or_values.weight = weight instance_or_values else - if instance_or_values.is_a?(Hash) instance_or_values = attribute_values_from_hash(instance_or_values) end data = internal_values_of(instance_or_values) - - # string attribute has unlimited range of possible values. - # Check the return index, if it is -1 then add the value to - # the attribute before creating the instance - data.map!.with_index do |value, index| - if value == -1 && attribute(index).string? - attribute(index).add_string_value(instance_or_values[index]) - else - value - end - end - + data = check_string_attributes(data, instance_or_values) DenseInstance.new(data, weight: weight) end end @@ -345,9 +332,21 @@ def attribute_values_from_hash(attribute_values) # # @return [Hash] a hash as described above def attribute_values_to_hash(attribute_values) - attribute_names(true).each_with_index.inject({}) do |hash, (attr_name, index)| + attribute_names(true).each_with_object({}).with_index do |(attr_name, hash), index| hash[attr_name] = attribute_values[index] - hash + end + end + + def check_string_attributes(internal_values, attribute_values) + # string attribute has unlimited range of possible values. + # Check the return index, if it is -1 then add the value to + # the attribute before creating the instance + internal_values.map!.with_index do |value, index| + if value == -1 && attribute(index).string? + attribute(index).add_string_value(attribute_values[index]) + else + value + end end end end From 5a4d47a2901ab4c69ae3fab7eb0fb8d5b3bab4b9 Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 05:25:32 +0100 Subject: [PATCH 04/25] Instances: add more tests --- lib/weka/core/instances.rb | 4 ++-- spec/core/instances_spec.rb | 33 ++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 500d852..cf479a3 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -308,7 +308,7 @@ def map_attribute_type(type) Attribute.const_get(type.upcase) end - # convert a hash whose keys are attribute names and values are attribute + # Convert a hash whose keys are attribute names and values are attribute # values into an array containing attribute values in the order # of the Instances attributes. # @@ -323,7 +323,7 @@ def attribute_values_from_hash(attribute_values) end end - # convert an array of attribute values in the same order as Instances + # Convert an array of attribute values in the same order as Instances # attributes into a hash whose keys are attribute names and values # are corresponding attribute values. # diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index dc4a32e..d3466d2 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -145,10 +145,17 @@ end describe '#attribute_names' do + names = %w(outlook temperature humidity windy play) + it 'returns an Array of the attribute names' do - names = %w(outlook temperature humidity windy play) expect(subject.attribute_names).to eq names end + + context 'if use_symbol is true' do + it 'returns an Array of the symbols of the attribute names' do + expect(subject.attribute_names(true)).to eq names.map(&:to_sym) + end + end end describe 'attribute definers:' do @@ -501,10 +508,26 @@ [[:sunny, 70, 80, :TRUE, :yes], [:overcast, 80, 85, :FALSE, :yes]] end - it 'adds the data to the Instances object' do - expect { subject.add_instances(data) } - .to change { subject.instances_count } - .by(data.count) + context 'when passing array of attribute values' do + it 'adds the data to the Instances object' do + expect { subject.add_instances(data) } + .to change { subject.instances_count } + .by(data.count) + end + end + + context 'when passing array of attribute values' do + let(:hash_data) do + data.map do |attribute_values| + subject.send(:attribute_values_to_hash, attribute_values) + end + end + + it 'adds the data to the Instances object' do + expect { subject.add_instances(hash_data) } + .to change { subject.instances_count } + .by(hash_data.count) + end end it 'calls #add_instance internally' do From ff3bc7f265fe768dc954ee5937d3d8f7f8e8ed2f Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 05:37:13 +0100 Subject: [PATCH 05/25] Instances: skip check_string_attributes if none exists --- lib/weka/core/instances.rb | 2 +- spec/core/instances_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index cf479a3..0220352 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -298,7 +298,7 @@ def instance_from(instance_or_values, weight:) end data = internal_values_of(instance_or_values) - data = check_string_attributes(data, instance_or_values) + check_string_attributes(data, instance_or_values) if has_string_attribute? DenseInstance.new(data, weight: weight) end end diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index d3466d2..ab36d79 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -473,7 +473,7 @@ end end - context 'when passing an hash of attribute values' do + context 'when passing a hash of attribute values' do it 'adds an instance from given values to the Instances object' do data = { outlook: :sunny, From b259d70f55fc94a28bbb4629bf2b7a78eb333fae Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 05:42:27 +0100 Subject: [PATCH 06/25] Instances: make the behavior of check_string_attributes less surprising --- lib/weka/core/instances.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 0220352..54ce17f 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -298,7 +298,7 @@ def instance_from(instance_or_values, weight:) end data = internal_values_of(instance_or_values) - check_string_attributes(data, instance_or_values) if has_string_attribute? + data = check_string_attributes(data, instance_or_values) if has_string_attribute? DenseInstance.new(data, weight: weight) end end @@ -341,7 +341,7 @@ def check_string_attributes(internal_values, attribute_values) # string attribute has unlimited range of possible values. # Check the return index, if it is -1 then add the value to # the attribute before creating the instance - internal_values.map!.with_index do |value, index| + internal_values.map.with_index do |value, index| if value == -1 && attribute(index).string? attribute(index).add_string_value(attribute_values[index]) else From fe73dea7a3b964b99d3bb1f832e264a66ce9f527 Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 05:50:09 +0100 Subject: [PATCH 07/25] Instances: fix comment in spec --- spec/core/instances_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index ab36d79..9fbd791 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -508,7 +508,7 @@ [[:sunny, 70, 80, :TRUE, :yes], [:overcast, 80, 85, :FALSE, :yes]] end - context 'when passing array of attribute values' do + context 'when each instance is stored as an array of attribute values' do it 'adds the data to the Instances object' do expect { subject.add_instances(data) } .to change { subject.instances_count } @@ -516,7 +516,7 @@ end end - context 'when passing array of attribute values' do + context 'when each instance is stored as a hash of attribute values' do let(:hash_data) do data.map do |attribute_values| subject.send(:attribute_values_to_hash, attribute_values) From 21c5d0ce3f7ad5c0a871f7aa1cbcae74aa108800 Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 11:04:00 +0100 Subject: [PATCH 08/25] Instances: bug fix One of the Java method of Instances has unexpected behavior. enumerateAttributes will skip the class attribute once it is set. Therfore we have to manually add the class attribute, otherwise we we will miss it in every method that calls Instances#attributes. See also: http://weka.sourceforge.net/doc.stable/weka/core/Instances.html#enumerateAttributes-- --- lib/weka/core/instances.rb | 8 ++- spec/core/instances_spec.rb | 102 +++++++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 54ce17f..5df9e57 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -318,7 +318,9 @@ def map_attribute_type(type) # @return [Array] an array containing attribute values in the # correct order def attribute_values_from_hash(attribute_values) - attribute_names(true).inject([]) do |values, attribute_name| + names = attribute_names(true) + names << class_attribute.name.to_sym if class_attribute_defined? + names.inject([]) do |values, attribute_name| values << attribute_values[attribute_name] end end @@ -332,7 +334,9 @@ def attribute_values_from_hash(attribute_values) # # @return [Hash] a hash as described above def attribute_values_to_hash(attribute_values) - attribute_names(true).each_with_object({}).with_index do |(attr_name, hash), index| + names = attribute_names(true) + names << class_attribute.name.to_sym if class_attribute_defined? + names.each_with_object({}).with_index do |(attr_name, hash), index| hash[attr_name] = attribute_values[index] end end diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 9fbd791..5580c1b 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -156,6 +156,18 @@ expect(subject.attribute_names(true)).to eq names.map(&:to_sym) end end + + context 'if class attribute is set' do + subject do + instances = load_instances('weather.arff') + instances.class_attribute = :play + instances + end + + it 'does not return the class attribute' do + expect(subject.attribute_names).to eq(names.reject { |n| n == 'play' }) + end + end end describe 'attribute definers:' do @@ -474,32 +486,48 @@ end context 'when passing a hash of attribute values' do - it 'adds an instance from given values to the Instances object' do - data = { + let(:data) do + { outlook: :sunny, temperature: 70, - humidity: 80, - windy: 'TRUE', - play: :yes + humidity: humidity_value, + windy: windy_value, + play: play_value } - subject.add_instance(data) + end + + let(:humidity_value) { 80 } + let(:windy_value) { 'TRUE' } + let(:play_value) { :yes } + it 'adds an instance from given values to the Instances object' do + subject.add_instance(data) expect(subject.instances.last.to_s).to eq data.values.join(',') end - it 'adds a given instance with partly missing values' do - data = { - outlook: :sunny, - temperature: 70, - humidity: nil, - windy: '?', - play: Float::NAN - } - subject.add_instance(data) + context 'when some attribute values are missing' do + let(:humidity_value) { nil } + let(:windy_value) { '?' } + let(:play_value) { Float::NAN } - expect(subject.instances.last.to_s).to eq 'sunny,70,?,?,?' + it 'adds a given instance with partly missing values' do + subject.add_instance(data) + expect(subject.instances.last.to_s).to eq 'sunny,70,?,?,?' + end end + context 'when class attribute is set' do + subject do + instances = load_instances('weather.arff') + instances.class_attribute = :play + instances + end + + it 'adds a given instance to the Instances object' do + subject.add_instance(data) + expect(subject.instances.last.to_s).to eq data.values.join(',') + end + end end end @@ -548,24 +576,36 @@ end context 'when passing a hash' do + values = { + outlook: :sunny, + temperature: 85, + humidity: 85, + windy: :FALSE, + play: :no + } + + internal_values = { + outlook: 0, + temperature: 85.0, + humidity: 85.0, + windy: 1, + play: 1 + } + it 'returns a hash of internal values of the given values' do - values = { - outlook: :sunny, - temperature: 85, - humidity: 85, - windy: :FALSE, - play: :no - } + expect(subject.internal_values_of(values)).to eq internal_values + end - internal_values = { - outlook: 0, - temperature: 85.0, - humidity: 85.0, - windy: 1, - play: 1 - } + context 'when class attribute is set' do + subject do + instances = load_instances('weather.arff') + instances.class_attribute = :play + instances + end - expect(subject.internal_values_of(values)).to eq internal_values + it 'return the index of the class attribute' do + expect(subject.internal_values_of(values)).to eq internal_values + end end end end From f7995fcd2ee3e9097ce3783c5618b79ef15a45af Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Fri, 23 Dec 2016 12:44:05 +0100 Subject: [PATCH 09/25] Instances: bug fix 1) The previous fix only works when class attribute is the last attribute. The bug fix address that problem. 2) use attribute in the middle as the class attribute for the test cases in the spec. --- lib/weka/core/instances.rb | 22 ++++++++++------------ spec/core/instances_spec.rb | 14 ++++---------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 5df9e57..6d1e271 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -49,16 +49,16 @@ def instances enumerate_instances.to_a end - def attributes - enumerate_attributes.to_a + def attributes(include_class_attribute = false) + attrs = enumerate_attributes.to_a + if include_class_attribute && class_attribute_defined? + attrs.insert(class_index, class_attribute) + end + attrs end - def attribute_names(use_symbol = false) - if use_symbol - attributes.map { |attribute| attribute.name.to_sym } - else - attributes.map(&:name) - end + def attribute_names(include_class_attribute = false) + attributes(include_class_attribute).map(&:name) end def add_attributes(&block) @@ -318,8 +318,7 @@ def map_attribute_type(type) # @return [Array] an array containing attribute values in the # correct order def attribute_values_from_hash(attribute_values) - names = attribute_names(true) - names << class_attribute.name.to_sym if class_attribute_defined? + names = attribute_names(true).map!(&:to_sym) names.inject([]) do |values, attribute_name| values << attribute_values[attribute_name] end @@ -334,8 +333,7 @@ def attribute_values_from_hash(attribute_values) # # @return [Hash] a hash as described above def attribute_values_to_hash(attribute_values) - names = attribute_names(true) - names << class_attribute.name.to_sym if class_attribute_defined? + names = attribute_names(true).map!(&:to_sym) names.each_with_object({}).with_index do |(attr_name, hash), index| hash[attr_name] = attribute_values[index] end diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 5580c1b..c4d5b1f 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -151,21 +151,15 @@ expect(subject.attribute_names).to eq names end - context 'if use_symbol is true' do - it 'returns an Array of the symbols of the attribute names' do - expect(subject.attribute_names(true)).to eq names.map(&:to_sym) - end - end - context 'if class attribute is set' do subject do instances = load_instances('weather.arff') - instances.class_attribute = :play + instances.class_attribute = :windy instances end it 'does not return the class attribute' do - expect(subject.attribute_names).to eq(names.reject { |n| n == 'play' }) + expect(subject.attribute_names).to eq(names.reject { |n| n == 'windy' }) end end end @@ -519,7 +513,7 @@ context 'when class attribute is set' do subject do instances = load_instances('weather.arff') - instances.class_attribute = :play + instances.class_attribute = :windy instances end @@ -599,7 +593,7 @@ context 'when class attribute is set' do subject do instances = load_instances('weather.arff') - instances.class_attribute = :play + instances.class_attribute = :windy instances end From 143944b08c4634fbe2e9d85824e739f06bda96f8 Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Sat, 24 Dec 2016 02:29:29 +0100 Subject: [PATCH 10/25] Instances: 1) use keyword argument for #attributes and #attribute_names 2) fix #ensure_attribute_defined! and #attribute_with_name 3) refactor private methods #attribute_values_to_hash and #attribute_values_from_hash 4) add new test cases and refactor the spec --- lib/weka/core/instances.rb | 39 ++++++----- spec/core/instances_spec.rb | 127 ++++++++++++++++++++++++------------ 2 files changed, 106 insertions(+), 60 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index 6d1e271..daad591 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -49,16 +49,18 @@ def instances enumerate_instances.to_a end - def attributes(include_class_attribute = false) + def attributes(include_class_attribute: false) attrs = enumerate_attributes.to_a + if include_class_attribute && class_attribute_defined? attrs.insert(class_index, class_attribute) end + attrs end - def attribute_names(include_class_attribute = false) - attributes(include_class_attribute).map(&:name) + def attribute_names(include_class_attribute: false) + attributes(include_class_attribute: include_class_attribute).map(&:name) end def add_attributes(&block) @@ -232,7 +234,7 @@ def internal_values_of(values) use_hash = values.is_a?(Hash) values = attribute_values_from_hash(values) if use_hash - values = values.each_with_index.map do |value, index| + values = values.map.with_index do |value, index| attribute(index).internal_value_of(value) end @@ -263,7 +265,9 @@ def add_attribute(attribute) end def ensure_attribute_defined!(name) - return if attribute_names.include?(name.to_s) + if attribute_names(include_class_attribute: true).include?(name.to_s) + return + end error = "\"#{name}\" is not defined." hint = 'Only defined attributes can be used as class attribute!' @@ -273,7 +277,9 @@ def ensure_attribute_defined!(name) end def attribute_with_name(name) - attributes.select { |attribute| attribute.name == name.to_s }.first + attributes(include_class_attribute: true).select do |attribute| + attribute.name == name.to_s + end.first end # Wrap the attribute values for the instance to be added with @@ -312,30 +318,27 @@ def map_attribute_type(type) # values into an array containing attribute values in the order # of the Instances attributes. # - # @param [Hash] attribute_values a hash whose keys are attribute + # @param [Hash] hash a hash whose keys are attribute # names and values are attribute values. # # @return [Array] an array containing attribute values in the # correct order - def attribute_values_from_hash(attribute_values) - names = attribute_names(true).map!(&:to_sym) - names.inject([]) do |values, attribute_name| - values << attribute_values[attribute_name] - end + def attribute_values_from_hash(hash) + names = attribute_names(include_class_attribute: true).map(&:to_sym) + hash.values_at(*names) end # Convert an array of attribute values in the same order as Instances # attributes into a hash whose keys are attribute names and values # are corresponding attribute values. # - # @param [Array] attribute_values an array containing the attribute - # values + # @param [Array] values an array containing the attribute values # # @return [Hash] a hash as described above - def attribute_values_to_hash(attribute_values) - names = attribute_names(true).map!(&:to_sym) - names.each_with_object({}).with_index do |(attr_name, hash), index| - hash[attr_name] = attribute_values[index] + def attribute_values_to_hash(values) + names = attribute_names(include_class_attribute: true).map(&:to_sym) + names.each_with_index.inject({}) do |hash, (name, index)| + hash.update(name => values[index]) end end diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index c4d5b1f..4062122 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -2,7 +2,14 @@ require 'fileutils' describe Weka::Core::Instances do - subject { load_instances('weather.arff') } + let(:set_class_attribute) { false } + let(:class_attribute_name) { :windy } + + subject do + instances = load_instances('weather.arff') + instances.class_attribute = class_attribute_name if set_class_attribute + instances + end it { is_expected.to respond_to :each } it { is_expected.to respond_to :each_with_index } @@ -142,6 +149,27 @@ expect(all_kind_of_attribute).to be true end + + context 'when class attribute is not set' do + it 'returns all attributes' do + expect(subject.attributes.size).to eq subject.attributes_count + expect(subject.attributes(include_class_attribute: true).size) + .to eq subject.attributes_count + end + end + + context 'when class attribute is set' do + let(:set_class_attribute) { true } + + it 'returns all attributes if include_class_attribute is true' do + expect(subject.attributes(include_class_attribute: true).size) + .to eq subject.attributes_count + end + + it 'skips the class attribute if include_class_attribute is false' do + expect(subject.attributes.size).to eq(subject.attributes_count-1) + end + end end describe '#attribute_names' do @@ -151,15 +179,17 @@ expect(subject.attribute_names).to eq names end - context 'if class attribute is set' do - subject do - instances = load_instances('weather.arff') - instances.class_attribute = :windy - instances + context 'when class attribute is set' do + let(:set_class_attribute) { true } + + it 'skips the class attribute if include_class_attribute is false' do + expect(subject.attribute_names) + .not_to include subject.class_attribute.name end - it 'does not return the class attribute' do - expect(subject.attribute_names).to eq(names.reject { |n| n == 'windy' }) + it 'returns all attributes if include_class_attribute is true' do + expect(subject.attribute_names(include_class_attribute: true)) + .to include subject.class_attribute.name end end end @@ -284,6 +314,20 @@ expect { subject.class_attribute = :not_existing_attribute } .to raise_error(ArgumentError) end + + context 'when class attribute is already set' do + let(:set_class_attribute) { true } + + it 'can set the same attribute as class attribute again' do + subject.class_attribute = class_attribute_name + expect(subject.class_attribute.name).to eq class_attribute_name.to_s + end + + it 'can set another attribute as class attribute' do + subject.class_attribute = :play + expect(subject.class_attribute.name).to eq :play.to_s + end + end end describe '#class_attribute' do @@ -336,12 +380,12 @@ it 'adds the numbers of attributes given in the block' do instances = Weka::Core::Instances.new - expect { + expect do instances.add_attributes do numeric 'attribute' nominal 'class', values: %w(YES NO) end - }.to change { instances.attributes.count }.from(0).to(2) + end.to change { instances.attributes.count }.from(0).to(2) end it 'adds the types of attributes given in the block' do @@ -449,7 +493,6 @@ end describe '#add_instance' do - context 'when passing an array of attribute values' do it 'adds an instance from given values to the Instances object' do data = [:sunny, 70, 80, 'TRUE', :yes] @@ -480,6 +523,10 @@ end context 'when passing a hash of attribute values' do + let(:humidity_value) { 80 } + let(:windy_value) { 'TRUE' } + let(:play_value) { :yes } + let(:data) do { outlook: :sunny, @@ -490,10 +537,6 @@ } end - let(:humidity_value) { 80 } - let(:windy_value) { 'TRUE' } - let(:play_value) { :yes } - it 'adds an instance from given values to the Instances object' do subject.add_instance(data) expect(subject.instances.last.to_s).to eq data.values.join(',') @@ -511,11 +554,7 @@ end context 'when class attribute is set' do - subject do - instances = load_instances('weather.arff') - instances.class_attribute = :windy - instances - end + let(:set_class_attribute) { true } it 'adds a given instance to the Instances object' do subject.add_instance(data) @@ -540,8 +579,13 @@ context 'when each instance is stored as a hash of attribute values' do let(:hash_data) do + names = subject.attribute_names(include_class_attribute: true) + .map(&:to_sym) + data.map do |attribute_values| - subject.send(:attribute_values_to_hash, attribute_values) + names.each_with_index.inject({}) do |hash, (name, index)| + hash.update(name => attribute_values[index]) + end end end @@ -559,7 +603,6 @@ end describe '#internal_values_of' do - context 'when passing an array' do it 'returns an array of internal values of the given values' do values = [:sunny, 85, 85, :FALSE, :no] @@ -570,32 +613,32 @@ end context 'when passing a hash' do - values = { - outlook: :sunny, - temperature: 85, - humidity: 85, - windy: :FALSE, - play: :no - } - - internal_values = { - outlook: 0, - temperature: 85.0, - humidity: 85.0, - windy: 1, - play: 1 - } + let(:values) do + { + outlook: :sunny, + temperature: 85, + humidity: 85, + windy: :FALSE, + play: :no + } + end + + let(:internal_values) do + { + outlook: 0, + temperature: 85.0, + humidity: 85.0, + windy: 1, + play: 1 + } + end it 'returns a hash of internal values of the given values' do expect(subject.internal_values_of(values)).to eq internal_values end context 'when class attribute is set' do - subject do - instances = load_instances('weather.arff') - instances.class_attribute = :windy - instances - end + let(:set_class_attribute) { true } it 'return the index of the class attribute' do expect(subject.internal_values_of(values)).to eq internal_values From bc8b4728b88ce358e60abeaaef32b0189e17126b Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Sat, 24 Dec 2016 02:45:12 +0100 Subject: [PATCH 11/25] Instances: refactor #attribute_with_name --- lib/weka/core/instances.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index daad591..c8aaa9c 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -277,9 +277,9 @@ def ensure_attribute_defined!(name) end def attribute_with_name(name) - attributes(include_class_attribute: true).select do |attribute| + attributes(include_class_attribute: true).find do |attribute| attribute.name == name.to_s - end.first + end end # Wrap the attribute values for the instance to be added with From ede252dbb949206ffcedcb0881a56f72590375ed Mon Sep 17 00:00:00 2001 From: Kai-Chun Ning Date: Sun, 25 Dec 2016 04:52:04 +0100 Subject: [PATCH 12/25] Instances: fix comment in the spec --- spec/core/instances_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 4062122..1b5fe00 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -640,7 +640,7 @@ context 'when class attribute is set' do let(:set_class_attribute) { true } - it 'return the index of the class attribute' do + it 'returns a hash of internal values of the given values' do expect(subject.internal_values_of(values)).to eq internal_values end end From 915d2f31e9d91c7920d964fd17bbf24e380dd590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Fri, 3 Mar 2017 18:03:16 +0100 Subject: [PATCH 13/25] Cleanup core/instances spec --- spec/core/instances_spec.rb | 46 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 1b5fe00..24e3db7 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -2,14 +2,9 @@ require 'fileutils' describe Weka::Core::Instances do - let(:set_class_attribute) { false } - let(:class_attribute_name) { :windy } + let(:class_attribute_name) { :windy } - subject do - instances = load_instances('weather.arff') - instances.class_attribute = class_attribute_name if set_class_attribute - instances - end + subject { load_instances('weather.arff') } it { is_expected.to respond_to :each } it { is_expected.to respond_to :each_with_index } @@ -159,7 +154,7 @@ end context 'when class attribute is set' do - let(:set_class_attribute) { true } + before { subject.class_attribute = class_attribute_name } it 'returns all attributes if include_class_attribute is true' do expect(subject.attributes(include_class_attribute: true).size) @@ -180,7 +175,7 @@ end context 'when class attribute is set' do - let(:set_class_attribute) { true } + before { subject.class_attribute = class_attribute_name } it 'skips the class attribute if include_class_attribute is false' do expect(subject.attribute_names) @@ -316,7 +311,7 @@ end context 'when class attribute is already set' do - let(:set_class_attribute) { true } + before { subject.class_attribute = class_attribute_name } it 'can set the same attribute as class attribute again' do subject.class_attribute = class_attribute_name @@ -443,7 +438,7 @@ describe '#each_with_index' do it 'runs a block on each instance' do subject.each_with_index do |instance, index| - @result = "#{instance.value(0)}, #{index}" if index == 0 + @result = "#{instance.value(0)}, #{index}" if index.zero? end expect(@result).to eq '0.0, 0' # 0.0 => index of nominal value @@ -477,7 +472,7 @@ describe '#each_attribute_with_index' do it 'runs a block on each attribute' do subject.each_attribute_with_index do |attribute, index| - @result = "#{attribute.name}, #{index}" if index == 0 + @result = "#{attribute.name}, #{index}" if index.zero? end expect(@result).to eq 'outlook, 0' @@ -523,9 +518,9 @@ end context 'when passing a hash of attribute values' do - let(:humidity_value) { 80 } - let(:windy_value) { 'TRUE' } - let(:play_value) { :yes } + let(:humidity_value) { 80 } + let(:windy_value) { 'TRUE' } + let(:play_value) { :yes } let(:data) do { @@ -543,9 +538,9 @@ end context 'when some attribute values are missing' do - let(:humidity_value) { nil } - let(:windy_value) { '?' } - let(:play_value) { Float::NAN } + let(:humidity_value) { nil } + let(:windy_value) { '?' } + let(:play_value) { Float::NAN } it 'adds a given instance with partly missing values' do subject.add_instance(data) @@ -554,7 +549,7 @@ end context 'when class attribute is set' do - let(:set_class_attribute) { true } + before { subject.class_attribute = class_attribute_name } it 'adds a given instance to the Instances object' do subject.add_instance(data) @@ -579,8 +574,9 @@ context 'when each instance is stored as a hash of attribute values' do let(:hash_data) do - names = subject.attribute_names(include_class_attribute: true) - .map(&:to_sym) + names = subject + .attribute_names(include_class_attribute: true) + .map(&:to_sym) data.map do |attribute_values| names.each_with_index.inject({}) do |hash, (name, index)| @@ -638,7 +634,7 @@ end context 'when class attribute is set' do - let(:set_class_attribute) { true } + before { subject.class_attribute = class_attribute_name } it 'returns a hash of internal values of the given values' do expect(subject.internal_values_of(values)).to eq internal_values @@ -728,8 +724,8 @@ end describe '#has_attribute_type?' do - subject { load_instances('weather.string.arff') } - let(:type) { 'nominal' } + subject { load_instances('weather.string.arff') } + let(:type) { 'nominal' } it 'calls the underlying Java method .check_for_attribute_type' do expect(subject) @@ -804,7 +800,7 @@ end it 'returns false for undefined attribute type' do - expect(subject.has_attribute_type?(1000)).to be false + expect(subject.has_attribute_type?(-1)).to be false end end end From b7ab5e29c996b28b48105cefd20905771a15fd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 3 Jun 2017 15:35:34 +0200 Subject: [PATCH 14/25] Include Persistent concern in Core::Instances --- lib/weka/core/instances.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index c8aaa9c..dff1e20 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -10,6 +10,7 @@ module Core java_import 'weka.core.FastVector' class Instances + include Weka::Concerns::Persistent include Weka::Concerns::Serializable DEFAULT_RELATION_NAME = 'Instances'.freeze @@ -355,7 +356,5 @@ def check_string_attributes(internal_values, attribute_values) end end end - - Java::WekaCore::Instances.__persistent__ = true end end From 6f05bd3bfd30c033a8676552250fde2e372437ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 3 Jun 2017 15:36:47 +0200 Subject: [PATCH 15/25] Add to_m to Core::Instances to_m returns a Matrix object holding all instances's values as rows. --- lib/weka/core/instances.rb | 8 ++++++++ spec/core/instances_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index dff1e20..d20b730 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -1,3 +1,4 @@ +require 'matrix' require 'weka/core/converters' require 'weka/core/loader' require 'weka/core/saver' @@ -259,6 +260,13 @@ def merge(*instances) end end + # Get the all instances's values as Matrix. + # + # @return [Matrix] a Matrix holding the instance's values as rows. + def to_m + Matrix[*instances.map(&:values)] + end + private def add_attribute(attribute) diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 24e3db7..47991bf 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'fileutils' +require 'matrix' describe Weka::Core::Instances do let(:class_attribute_name) { :windy } @@ -34,6 +35,7 @@ it { is_expected.to respond_to :reset_class_attribute } it { is_expected.to respond_to :serialize } + it { is_expected.to respond_to :to_m } describe 'aliases:' do let(:instances) { described_class.new } @@ -804,4 +806,29 @@ end end end + + describe '#to_m' do + subject { load_instances('weather.arff') } + + it 'returns a matrix of all instance values' do + matrix = Matrix[ + ['sunny', 85.0, 85.0, 'FALSE', 'no'], + ['sunny', 80.0, 90.0, 'TRUE', 'no'], + ['overcast', 83.0, 86.0, 'FALSE', 'yes'], + ['rainy', 70.0, 96.0, 'FALSE', 'yes'], + ['rainy', 68.0, 80.0, 'FALSE', 'yes'], + ['rainy', 65.0, 70.0, 'TRUE', 'no'], + ['overcast', 64.0, 65.0, 'TRUE', 'yes'], + ['sunny', 72.0, 95.0, 'FALSE', 'no'], + ['sunny', 69.0, 70.0, 'FALSE', 'yes'], + ['rainy', 75.0, 80.0, 'FALSE', 'yes'], + ['sunny', 75.0, 70.0, 'TRUE', 'yes'], + ['overcast', 72.0, 90.0, 'TRUE', 'yes'], + ['overcast', 81.0, 75.0, 'FALSE', 'yes'], + ['rainy', 71.0, 91.0, 'TRUE', 'no'] + ] + + expect(subject.to_m).to eq matrix + end + end end From d289d958f2914c284e8f668369adbe28fee1fd3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 3 Jun 2017 15:39:49 +0200 Subject: [PATCH 16/25] Remove not used block argument from Core::Instances#initialize --- lib/weka/core/instances.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index d20b730..ed18200 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -40,7 +40,7 @@ def from_c45(file) end end - def initialize(relation_name: DEFAULT_RELATION_NAME, attributes: [], &block) + def initialize(relation_name: DEFAULT_RELATION_NAME, attributes: []) attribute_list = FastVector.new attributes.each { |attribute| attribute_list.add_element(attribute) } From 5f1cc11011482efdfcfdf65dd482174973d6bf3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Fri, 16 Jun 2017 13:57:57 +0200 Subject: [PATCH 17/25] Add Weka::Classifiers::Evaluation curve classes --- lib/weka/classifiers/evaluation.rb | 11 +++++++++-- spec/classifiers/evaluation_spec.rb | 12 ++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/weka/classifiers/evaluation.rb b/lib/weka/classifiers/evaluation.rb index f8cfa2d..667cc2c 100644 --- a/lib/weka/classifiers/evaluation.rb +++ b/lib/weka/classifiers/evaluation.rb @@ -1,8 +1,12 @@ +require 'weka/class_builder' + module Weka module Classifiers java_import 'weka.classifiers.Evaluation' class Evaluation + include ClassBuilder + # Use both nomenclatures f_measure and fmeasure for consistency # due to jruby's auto method generation of 'fMeasure' to 'f_measure' and # 'weightedFMeasure' to 'weighted_fmeasure'. @@ -29,8 +33,11 @@ class Evaluation alias average_cost avg_cost alias cumulative_margin_distribution to_cumulative_margin_distribution_string - end - Java::WekaClassifiers::Evaluation.__persistent__ = true + build_classes :CostCurve, + :MarginCurve, + :ThresholdCurve, + weka_module: 'weka.classifiers.evaluation' + end end end diff --git a/spec/classifiers/evaluation_spec.rb b/spec/classifiers/evaluation_spec.rb index 601b053..ff4347e 100644 --- a/spec/classifiers/evaluation_spec.rb +++ b/spec/classifiers/evaluation_spec.rb @@ -35,4 +35,16 @@ end end end + + it_behaves_like 'class builder' + + %i[ + CostCurve + MarginCurve + ThresholdCurve + ].each do |class_name| + it "defines a class #{class_name}" do + expect(described_class.const_defined?(class_name)).to be true + end + end end From 5564b12933c1d06270ffab4c8c9e19ea71502f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Fri, 16 Jun 2017 14:31:41 +0200 Subject: [PATCH 18/25] Allow including additional modules on class building --- lib/weka/class_builder.rb | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/weka/class_builder.rb b/lib/weka/class_builder.rb index a842b36..811300c 100644 --- a/lib/weka/class_builder.rb +++ b/lib/weka/class_builder.rb @@ -7,14 +7,24 @@ def self.included(base) end module ClassMethods - def build_class(class_name, weka_module: nil, include_concerns: true) + def build_class(class_name, weka_module: nil, include_concerns: true, additional_includes: []) java_import java_class_path(class_name, weka_module) - define_class(class_name, weka_module, include_concerns: include_concerns) + define_class( + class_name, + weka_module, + include_concerns: include_concerns, + additional_includes: additional_includes + ) end - def build_classes(*class_names, weka_module: nil, include_concerns: true) + def build_classes(*class_names, weka_module: nil, include_concerns: true, additional_includes: []) class_names.each do |name| - build_class(name, weka_module: weka_module, include_concerns: include_concerns) + build_class( + name, + weka_module: weka_module, + include_concerns: include_concerns, + additional_includes: additional_includes + ) end end @@ -58,12 +68,13 @@ def toplevel_module? name.scan('::').count == 1 end - def define_class(class_name, weka_module, include_concerns: true) + def define_class(class_name, weka_module, include_concerns: true, additional_includes: []) module_eval <<-CLASS_DEFINITION, __FILE__, __LINE__ + 1 class #{class_name} #{'include Concerns' if include_concerns} #{include_serializable_for(class_name, weka_module)} #{include_utils} + #{include_additionals(additional_includes)} end CLASS_DEFINITION end @@ -84,6 +95,13 @@ def utils_defined? constantize(utils_super_modules).const_defined?(:Utils) end + def include_additionals(modules) + modules = Array(modules) + return if modules.empty? + + modules.map { |name| "include #{name}" }.join("\n") + end + def constantize(module_names) Object.module_eval("::#{module_names}") end From 317077d9dfe64de3adeb6aee3fc4e5708766c291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Fri, 16 Jun 2017 15:01:32 +0200 Subject: [PATCH 19/25] Add #curve alias to Weka::Classifiers::Evaluation curve classes --- lib/weka/classifiers/evaluation.rb | 11 ++++++- spec/classifiers/evaluation_spec.rb | 47 +++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/weka/classifiers/evaluation.rb b/lib/weka/classifiers/evaluation.rb index 667cc2c..8490577 100644 --- a/lib/weka/classifiers/evaluation.rb +++ b/lib/weka/classifiers/evaluation.rb @@ -34,10 +34,19 @@ class Evaluation alias cumulative_margin_distribution to_cumulative_margin_distribution_string + module Curve + def self.included(base) + base.class_eval do + alias_method :curve, :get_curve + end + end + end + build_classes :CostCurve, :MarginCurve, :ThresholdCurve, - weka_module: 'weka.classifiers.evaluation' + weka_module: 'weka.classifiers.evaluation', + additional_includes: Curve end end end diff --git a/spec/classifiers/evaluation_spec.rb b/spec/classifiers/evaluation_spec.rb index ff4347e..eecb414 100644 --- a/spec/classifiers/evaluation_spec.rb +++ b/spec/classifiers/evaluation_spec.rb @@ -1,12 +1,14 @@ require 'spec_helper' describe Weka::Classifiers::Evaluation do - subject do + let(:instances) do instances = load_instances('weather.arff') instances.class_attribute = :play - Weka::Classifiers::Evaluation.new(instances) + instances end + subject { Weka::Classifiers::Evaluation.new(instances) } + it { is_expected.to be_kind_of(Java::WekaClassifiers::Evaluation) } describe 'aliases:' do @@ -46,5 +48,46 @@ it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true end + + describe "#{class_name}" do + let(:curve) do + Object.module_eval("Weka::Classifiers::Evaluation::#{class_name}").new + end + + it 'responds to #curve' do + expect(curve).to respond_to :curve + end + + describe '#curve' do + let(:classifier) do + classifier = Weka::Classifiers::Bayes::NaiveBayes.new + classifier.train_with_instances(instances) + classifier + end + + context 'without class index' do + it 'returns Instances for predictions' do + evaluation = classifier.cross_validate + curve_instances = curve.curve(evaluation.predictions) + + expect(curve_instances).to be_a Weka::Core::Instances + end + end + + unless class_name == :MarginCurve + context 'with class index' do + it 'returns Instances for predictions' do + evaluation = classifier.cross_validate + + instances.class_attribute.values.each do |value| + class_index = instances.class_attribute.internal_value_of(value) + curve_instances = curve.curve(evaluation.predictions, class_index) + expect(curve_instances).to be_a Weka::Core::Instances + end + end + end + end + end + end end end From 568b691f031a51bfc8d36e313a7983978e18f74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 10:47:54 +0200 Subject: [PATCH 20/25] Bump version to 0.5.0 --- lib/weka/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/weka/version.rb b/lib/weka/version.rb index af7625d..b00e790 100644 --- a/lib/weka/version.rb +++ b/lib/weka/version.rb @@ -1,3 +1,3 @@ module Weka - VERSION = '0.4.0'.freeze + VERSION = '0.5.0'.freeze end From 8ea67b6218f61c23090ddf50acfb18c7f3ac4c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 10:48:17 +0200 Subject: [PATCH 21/25] Add rubocop config --- .rubocop.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..611b10b --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,30 @@ +AllCops: + TargetRubyVersion: 2.4 + Exclude: + - 'bin/**/*' + - '*.gemspec' + - 'Gemfile' + - 'Gemfile.lock' + +Style/Copyright: + Enabled: false + +Style/Documentation: + Enabled: false + +Metrics/LineLength: + Max: 80 + +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented + +Style/FrozenStringLiteralComment: + Enabled: false + +Metrics/ModuleLength: + Exclude: + - "**/*_spec.rb" + +Metrics/BlockLength: + Exclude: + - "**/*_spec.rb" From 54913b62bb321f652005e5a6594c5b917d345edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 14:23:12 +0200 Subject: [PATCH 22/25] Refactor utils This splits up the different method groups into separate modules and makes it a bit more readable and changeable. --- lib/weka/classifiers/utils.rb | 206 +++++++++++++++++++--------------- lib/weka/clusterers/utils.rb | 174 ++++++++++++++++------------ lib/weka/filters/utils.rb | 10 +- 3 files changed, 219 insertions(+), 171 deletions(-) diff --git a/lib/weka/classifiers/utils.rb b/lib/weka/classifiers/utils.rb index 7e8096a..d5dfdfb 100644 --- a/lib/weka/classifiers/utils.rb +++ b/lib/weka/classifiers/utils.rb @@ -5,128 +5,154 @@ module Weka module Classifiers module Utils def self.included(base) - base.class_eval do - java_import 'java.util.Random' + base.include Buildable if base.instance_methods.include?(:build_classifier) + base.include Classifiable if base.instance_methods.include?(:classify_instance) + base.include Updatable if base.instance_methods.include?(:update_classifier) + base.include Distributable if base.instance_methods.include?(:distribution_for_instance) + end - if instance_methods.include?(:build_classifier) - attr_reader :training_instances + module Checks + private - def train_with_instances(instances) - ensure_class_attribute_assigned!(instances) + def ensure_class_attribute_assigned!(instances) + return if instances.class_attribute_defined? - @training_instances = instances - build_classifier(instances) + error = 'Class attribute is not assigned for Instances.' + hint = 'You can assign a class attribute with #class_attribute=.' + message = "#{error} #{hint}" - self - end + raise UnassignedClassError, message + end - def cross_validate(folds: 3) - ensure_trained_with_instances! + def ensure_trained_with_instances! + return unless training_instances.nil? - evaluation = Evaluation.new(training_instances) - random = Java::JavaUtil::Random.new(1) + error = 'Classifier is not trained with Instances.' + hint = 'You can set the training instances with #train_with_instances.' + message = "#{error} #{hint}" - evaluation.cross_validate_model(self, training_instances, folds.to_i, random) - evaluation - end + raise UnassignedTrainingInstancesError, message + end + end - def evaluate(test_instances) - ensure_trained_with_instances! - ensure_class_attribute_assigned!(test_instances) + module Transformers + private - evaluation = Evaluation.new(training_instances) - evaluation.evaluate_model(self, test_instances) - evaluation - end - end + def classifiable_instance_from(instance_or_values) + attributes = training_instances.attributes + instances = Weka::Core::Instances.new(attributes: attributes) - if instance_methods.include?(:classify_instance) - def classify(instance_or_values) - ensure_trained_with_instances! + class_attribute = training_instances.class_attribute + class_index = training_instances.class_index + instances.insert_attribute_at(class_attribute, class_index) - instance = classifiable_instance_from(instance_or_values) - index = classify_instance(instance) + instances.class_index = training_instances.class_index + instances.add_instance(instance_or_values) - class_value_of_index(index) - end - end + instance = instances.first + instance.set_class_missing + instance + end + end - if instance_methods.include?(:update_classifier) - def add_training_instance(instance) - training_instances.add(instance) - update_classifier(instance) + module Buildable + java_import 'java.util.Random' + include Checks - self - end + attr_reader :training_instances - def add_training_data(data) - values = training_instances.internal_values_of(data) - instance = Weka::Core::DenseInstance.new(values) - add_training_instance(instance) - end - end + def train_with_instances(instances) + ensure_class_attribute_assigned!(instances) - if instance_methods.include?(:distribution_for_instance) - def distribution_for(instance_or_values) - ensure_trained_with_instances! + @training_instances = instances + build_classifier(instances) - instance = classifiable_instance_from(instance_or_values) - distributions = distribution_for_instance(instance) + self + end - class_distributions_from(distributions) - end - end + def cross_validate(folds: 3) + ensure_trained_with_instances! - private + evaluation = Evaluation.new(training_instances) + random = Java::JavaUtil::Random.new(1) - def ensure_class_attribute_assigned!(instances) - return if instances.class_attribute_defined? + evaluation.cross_validate_model( + self, + training_instances, + folds.to_i, + random + ) - error = 'Class attribute is not assigned for Instances.' - hint = 'You can assign a class attribute with #class_attribute=.' - message = "#{error} #{hint}" + evaluation + end - raise UnassignedClassError, message - end + def evaluate(test_instances) + ensure_trained_with_instances! + ensure_class_attribute_assigned!(test_instances) - def ensure_trained_with_instances! - return unless training_instances.nil? + evaluation = Evaluation.new(training_instances) + evaluation.evaluate_model(self, test_instances) + evaluation + end + end - error = 'Classifier is not trained with Instances.' - hint = 'You can set the training instances with #train_with_instances.' - message = "#{error} #{hint}" + module Classifiable + include Checks + include Transformers - raise UnassignedTrainingInstancesError, message - end + def classify(instance_or_values) + ensure_trained_with_instances! - def classifiable_instance_from(instance_or_values) - attributes = training_instances.attributes - instances = Weka::Core::Instances.new(attributes: attributes) + instance = classifiable_instance_from(instance_or_values) + index = classify_instance(instance) - class_attribute = training_instances.class_attribute - class_index = training_instances.class_index - instances.insert_attribute_at(class_attribute, class_index) + class_value_of_index(index) + end - instances.class_index = training_instances.class_index - instances.add_instance(instance_or_values) + private - instance = instances.first - instance.set_class_missing - instance - end + def class_value_of_index(index) + training_instances.class_attribute.value(index) + end + end - def class_value_of_index(index) - training_instances.class_attribute.value(index) - end + module Updatable + def add_training_instance(instance) + training_instances.add(instance) + update_classifier(instance) + + self + end + + def add_training_data(data) + values = training_instances.internal_values_of(data) + instance = Weka::Core::DenseInstance.new(values) + add_training_instance(instance) + end + end + + module Distributable + include Checks + include Transformers + + def distribution_for(instance_or_values) + ensure_trained_with_instances! + + instance = classifiable_instance_from(instance_or_values) + distributions = distribution_for_instance(instance) + + class_distributions_from(distributions) + end + + private - def class_distributions_from(distributions) - class_values = training_instances.class_attribute.values + def class_distributions_from(distributions) + class_values = training_instances.class_attribute.values - distributions.each_with_index.reduce({}) do |result, (distribution, index)| - class_value = class_values[index] - result[class_value] = distribution - result - end + distributions.each_with_object({}).with_index do |(distribution, result), index| + class_value = class_values[index] + result[class_value] = distribution + result end end end diff --git a/lib/weka/clusterers/utils.rb b/lib/weka/clusterers/utils.rb index b08c11a..df35ee7 100644 --- a/lib/weka/clusterers/utils.rb +++ b/lib/weka/clusterers/utils.rb @@ -5,96 +5,122 @@ module Weka module Clusterers module Utils def self.included(base) - base.class_eval do - java_import 'java.util.Random' - - if instance_methods.include?(:build_clusterer) - attr_reader :training_instances - - def train_with_instances(instances) - @training_instances = instances - build_clusterer(instances) - - self - end - - if ancestors.include?(Java::WekaClusterers::DensityBasedClusterer) - def cross_validate(folds: 3) - ensure_trained_with_instances! - - ClusterEvaluation.cross_validate_model( - self, - training_instances, - folds.to_i, - Java::JavaUtil::Random.new(1) - ) - end - end - - def evaluate(test_instances) - ensure_trained_with_instances! - - ClusterEvaluation.new.tap do |evaluation| - evaluation.clusterer = self - evaluation.evaluate_clusterer(test_instances) - end - end - end + if base.instance_methods.include?(:build_clusterer) + base.include Buildable + base.include CrossValidatable if density_based?(base) + end - if instance_methods.include?(:cluster_instance) - def cluster(instance_or_values) - ensure_trained_with_instances! + base.include Clusterable if base.instance_methods.include?(:cluster_instance) + base.include Updatable if base.instance_methods.include?(:update_clusterer) + base.include Distributable if base.instance_methods.include?(:distribution_for_instance) + end - instance = clusterable_instance_from(instance_or_values) - cluster_instance(instance) - end - end + def self.density_based?(base) + base.ancestors.include?(Java::WekaClusterers::DensityBasedClusterer) + end - if instance_methods.include?(:update_clusterer) - def add_training_instance(instance) - training_instances.add(instance) - update_clusterer(instance) + module Checks + private - self - end + def ensure_trained_with_instances! + return unless training_instances.nil? - def add_training_data(data) - values = training_instances.internal_values_of(data) - instance = Weka::Core::DenseInstance.new(values) - add_training_instance(instance) - end - end + error = 'Clusterer is not trained with Instances.' + hint = 'You can set the training instances with #train_with_instances.' + message = "#{error} #{hint}" - if instance_methods.include?(:distribution_for_instance) - def distribution_for(instance_or_values) - ensure_trained_with_instances! + raise UnassignedTrainingInstancesError, message + end + end - instance = clusterable_instance_from(instance_or_values) - distribution_for_instance(instance).to_a - end - end + module Transformers + private - private + def clusterable_instance_from(instance_or_values) + attributes = training_instances.attributes + instances = Weka::Core::Instances.new(attributes: attributes) - def ensure_trained_with_instances! - return unless training_instances.nil? + instances.add_instance(instance_or_values) + instances.first + end + end - error = 'Clusterer is not trained with Instances.' - hint = 'You can set the training instances with #train_with_instances.' - message = "#{error} #{hint}" + module Buildable + include Checks - raise UnassignedTrainingInstancesError, message - end + attr_reader :training_instances - def clusterable_instance_from(instance_or_values) - attributes = training_instances.attributes - instances = Weka::Core::Instances.new(attributes: attributes) + def train_with_instances(instances) + @training_instances = instances + build_clusterer(instances) + + self + end - instances.add_instance(instance_or_values) - instances.first + def evaluate(test_instances) + ensure_trained_with_instances! + + ClusterEvaluation.new.tap do |evaluation| + evaluation.clusterer = self + evaluation.evaluate_clusterer(test_instances) end end end + + module CrossValidatable + java_import 'java.util.Random' + include Checks + + def cross_validate(folds: 3) + ensure_trained_with_instances! + + ClusterEvaluation.cross_validate_model( + self, + training_instances, + folds.to_i, + Java::JavaUtil::Random.new(1) + ) + end + end + + module Clusterable + include Checks + include Transformers + + def cluster(instance_or_values) + ensure_trained_with_instances! + + instance = clusterable_instance_from(instance_or_values) + cluster_instance(instance) + end + end + + module Updatable + def add_training_instance(instance) + training_instances.add(instance) + update_clusterer(instance) + + self + end + + def add_training_data(data) + values = training_instances.internal_values_of(data) + instance = Weka::Core::DenseInstance.new(values) + add_training_instance(instance) + end + end + + module Distributable + include Checks + include Transformers + + def distribution_for(instance_or_values) + ensure_trained_with_instances! + + instance = clusterable_instance_from(instance_or_values) + distribution_for_instance(instance).to_a + end + end end end end diff --git a/lib/weka/filters/utils.rb b/lib/weka/filters/utils.rb index 70f5955..6cccdd2 100644 --- a/lib/weka/filters/utils.rb +++ b/lib/weka/filters/utils.rb @@ -1,13 +1,9 @@ module Weka module Filters module Utils - def self.included(base) - base.class_eval do - def filter(instances) - set_input_format(instances) - Filter.use_filter(instances, self) - end - end + def filter(instances) + set_input_format(instances) + Filter.use_filter(instances, self) end end end From e143fe8f89cf1aaac360ff6c11e86b8bc26b81d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 14:24:54 +0200 Subject: [PATCH 23/25] Refactor concerns --- lib/weka/concerns/optionizable.rb | 59 ++++++++++++++++--------------- lib/weka/concerns/serializable.rb | 8 ++--- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/lib/weka/concerns/optionizable.rb b/lib/weka/concerns/optionizable.rb index 1300eae..483596f 100644 --- a/lib/weka/concerns/optionizable.rb +++ b/lib/weka/concerns/optionizable.rb @@ -2,45 +2,46 @@ module Weka module Concerns module Optionizable def self.included(base) - base.extend(ClassMethods) + base.extend ClassMethods + base.include InstanceMethods + end - base.class_eval do - java_import 'weka.core.Utils' + module ClassMethods + def default_options + new.get_options.to_a.join(' ') + end + end - def use_options(*single_options, **hash_options) - joined_options = join_options(single_options, hash_options) - options = Java::WekaCore::Utils.split_options(joined_options) + module InstanceMethods + java_import 'weka.core.Utils' - set_options(options) - @options = joined_options - end + def use_options(*single_options, **hash_options) + joined_options = join_options(single_options, hash_options) + options = Java::WekaCore::Utils.split_options(joined_options) - def options - @options || self.class.default_options - end + set_options(options) + @options = joined_options + end - private + def options + @options || self.class.default_options + end - def join_options(*single_options, **hash_options) - [ - join_single_options(*single_options), - join_hash_options(**hash_options) - ].reject(&:empty?).join(' ') - end + private - def join_single_options(options) - options.map { |option| "-#{option.to_s.sub(/^-/, '')}" }.join(' ') - end + def join_options(*single_options, **hash_options) + [ + join_single_options(*single_options), + join_hash_options(**hash_options) + ].reject(&:empty?).join(' ') + end - def join_hash_options(options) - options.map { |key, value| "-#{key} #{value}" }.join(' ') - end + def join_single_options(options) + options.map { |option| "-#{option.to_s.sub(/^-/, '')}" }.join(' ') end - end - module ClassMethods - def default_options - new.get_options.to_a.join(' ') + def join_hash_options(options) + options.map { |key, value| "-#{key} #{value}" }.join(' ') end end end diff --git a/lib/weka/concerns/serializable.rb b/lib/weka/concerns/serializable.rb index 9b2bf85..ce48de7 100644 --- a/lib/weka/concerns/serializable.rb +++ b/lib/weka/concerns/serializable.rb @@ -3,12 +3,8 @@ module Weka module Concerns module Serializable - def self.included(base) - base.class_eval do - def serialize(filename) - Weka::Core::SerializationHelper.write(filename, self) - end - end + def serialize(filename) + Weka::Core::SerializationHelper.write(filename, self) end end end From a1970a31eb132e0e6b90ce409f557ceeaa53abbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 14:25:47 +0200 Subject: [PATCH 24/25] Refactor code regarding Rubocop recommendations --- lib/weka/core/attribute.rb | 2 +- lib/weka/core/dense_instance.rb | 22 ++-- lib/weka/core/instances.rb | 13 ++- spec/attribute_selection/evaluator_spec.rb | 16 +-- spec/attribute_selection/search_spec.rb | 8 +- spec/class_builder_spec.rb | 21 ++-- spec/classifiers/bayes_spec.rb | 14 +-- spec/classifiers/evaluation_spec.rb | 2 +- spec/classifiers/functions_spec.rb | 24 ++--- spec/classifiers/lazy_spec.rb | 8 +- spec/classifiers/meta_spec.rb | 40 +++---- spec/classifiers/rules_spec.rb | 14 +-- spec/classifiers/trees_spec.rb | 18 ++-- spec/classifiers/utils_spec.rb | 15 ++- spec/clusterers/utils_spec.rb | 37 +++---- spec/clusterers_spec.rb | 14 +-- spec/concerns/optionizable_spec.rb | 2 +- spec/core/attribute_spec.rb | 4 +- spec/core/converters_spec.rb | 14 +-- spec/core/dense_instance_spec.rb | 13 +-- spec/core/instances_spec.rb | 26 +++-- spec/core/loader_spec.rb | 4 +- spec/core/saver_spec.rb | 4 +- spec/filters/supervised/attribute_spec.rb | 18 ++-- spec/filters/supervised/instance_spec.rb | 10 +- spec/filters/unsupervised/attribute_spec.rb | 112 ++++++++++---------- spec/filters/unsupervised/instance_spec.rb | 28 ++--- weka.gemspec | 1 + 28 files changed, 248 insertions(+), 256 deletions(-) diff --git a/lib/weka/core/attribute.rb b/lib/weka/core/attribute.rb index 93dfaa5..3985564 100644 --- a/lib/weka/core/attribute.rb +++ b/lib/weka/core/attribute.rb @@ -7,7 +7,7 @@ module Core class Attribute include Weka::Concerns::Persistent - TYPES = %i(numeric nominal string date).freeze + TYPES = %i[numeric nominal string date].freeze class << self def new_numeric(name) diff --git a/lib/weka/core/dense_instance.rb b/lib/weka/core/dense_instance.rb index 2332b23..f05fa08 100644 --- a/lib/weka/core/dense_instance.rb +++ b/lib/weka/core/dense_instance.rb @@ -57,25 +57,17 @@ def value_from(value, index) attribute = attribute_at(index) - if attribute.date? - format_date(value, attribute.date_format) - elsif attribute.numeric? - value - elsif attribute.nominal? || attribute.string? - attribute.value(value) - end + return format_date(value, attribute.date_format) if attribute.date? + return value if attribute.numeric? + return attribute.value(value) if attribute.nominal? || attribute.string? end def attribute_at(index) - return attributes[index] unless dataset.class_attribute_defined? + return attributes[index] unless dataset.class_attribute_defined? + return class_attribute if dataset.class_index == index + return attributes[index - 1] if index > dataset.class_index - if dataset.class_index == index - class_attribute - elsif index > dataset.class_index - attributes[index - 1] - else - attributes[index] - end + attributes[index] end def format_date(value, format) diff --git a/lib/weka/core/instances.rb b/lib/weka/core/instances.rb index ed18200..85d8070 100644 --- a/lib/weka/core/instances.rb +++ b/lib/weka/core/instances.rb @@ -297,7 +297,7 @@ def attribute_with_name(name) # # @param [Instance, Array, Hash] instance_or_values either the # instance object to be added or the attribute values for it. - # For the latter case, we can pass an array or a hash. + # For the latter case, it accepts an array or a hash. # # @param [Float] weight the weight for the Instance to be added # @@ -313,7 +313,11 @@ def instance_from(instance_or_values, weight:) end data = internal_values_of(instance_or_values) - data = check_string_attributes(data, instance_or_values) if has_string_attribute? + + if has_string_attribute? + data = check_string_attributes(data, instance_or_values) + end + DenseInstance.new(data, weight: weight) end end @@ -327,8 +331,8 @@ def map_attribute_type(type) # values into an array containing attribute values in the order # of the Instances attributes. # - # @param [Hash] hash a hash whose keys are attribute - # names and values are attribute values. + # @param [Hash] hash a hash whose keys are attribute names and + # values are attribute values. # # @return [Array] an array containing attribute values in the # correct order @@ -346,6 +350,7 @@ def attribute_values_from_hash(hash) # @return [Hash] a hash as described above def attribute_values_to_hash(values) names = attribute_names(include_class_attribute: true).map(&:to_sym) + names.each_with_index.inject({}) do |hash, (name, index)| hash.update(name => values[index]) end diff --git a/spec/attribute_selection/evaluator_spec.rb b/spec/attribute_selection/evaluator_spec.rb index 4412ca1..f156d09 100644 --- a/spec/attribute_selection/evaluator_spec.rb +++ b/spec/attribute_selection/evaluator_spec.rb @@ -6,14 +6,14 @@ it_behaves_like 'class builder' { - :CfsSubset => :CfsSubsetEval, - :CorrelationAttribute => :CorrelationAttributeEval, - :GainRatioAttribute => :GainRatioAttributeEval, - :InfoGainAttribute => :InfoGainAttributeEval, - :OneRAttribute => :OneRAttributeEval, - :ReliefFAttribute => :ReliefFAttributeEval, - :SymmetricalUncertAttribute => :SymmetricalUncertAttributeEval, - :WrapperSubset => :WrapperSubsetEval + CfsSubset: :CfsSubsetEval, + CorrelationAttribute: :CorrelationAttributeEval, + GainRatioAttribute: :GainRatioAttributeEval, + InfoGainAttribute: :InfoGainAttributeEval, + OneRAttribute: :OneRAttributeEval, + ReliefFAttribute: :ReliefFAttributeEval, + SymmetricalUncertAttribute: :SymmetricalUncertAttributeEval, + WrapperSubset: :WrapperSubsetEval }.each do |class_name, super_class_name| it "defines a class #{class_name}" do expect(subject.const_defined?(class_name)).to be true diff --git a/spec/attribute_selection/search_spec.rb b/spec/attribute_selection/search_spec.rb index 008816a..d7e0d63 100644 --- a/spec/attribute_selection/search_spec.rb +++ b/spec/attribute_selection/search_spec.rb @@ -5,10 +5,10 @@ it_behaves_like 'class builder' - [ - :GreedyStepwise, - :Ranker, - :BestFirst + %i[ + GreedyStepwise + Ranker + BestFirst ].each do |class_name| it "defines a class #{class_name}" do expect(subject.const_defined?(class_name)).to be true diff --git a/spec/class_builder_spec.rb b/spec/class_builder_spec.rb index 833d814..6d7cb88 100644 --- a/spec/class_builder_spec.rb +++ b/spec/class_builder_spec.rb @@ -17,7 +17,7 @@ module Module before { allow(subject).to receive(:java_import).and_return('') } - [:build_class, :build_classes].each do |method| + %i[build_class build_classes].each do |method| it "defines .#{method} if included" do expect(subject).to respond_to method end @@ -57,7 +57,7 @@ module Module expect(built_class).to respond_to :default_options end - [:use_options, :options].each do |method| + %i[use_options options].each do |method| it "responds to ##{method}" do expect(built_class_instance).to respond_to method end @@ -70,8 +70,7 @@ module Module module Some module Weka module Utils - def shared_method - end + def shared_method; end end end end @@ -123,18 +122,24 @@ module Module describe '.build_classes' do context 'without a given weka_module' do it 'runs .build_class for each of the given classes' do - class_names = %i(SomeClass SomeOtherClass) + class_names = %i[SomeClass SomeOtherClass] + + expect(subject) + .to receive(:build_class) + .exactly(class_names.count).times - expect(subject).to receive(:build_class).exactly(class_names.count).times subject.build_classes(*class_names) end end context 'with a given weka_module' do it 'runs .build_class for each of the given classes' do - class_names = %i(SomeClass SomeOtherClass) + class_names = %i[SomeClass SomeOtherClass] + + expect(subject) + .to receive(:build_class) + .exactly(class_names.count).times - expect(subject).to receive(:build_class).exactly(class_names.count).times subject.build_classes(*class_names, weka_module: 'weka.module') end end diff --git a/spec/classifiers/bayes_spec.rb b/spec/classifiers/bayes_spec.rb index 1279424..eaeed33 100644 --- a/spec/classifiers/bayes_spec.rb +++ b/spec/classifiers/bayes_spec.rb @@ -3,13 +3,13 @@ describe Weka::Classifiers::Bayes do it_behaves_like 'class builder' - [ - :BayesNet, - :NaiveBayes, - :NaiveBayesMultinomial, - :NaiveBayesMultinomialText, - :NaiveBayesMultinomialUpdateable, - :NaiveBayesUpdateable + %i[ + BayesNet + NaiveBayes + NaiveBayesMultinomial + NaiveBayesMultinomialText + NaiveBayesMultinomialUpdateable + NaiveBayesUpdateable ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/evaluation_spec.rb b/spec/classifiers/evaluation_spec.rb index eecb414..7e4d946 100644 --- a/spec/classifiers/evaluation_spec.rb +++ b/spec/classifiers/evaluation_spec.rb @@ -49,7 +49,7 @@ expect(described_class.const_defined?(class_name)).to be true end - describe "#{class_name}" do + describe class_name.to_s do let(:curve) do Object.module_eval("Weka::Classifiers::Evaluation::#{class_name}").new end diff --git a/spec/classifiers/functions_spec.rb b/spec/classifiers/functions_spec.rb index 234f17a..77d3539 100644 --- a/spec/classifiers/functions_spec.rb +++ b/spec/classifiers/functions_spec.rb @@ -3,18 +3,18 @@ describe Weka::Classifiers::Functions do it_behaves_like 'class builder' - [ - :GaussianProcesses, - :LinearRegression, - :Logistic, - :MultilayerPerceptron, - :SGD, - :SGDText, - :SimpleLinearRegression, - :SimpleLogistic, - :SMO, - :SMOreg, - :VotedPerceptron + %i[ + GaussianProcesses + LinearRegression + Logistic + MultilayerPerceptron + SGD + SGDText + SimpleLinearRegression + SimpleLogistic + SMO + SMOreg + VotedPerceptron ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/lazy_spec.rb b/spec/classifiers/lazy_spec.rb index c2b7383..d4fd1a2 100644 --- a/spec/classifiers/lazy_spec.rb +++ b/spec/classifiers/lazy_spec.rb @@ -3,10 +3,10 @@ describe Weka::Classifiers::Lazy do it_behaves_like 'class builder' - [ - :IBk, - :KStar, - :LWL + %i[ + IBk + KStar + LWL ].each do |class_name| it "definess a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/meta_spec.rb b/spec/classifiers/meta_spec.rb index 4c09994..ea8afcc 100644 --- a/spec/classifiers/meta_spec.rb +++ b/spec/classifiers/meta_spec.rb @@ -3,26 +3,26 @@ describe Weka::Classifiers::Meta do it_behaves_like 'class builder' - [ - :AdaBoostM1, - :AdditiveRegression, - :AttributeSelectedClassifier, - :Bagging, - :ClassificationViaRegression, - :CostSensitiveClassifier, - :CVParameterSelection, - :FilteredClassifier, - :IterativeClassifierOptimizer, - :LogitBoost, - :MultiClassClassifier, - :MultiClassClassifierUpdateable, - :MultiScheme, - :RandomCommittee, - :RandomizableFilteredClassifier, - :RandomSubSpace, - :RegressionByDiscretization, - :Stacking, - :Vote + %i[ + AdaBoostM1 + AdditiveRegression + AttributeSelectedClassifier + Bagging + ClassificationViaRegression + CostSensitiveClassifier + CVParameterSelection + FilteredClassifier + IterativeClassifierOptimizer + LogitBoost + MultiClassClassifier + MultiClassClassifierUpdateable + MultiScheme + RandomCommittee + RandomizableFilteredClassifier + RandomSubSpace + RegressionByDiscretization + Stacking + Vote ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/rules_spec.rb b/spec/classifiers/rules_spec.rb index 038b2a8..0add5e2 100644 --- a/spec/classifiers/rules_spec.rb +++ b/spec/classifiers/rules_spec.rb @@ -3,13 +3,13 @@ describe Weka::Classifiers::Rules do it_behaves_like 'class builder' - [ - :DecisionTable, - :JRip, - :M5Rules, - :OneR, - :PART, - :ZeroR + %i[ + DecisionTable + JRip + M5Rules + OneR + PART + ZeroR ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/trees_spec.rb b/spec/classifiers/trees_spec.rb index 9b0b006..02b1de9 100644 --- a/spec/classifiers/trees_spec.rb +++ b/spec/classifiers/trees_spec.rb @@ -3,15 +3,15 @@ describe Weka::Classifiers::Trees do it_behaves_like 'class builder' - [ - :DecisionStump, - :HoeffdingTree, - :J48, - :LMT, - :M5P, - :RandomForest, - :RandomTree, - :REPTree + %i[ + DecisionStump + HoeffdingTree + J48 + LMT + M5P + RandomForest + RandomTree + REPTree ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/classifiers/utils_spec.rb b/spec/classifiers/utils_spec.rb index ec8d896..6912ab0 100644 --- a/spec/classifiers/utils_spec.rb +++ b/spec/classifiers/utils_spec.rb @@ -3,17 +3,13 @@ describe Weka::Classifiers::Utils do let(:including_class) do Class.new do - def build_classifier(instances) - end + def build_classifier(instances); end - def update_classifier(instance) - end + def update_classifier(instance); end - def classify_instance - end + def classify_instance; end - def distribution_for_instance - end + def distribution_for_instance; end include Weka::Classifiers::Utils end @@ -183,7 +179,8 @@ def distribution_for_instance describe '#evaluate' do before do allow(subject).to receive(:training_instances).and_return(instances) - allow_any_instance_of(Weka::Classifiers::Evaluation).to receive(:evaluate_model) + allow_any_instance_of(Weka::Classifiers::Evaluation) + .to receive(:evaluate_model) end it 'returns a Weka::Classifiers::Evaluation' do diff --git a/spec/clusterers/utils_spec.rb b/spec/clusterers/utils_spec.rb index 36e9a96..995875c 100644 --- a/spec/clusterers/utils_spec.rb +++ b/spec/clusterers/utils_spec.rb @@ -3,17 +3,13 @@ describe Weka::Clusterers::Utils do let(:including_class) do Class.new do - def build_clusterer(instances) - end + def build_clusterer(instances); end - def update_clusterer(instance) - end + def update_clusterer(instance); end - def cluster_instance - end + def cluster_instance; end - def distribution_for_instance - end + def distribution_for_instance; end include Weka::Clusterers::Utils end @@ -96,12 +92,12 @@ def distribution_for_instance context 'if clusterer is not density-based' do subject do - Class.new { - def build_clusterer(instances) - end - + test_class = Class.new do + def build_clusterer(instances); end include Weka::Clusterers::Utils - }.new + end + + test_class.new end it { is_expected.not_to respond_to :cross_validate } @@ -109,14 +105,13 @@ def build_clusterer(instances) context 'if clusterer is density-based' do subject do - Class.new { + test_class = Class.new do include Java::WekaClusterers::DensityBasedClusterer - - def build_clusterer(instances) - end - + def build_clusterer(instances); end include Weka::Clusterers::Utils - }.new + end + + test_class.new end before do @@ -231,9 +226,7 @@ def build_clusterer(instances) let(:values) { [:overcast, 83, 86, :FALSE, :yes] } let(:cluster) { 1 } - before do - allow(subject).to receive(:cluster_instance).and_return(cluster) - end + before { allow(subject).to receive(:cluster_instance).and_return(cluster) } context 'with a given instance' do it 'calls Java’s #cluster_instance' do diff --git a/spec/clusterers_spec.rb b/spec/clusterers_spec.rb index 5a29afc..26d7c58 100644 --- a/spec/clusterers_spec.rb +++ b/spec/clusterers_spec.rb @@ -3,13 +3,13 @@ describe Weka::Clusterers do it_behaves_like 'class builder' - [ - :Cobweb, - :Canopy, - :EM, - :FarthestFirst, - :HierarchicalClusterer, - :SimpleKMeans + %i[ + Cobweb + Canopy + EM + FarthestFirst + HierarchicalClusterer + SimpleKMeans ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/concerns/optionizable_spec.rb b/spec/concerns/optionizable_spec.rb index 7db99d1..a39c6b3 100644 --- a/spec/concerns/optionizable_spec.rb +++ b/spec/concerns/optionizable_spec.rb @@ -103,7 +103,7 @@ before do allow_any_instance_of(subject.class) .to receive(:get_options) - .and_return(%w(-C last -Z -P 10 -M -B 0.1)) + .and_return(%w[-C last -Z -P 10 -M -B 0.1]) end it 'receives Java’s #get_options' do diff --git a/spec/core/attribute_spec.rb b/spec/core/attribute_spec.rb index 53ac527..ce422e7 100644 --- a/spec/core/attribute_spec.rb +++ b/spec/core/attribute_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Weka::Core::Attribute do - let(:values) { %w(true false) } + let(:values) { %w[true false] } let(:name) { 'name' } let(:format) { 'yyyy-MM-dd HH:mm' } @@ -149,7 +149,7 @@ end context 'for a string attribute' do - let(:string_values) { %w(first_string second_string) } + let(:string_values) { %w[first_string second_string] } let(:phantom_string_value) { 'i_do_not_exist' } subject do diff --git a/spec/core/converters_spec.rb b/spec/core/converters_spec.rb index df7f1f8..9cf4ec2 100644 --- a/spec/core/converters_spec.rb +++ b/spec/core/converters_spec.rb @@ -3,13 +3,13 @@ describe Weka::Core::Converters do it_behaves_like 'class builder' - [ - :ArffLoader, - :ArffSaver, - :CSVLoader, - :CSVSaver, - :JSONLoader, - :JSONSaver + %i[ + ArffLoader + ArffSaver + CSVLoader + CSVSaver + JSONLoader + JSONSaver ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/core/dense_instance_spec.rb b/spec/core/dense_instance_spec.rb index 995194d..9360dad 100644 --- a/spec/core/dense_instance_spec.rb +++ b/spec/core/dense_instance_spec.rb @@ -68,12 +68,13 @@ context 'with string attribute' do let(:values) do - ['overcast', - 'Wind increasing. A few clouds from time to time. High 32F. Winds WNW at 20 to 30 mph.', - 15.0, - 40.0, - 'TRUE', - 'no' + [ + 'overcast', + 'Wind increasing. A few clouds from time to time. High 32F. Winds WNW at 20 to 30 mph.', + 15.0, + 40.0, + 'TRUE', + 'no' ] end diff --git a/spec/core/instances_spec.rb b/spec/core/instances_spec.rb index 47991bf..97a3a44 100644 --- a/spec/core/instances_spec.rb +++ b/spec/core/instances_spec.rb @@ -57,7 +57,7 @@ end describe 'loader' do - [:arff, :csv, :json].each do |type| + %i[arff csv json].each do |type| before do allow(Weka::Core::Loader).to receive(:"load_#{type}").and_return('') end @@ -89,7 +89,7 @@ end describe 'saver' do - [:arff, :csv, :json].each do |type| + %i[arff csv json].each do |type| before do allow(Weka::Core::Saver).to receive(:"save_#{type}").and_return('') end @@ -108,9 +108,7 @@ describe '#to_c45' do let(:file) { 'test.names' } - before do - allow(Weka::Core::Saver).to receive(:save_c45).and_return('') - end + before { allow(Weka::Core::Saver).to receive(:save_c45).and_return('') } it 'calls the Weka::Core::Saver.save_c45' do expect(Weka::Core::Saver) @@ -164,13 +162,13 @@ end it 'skips the class attribute if include_class_attribute is false' do - expect(subject.attributes.size).to eq(subject.attributes_count-1) + expect(subject.attributes.size).to eq(subject.attributes_count - 1) end end end describe '#attribute_names' do - names = %w(outlook temperature humidity windy play) + names = %w[outlook temperature humidity windy play] it 'returns an Array of the attribute names' do expect(subject.attribute_names).to eq names @@ -225,13 +223,13 @@ describe '#nominal' do it 'can be used to add a nominal attribute' do - instances.nominal(name, values: %w(yes no)) + instances.nominal(name, values: %w[yes no]) expect(instances.attributes.first).to be_nominal end context 'with the class_attribute option' do it 'defines the attribute as class attribute' do - instances.nominal(name, values: %w(yes no), class_attribute: true) + instances.nominal(name, values: %w[yes no], class_attribute: true) expect(instances.class_attribute.name).to eq name end end @@ -268,7 +266,7 @@ describe '#nominal' do it 'can be used to add a nominal attribute' do - instances.nominal(:attribute_name, values: [:yes, :no]) + instances.nominal(:attribute_name, values: %i[yes no]) expect(instances.attributes.first).to be_nominal end @@ -279,7 +277,7 @@ it 'converts the options into strings' do instances.nominal(:attribute_name, values: [true, false]) - expect(instances.attributes.first.values).to eq %w(true false) + expect(instances.attributes.first.values).to eq %w[true false] end end @@ -380,7 +378,7 @@ expect do instances.add_attributes do numeric 'attribute' - nominal 'class', values: %w(YES NO) + nominal 'class', values: %w[YES NO] end end.to change { instances.attributes.count }.from(0).to(2) end @@ -390,10 +388,10 @@ instances.add_attributes do numeric 'attribute' - nominal 'class', values: %w(YES NO) + nominal 'class', values: %w[YES NO] end - expect(instances.attributes.map(&:name)).to eq %w(attribute class) + expect(instances.attributes.map(&:name)).to eq %w[attribute class] end end diff --git a/spec/core/loader_spec.rb b/spec/core/loader_spec.rb index 5c34914..afde479 100644 --- a/spec/core/loader_spec.rb +++ b/spec/core/loader_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Weka::Core::Loader do - CLASS_METHODS = %i(load_arff load_csv load_json load_c45).freeze + CLASS_METHODS = %i[load_arff load_csv load_json load_c45].freeze CLASS_METHODS.each do |method| it "responds to .#{method}" do @@ -9,7 +9,7 @@ end end - [:arff, :csv, :json].each do |type| + %i[arff csv json].each do |type| method = "load_#{type}" describe "##{method}" do diff --git a/spec/core/saver_spec.rb b/spec/core/saver_spec.rb index 2378f15..c271ea9 100644 --- a/spec/core/saver_spec.rb +++ b/spec/core/saver_spec.rb @@ -6,7 +6,7 @@ before(:all) { @tmp_dir = File.expand_path('../../tmp/', __FILE__) } after(:all) { FileUtils.remove_dir(@tmp_dir, true) } - CLASS_METHODS = %i(save_arff save_csv save_json).freeze + CLASS_METHODS = %i[save_arff save_csv save_json].freeze CLASS_METHODS.each do |method| it "responds to .#{method}" do @@ -14,7 +14,7 @@ end end - [:arff, :csv, :json].each do |type| + %i[arff csv json].each do |type| method = "save_#{type}" describe "##{method}" do diff --git a/spec/filters/supervised/attribute_spec.rb b/spec/filters/supervised/attribute_spec.rb index 6bfffaf..8d75d9e 100644 --- a/spec/filters/supervised/attribute_spec.rb +++ b/spec/filters/supervised/attribute_spec.rb @@ -3,15 +3,15 @@ describe Weka::Filters::Supervised::Attribute do it_behaves_like 'class builder' - [ - :AddClassification, - :AttributeSelection, - :ClassConditionalProbabilities, - :ClassOrder, - :Discretize, - :MergeNominalValues, - :NominalToBinary, - :PartitionMembership + %i[ + AddClassification + AttributeSelection + ClassConditionalProbabilities + ClassOrder + Discretize + MergeNominalValues + NominalToBinary + PartitionMembership ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/filters/supervised/instance_spec.rb b/spec/filters/supervised/instance_spec.rb index 20618ef..50dc004 100644 --- a/spec/filters/supervised/instance_spec.rb +++ b/spec/filters/supervised/instance_spec.rb @@ -3,11 +3,11 @@ describe Weka::Filters::Supervised::Instance do it_behaves_like 'class builder' - [ - :ClassBalancer, - :Resample, - :SpreadSubsample, - :StratifiedRemoveFolds + %i[ + ClassBalancer + Resample + SpreadSubsample + StratifiedRemoveFolds ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/filters/unsupervised/attribute_spec.rb b/spec/filters/unsupervised/attribute_spec.rb index e8344b0..99fece7 100644 --- a/spec/filters/unsupervised/attribute_spec.rb +++ b/spec/filters/unsupervised/attribute_spec.rb @@ -3,62 +3,62 @@ describe Weka::Filters::Unsupervised::Attribute do it_behaves_like 'class builder' - [ - :AbstractTimeSeries, - :Add, - :AddCluster, - :AddExpression, - :AddID, - :AddNoise, - :AddUserFields, - :AddUserFieldsBeanInfo, - :AddValues, - :Center, - :ChangeDateFormat, - :ClassAssigner, - :ClusterMembership, - :Copy, - :Discretize, - :FirstOrder, - :InterquartileRange, - :KernelFilter, - :MakeIndicator, - :MathExpression, - :MergeInfrequentNominalValues, - :MergeManyValues, - :MergeTwoValues, - :NominalToBinary, - :NominalToString, - :Normalize, - :NumericCleaner, - :NumericToBinary, - :NumericToNominal, - :NumericTransform, - :Obfuscate, - :PartitionedMultiFilter, - :PKIDiscretize, - :PotentialClassIgnorer, - :PrincipalComponents, - :RandomProjection, - :RandomSubset, - :Remove, - :RemoveByName, - :RemoveType, - :RemoveUseless, - :RenameAttribute, - :RenameNominalValues, - :Reorder, - :ReplaceMissingValues, - :ReplaceMissingWithUserConstant, - :ReplaceWithMissingValue, - :SortLabels, - :Standardize, - :StringToNominal, - :StringToWordVector, - :SwapValues, - :TimeSeriesDelta, - :TimeSeriesTranslate, - :Transpose + %i[ + AbstractTimeSeries + Add + AddCluster + AddExpression + AddID + AddNoise + AddUserFields + AddUserFieldsBeanInfo + AddValues + Center + ChangeDateFormat + ClassAssigner + ClusterMembership + Copy + Discretize + FirstOrder + InterquartileRange + KernelFilter + MakeIndicator + MathExpression + MergeInfrequentNominalValues + MergeManyValues + MergeTwoValues + NominalToBinary + NominalToString + Normalize + NumericCleaner + NumericToBinary + NumericToNominal + NumericTransform + Obfuscate + PartitionedMultiFilter + PKIDiscretize + PotentialClassIgnorer + PrincipalComponents + RandomProjection + RandomSubset + Remove + RemoveByName + RemoveType + RemoveUseless + RenameAttribute + RenameNominalValues + Reorder + ReplaceMissingValues + ReplaceMissingWithUserConstant + ReplaceWithMissingValue + SortLabels + Standardize + StringToNominal + StringToWordVector + SwapValues + TimeSeriesDelta + TimeSeriesTranslate + Transpose ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/spec/filters/unsupervised/instance_spec.rb b/spec/filters/unsupervised/instance_spec.rb index 6777f28..b998ac8 100644 --- a/spec/filters/unsupervised/instance_spec.rb +++ b/spec/filters/unsupervised/instance_spec.rb @@ -3,20 +3,20 @@ describe Weka::Filters::Unsupervised::Instance do it_behaves_like 'class builder' - [ - :NonSparseToSparse, - :Randomize, - :RemoveDuplicates, - :RemoveFolds, - :RemoveFrequentValues, - :RemoveMisclassified, - :RemovePercentage, - :RemoveRange, - :RemoveWithValues, - :Resample, - :ReservoirSample, - :SparseToNonSparse, - :SubsetByExpression + %i[ + NonSparseToSparse + Randomize + RemoveDuplicates + RemoveFolds + RemoveFrequentValues + RemoveMisclassified + RemovePercentage + RemoveRange + RemoveWithValues + Resample + ReservoirSample + SparseToNonSparse + SubsetByExpression ].each do |class_name| it "defines a class #{class_name}" do expect(described_class.const_defined?(class_name)).to be true diff --git a/weka.gemspec b/weka.gemspec index a7eba1e..c18a062 100644 --- a/weka.gemspec +++ b/weka.gemspec @@ -1,4 +1,5 @@ # coding: utf-8 + lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'weka/version' From 0ea14dbd40fb986ed67941e5a3d9dc448d1f854d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20G=C3=B6tze?= Date: Sat, 17 Jun 2017 14:42:13 +0200 Subject: [PATCH 25/25] Add codacy badge to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index acb6708..fea9f20 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Gem Version](https://badge.fury.io/rb/weka.svg)](http://badge.fury.io/rb/weka) [![Travis Build](https://travis-ci.org/paulgoetze/weka-jruby.svg)](https://travis-ci.org/paulgoetze/weka-jruby) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9634a6709ef545198e079a8daddff100)](https://www.codacy.com/app/paul-christoph-goetze/weka-jruby?utm_source=github.com&utm_medium=referral&utm_content=paulgoetze/weka-jruby&utm_campaign=Badge_Grade) Machine Learning & Data Mining with JRuby based on the [Weka](http://www.cs.waikato.ac.nz/~ml/weka/index.html) Java library. @@ -60,7 +61,7 @@ Here’s how to contribute: Please try to add RSpec tests along with your new features. This will ensure that your code does not break existing functionality and that your feature is working as expected. We use [Rubocop](https://github.com/bbatsov/rubocop) for code style recommendations. -Please make sure your contributions comply with the default config of Rubocop. +Please make sure your contributions comply with the project’s Rubocop config. ## Acknowledgement