ai4ruby 1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +47 -0
- data/examples/classifiers/id3_data.csv +121 -0
- data/examples/classifiers/id3_example.rb +29 -0
- data/examples/classifiers/naive_bayes_data.csv +11 -0
- data/examples/classifiers/naive_bayes_example.rb +16 -0
- data/examples/classifiers/results.txt +31 -0
- data/examples/genetic_algorithm/genetic_algorithm_example.rb +37 -0
- data/examples/genetic_algorithm/travel_cost.csv +16 -0
- data/examples/neural_network/backpropagation_example.rb +67 -0
- data/examples/neural_network/patterns_with_base_noise.rb +68 -0
- data/examples/neural_network/patterns_with_noise.rb +66 -0
- data/examples/neural_network/training_patterns.rb +68 -0
- data/examples/neural_network/xor_example.rb +35 -0
- data/examples/som/som_data.rb +156 -0
- data/examples/som/som_multi_node_example.rb +22 -0
- data/examples/som/som_single_example.rb +24 -0
- data/lib/ai4r.rb +33 -0
- data/lib/ai4r/classifiers/classifier.rb +62 -0
- data/lib/ai4r/classifiers/hyperpipes.rb +118 -0
- data/lib/ai4r/classifiers/ib1.rb +121 -0
- data/lib/ai4r/classifiers/id3.rb +326 -0
- data/lib/ai4r/classifiers/multilayer_perceptron.rb +135 -0
- data/lib/ai4r/classifiers/naive_bayes.rb +259 -0
- data/lib/ai4r/classifiers/one_r.rb +110 -0
- data/lib/ai4r/classifiers/prism.rb +197 -0
- data/lib/ai4r/classifiers/zero_r.rb +73 -0
- data/lib/ai4r/clusterers/average_linkage.rb +59 -0
- data/lib/ai4r/clusterers/bisecting_k_means.rb +93 -0
- data/lib/ai4r/clusterers/centroid_linkage.rb +66 -0
- data/lib/ai4r/clusterers/clusterer.rb +61 -0
- data/lib/ai4r/clusterers/complete_linkage.rb +67 -0
- data/lib/ai4r/clusterers/diana.rb +139 -0
- data/lib/ai4r/clusterers/k_means.rb +126 -0
- data/lib/ai4r/clusterers/median_linkage.rb +61 -0
- data/lib/ai4r/clusterers/single_linkage.rb +194 -0
- data/lib/ai4r/clusterers/ward_linkage.rb +64 -0
- data/lib/ai4r/clusterers/ward_linkage_hierarchical.rb +31 -0
- data/lib/ai4r/clusterers/weighted_average_linkage.rb +61 -0
- data/lib/ai4r/data/data_set.rb +266 -0
- data/lib/ai4r/data/parameterizable.rb +64 -0
- data/lib/ai4r/data/proximity.rb +100 -0
- data/lib/ai4r/data/statistics.rb +77 -0
- data/lib/ai4r/experiment/classifier_evaluator.rb +95 -0
- data/lib/ai4r/genetic_algorithm/genetic_algorithm.rb +270 -0
- data/lib/ai4r/neural_network/backpropagation.rb +326 -0
- data/lib/ai4r/neural_network/hopfield.rb +149 -0
- data/lib/ai4r/som/layer.rb +68 -0
- data/lib/ai4r/som/node.rb +96 -0
- data/lib/ai4r/som/som.rb +155 -0
- data/lib/ai4r/som/two_phase_layer.rb +90 -0
- data/test/classifiers/hyperpipes_test.rb +84 -0
- data/test/classifiers/ib1_test.rb +78 -0
- data/test/classifiers/id3_test.rb +208 -0
- data/test/classifiers/multilayer_perceptron_test.rb +79 -0
- data/test/classifiers/naive_bayes_test.rb +43 -0
- data/test/classifiers/one_r_test.rb +62 -0
- data/test/classifiers/prism_test.rb +85 -0
- data/test/classifiers/zero_r_test.rb +49 -0
- data/test/clusterers/average_linkage_test.rb +51 -0
- data/test/clusterers/bisecting_k_means_test.rb +66 -0
- data/test/clusterers/centroid_linkage_test.rb +53 -0
- data/test/clusterers/complete_linkage_test.rb +57 -0
- data/test/clusterers/diana_test.rb +69 -0
- data/test/clusterers/k_means_test.rb +100 -0
- data/test/clusterers/median_linkage_test.rb +53 -0
- data/test/clusterers/single_linkage_test.rb +122 -0
- data/test/clusterers/ward_linkage_hierarchical_test.rb +61 -0
- data/test/clusterers/ward_linkage_test.rb +53 -0
- data/test/clusterers/weighted_average_linkage_test.rb +53 -0
- data/test/data/data_set_test.rb +96 -0
- data/test/data/proximity_test.rb +81 -0
- data/test/data/statistics_test.rb +65 -0
- data/test/experiment/classifier_evaluator_test.rb +76 -0
- data/test/genetic_algorithm/chromosome_test.rb +58 -0
- data/test/genetic_algorithm/genetic_algorithm_test.rb +81 -0
- data/test/neural_network/backpropagation_test.rb +82 -0
- data/test/neural_network/hopfield_test.rb +72 -0
- data/test/som/som_test.rb +97 -0
- metadata +168 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/classifiers/prism'
|
3
|
+
|
4
|
+
|
5
|
+
class PrismTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Ai4r::Classifiers
|
8
|
+
include Ai4r::Data
|
9
|
+
|
10
|
+
@@data_examples = [ ['New York', '<30', 'M', 'Y'],
|
11
|
+
['Chicago', '<30', 'M', 'Y'],
|
12
|
+
['Chicago', '<30', 'F', 'Y'],
|
13
|
+
['New York', '<30', 'M', 'Y'],
|
14
|
+
['New York', '<30', 'M', 'Y'],
|
15
|
+
['Chicago', '[30-50)', 'M', 'Y'],
|
16
|
+
['New York', '[30-50)', 'F', 'N'],
|
17
|
+
['Chicago', '[30-50)', 'F', 'Y'],
|
18
|
+
['New York', '[30-50)', 'F', 'N'],
|
19
|
+
['Chicago', '[50-80]', 'M', 'N'],
|
20
|
+
['New York', '[50-80]', 'F', 'N'],
|
21
|
+
['New York', '[50-80]', 'M', 'N'],
|
22
|
+
['Chicago', '[50-80]', 'M', 'N'],
|
23
|
+
['New York', '[50-80]', 'F', 'N'],
|
24
|
+
['Chicago', '>80', 'F', 'Y']
|
25
|
+
]
|
26
|
+
|
27
|
+
@@data_labels = [ 'city', 'age_range', 'gender', 'marketing_target' ]
|
28
|
+
|
29
|
+
def test_build
|
30
|
+
assert_raise(ArgumentError) { Prism.new.build(DataSet.new) }
|
31
|
+
classifier = Prism.new.build(DataSet.new(:data_items=>@@data_examples))
|
32
|
+
assert_not_nil(classifier.data_set.data_labels)
|
33
|
+
assert_not_nil(classifier.rules)
|
34
|
+
assert_equal("attribute_1", classifier.data_set.data_labels.first)
|
35
|
+
assert_equal("class_value", classifier.data_set.data_labels.last)
|
36
|
+
classifier = Prism.new.build(DataSet.new(:data_items => @@data_examples,
|
37
|
+
:data_labels => @@data_labels))
|
38
|
+
assert_not_nil(classifier.data_set.data_labels)
|
39
|
+
assert_not_nil(classifier.rules)
|
40
|
+
assert_equal("city", classifier.data_set.data_labels.first)
|
41
|
+
assert_equal("marketing_target", classifier.data_set.data_labels.last)
|
42
|
+
assert !classifier.rules.empty?
|
43
|
+
|
44
|
+
Prism.send(:public, *Prism.protected_instance_methods)
|
45
|
+
Prism.send(:public, *Prism.private_instance_methods)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_eval
|
49
|
+
classifier = Prism.new.build(DataSet.new(:data_items=>@@data_examples))
|
50
|
+
@@data_examples.each do |data|
|
51
|
+
assert_equal(data.last, classifier.eval(data[0...-1]))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_get_rules
|
56
|
+
classifier = Prism.new.build(DataSet.new(:data_items => @@data_examples,
|
57
|
+
:data_labels => @@data_labels))
|
58
|
+
marketing_target = nil
|
59
|
+
age_range = nil
|
60
|
+
city = 'Chicago'
|
61
|
+
eval(classifier.get_rules)
|
62
|
+
age_range = '<30'
|
63
|
+
eval(classifier.get_rules)
|
64
|
+
assert_equal("Y", marketing_target)
|
65
|
+
age_range = '[30-50)'
|
66
|
+
eval(classifier.get_rules)
|
67
|
+
assert_equal("Y", marketing_target)
|
68
|
+
age_range = '[30-50)'
|
69
|
+
city = 'New York'
|
70
|
+
eval(classifier.get_rules)
|
71
|
+
assert_equal("N", marketing_target)
|
72
|
+
age_range = '[50-80]'
|
73
|
+
eval(classifier.get_rules)
|
74
|
+
assert_equal("N", marketing_target)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_matches_conditions
|
78
|
+
classifier = Prism.new.build(DataSet.new(:data_labels => @@data_labels,
|
79
|
+
:data_items => @@data_examples))
|
80
|
+
|
81
|
+
assert classifier.matches_conditions(['New York', '<30', 'M', 'Y'], {"age_range" => "<30"})
|
82
|
+
assert !classifier.matches_conditions(['New York', '<30', 'M', 'Y'], {"age_range" => "[50-80]"})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/classifiers/zero_r'
|
3
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/data/data_set'
|
4
|
+
|
5
|
+
class ZeroRTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Ai4r::Classifiers
|
8
|
+
include Ai4r::Data
|
9
|
+
|
10
|
+
@@data_examples = [ ['New York', '<30', 'M', 'Y'],
|
11
|
+
['Chicago', '<30', 'M', 'Y'],
|
12
|
+
['New York', '<30', 'M', 'Y'],
|
13
|
+
['New York', '[30-50)', 'F', 'N'],
|
14
|
+
['Chicago', '[30-50)', 'F', 'Y'],
|
15
|
+
['New York', '[30-50)', 'F', 'N'],
|
16
|
+
['Chicago', '[50-80]', 'M', 'N'],
|
17
|
+
]
|
18
|
+
|
19
|
+
@@data_labels = [ 'city', 'age_range', 'gender', 'marketing_target' ]
|
20
|
+
|
21
|
+
def test_build
|
22
|
+
assert_raise(ArgumentError) { ZeroR.new.build(DataSet.new) }
|
23
|
+
classifier = ZeroR.new.build(DataSet.new(:data_items => @@data_examples))
|
24
|
+
assert_equal("Y", classifier.class_value)
|
25
|
+
assert_equal("attribute_1", classifier.data_set.data_labels.first)
|
26
|
+
assert_equal("class_value", classifier.data_set.data_labels.last)
|
27
|
+
classifier = ZeroR.new.build(DataSet.new(:data_items => @@data_examples,
|
28
|
+
:data_labels => @@data_labels))
|
29
|
+
assert_equal("Y", classifier.class_value)
|
30
|
+
assert_equal("city", classifier.data_set.data_labels.first)
|
31
|
+
assert_equal("marketing_target", classifier.data_set.data_labels.last)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_eval
|
35
|
+
classifier = ZeroR.new.build(DataSet.new(:data_items => @@data_examples))
|
36
|
+
assert_equal('Y', classifier.eval(@@data_examples.first) )
|
37
|
+
assert_equal('Y', classifier.eval(@@data_examples.last) )
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_get_rules
|
41
|
+
classifier = ZeroR.new.build(DataSet.new(:data_items => @@data_examples,
|
42
|
+
:data_labels => @@data_labels))
|
43
|
+
marketing_target = nil
|
44
|
+
eval(classifier.get_rules)
|
45
|
+
assert_equal('Y', marketing_target)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/average_linkage'
|
12
|
+
|
13
|
+
class Ai4r::Clusterers::AverageLinkage < Ai4r::Clusterers::SingleLinkage
|
14
|
+
attr_accessor :data_set, :number_of_clusters, :clusters, :distance_matrix
|
15
|
+
end
|
16
|
+
|
17
|
+
class AverageLinkageTest < Test::Unit::TestCase
|
18
|
+
|
19
|
+
include Ai4r::Clusterers
|
20
|
+
include Ai4r::Data
|
21
|
+
|
22
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
23
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
24
|
+
|
25
|
+
@@expected_distance_matrix = [
|
26
|
+
[98.0],
|
27
|
+
[89.0, 5.0],
|
28
|
+
[68.0, 26.0, 9.0],
|
29
|
+
[74.0, 4.0, 1.0, 10.0],
|
30
|
+
[0.0, 98.0, 89.0, 68.0, 74.0],
|
31
|
+
[81.0, 53.0, 26.0, 5.0, 29.0, 81.0],
|
32
|
+
[8.0, 106.0, 85.0, 52.0, 74.0, 8.0, 53.0],
|
33
|
+
[100.0, 2.0, 1.0, 16.0, 2.0, 100.0, 37.0, 100.0],
|
34
|
+
[68.0, 26.0, 9.0, 0.0, 10.0, 68.0, 5.0, 52.0, 16.0],
|
35
|
+
[49.0, 49.0, 26.0, 5.0, 25.0, 49.0, 4.0, 29.0, 37.0, 5.0],
|
36
|
+
[2.0, 72.0, 65.0, 50.0, 52.0, 2.0, 65.0, 10.0, 74.0, 50.0, 37.0]]
|
37
|
+
|
38
|
+
def setup
|
39
|
+
Ai4r::Clusterers::AverageLinkage.send(:public,
|
40
|
+
*Ai4r::Clusterers::AverageLinkage.protected_instance_methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_linkage_distance
|
44
|
+
clusterer = Ai4r::Clusterers::AverageLinkage.new
|
45
|
+
clusterer.distance_matrix = @@expected_distance_matrix
|
46
|
+
assert_equal 93.5, clusterer.linkage_distance(0,1,2)
|
47
|
+
assert_equal 37.5, clusterer.linkage_distance(4,2,5)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/bisecting_k_means'
|
12
|
+
|
13
|
+
class BisectingKMeansTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
include Ai4r::Clusterers
|
16
|
+
include Ai4r::Data
|
17
|
+
|
18
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
19
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
20
|
+
|
21
|
+
def test_build_without_refine
|
22
|
+
build(false)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_build_with_refine
|
26
|
+
build(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def build(refine)
|
31
|
+
data_set = DataSet.new(:data_items => @@data, :data_labels => ["X", "Y"])
|
32
|
+
clusterer = BisectingKMeans.new
|
33
|
+
clusterer.set_parameters :refine => refine
|
34
|
+
clusterer.build(data_set, 4)
|
35
|
+
#draw_map(clusterer)
|
36
|
+
# Verify that all 4 clusters are created
|
37
|
+
assert_equal 4, clusterer.clusters.length
|
38
|
+
assert_equal 4, clusterer.centroids.length
|
39
|
+
# The addition of all instances of every cluster must be equal than
|
40
|
+
# the number of data points
|
41
|
+
total_length = 0
|
42
|
+
clusterer.clusters.each do |cluster|
|
43
|
+
total_length += cluster.data_items.length
|
44
|
+
end
|
45
|
+
assert_equal @@data.length, total_length
|
46
|
+
# Data inside clusters must be the same as orifinal data
|
47
|
+
clusterer.clusters.each do |cluster|
|
48
|
+
cluster.data_items.each do |data_item|
|
49
|
+
assert @@data.include?(data_item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def draw_map(clusterer)
|
56
|
+
map = Array.new(11) {Array.new(11, 0)}
|
57
|
+
clusterer.clusters.each_index do |i|
|
58
|
+
clusterer.clusters[i].data_items.each do |point|
|
59
|
+
map[point.first][point.last]=(i+1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
map.each { |row| puts row.inspect}
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/centroid_linkage'
|
12
|
+
|
13
|
+
class Ai4r::Clusterers::CentroidLinkage
|
14
|
+
attr_accessor :data_set, :number_of_clusters, :clusters, :distance_matrix, :index_clusters
|
15
|
+
end
|
16
|
+
|
17
|
+
class Ai4r::Clusterers::CentroidLinkageTest < Test::Unit::TestCase
|
18
|
+
|
19
|
+
include Ai4r::Clusterers
|
20
|
+
include Ai4r::Data
|
21
|
+
|
22
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
23
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
24
|
+
|
25
|
+
@@expected_distance_matrix = [
|
26
|
+
[98.0],
|
27
|
+
[89.0, 5.0],
|
28
|
+
[68.0, 26.0, 9.0],
|
29
|
+
[74.0, 4.0, 1.0, 10.0],
|
30
|
+
[0.0, 98.0, 89.0, 68.0, 74.0],
|
31
|
+
[81.0, 53.0, 26.0, 5.0, 29.0, 81.0],
|
32
|
+
[8.0, 106.0, 85.0, 52.0, 74.0, 8.0, 53.0],
|
33
|
+
[100.0, 2.0, 1.0, 16.0, 2.0, 100.0, 37.0, 100.0],
|
34
|
+
[68.0, 26.0, 9.0, 0.0, 10.0, 68.0, 5.0, 52.0, 16.0],
|
35
|
+
[49.0, 49.0, 26.0, 5.0, 25.0, 49.0, 4.0, 29.0, 37.0, 5.0],
|
36
|
+
[2.0, 72.0, 65.0, 50.0, 52.0, 2.0, 65.0, 10.0, 74.0, 50.0, 37.0]]
|
37
|
+
|
38
|
+
def setup
|
39
|
+
Ai4r::Clusterers::CentroidLinkage.send(:public,
|
40
|
+
*Ai4r::Clusterers::CentroidLinkage.protected_instance_methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_linkage_distance
|
44
|
+
clusterer = Ai4r::Clusterers::CentroidLinkage.new
|
45
|
+
clusterer.data_set = DataSet.new :data_items => @@data
|
46
|
+
clusterer.index_clusters = clusterer.create_initial_index_clusters
|
47
|
+
clusterer.distance_matrix = @@expected_distance_matrix
|
48
|
+
assert_equal 92.25, clusterer.linkage_distance(0,1,2)
|
49
|
+
assert_equal 15.25, clusterer.linkage_distance(4,2,5)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/complete_linkage'
|
12
|
+
|
13
|
+
class Ai4r::Clusterers::CompleteLinkage
|
14
|
+
attr_accessor :data_set, :number_of_clusters, :clusters, :distance_matrix
|
15
|
+
end
|
16
|
+
|
17
|
+
class CompleteLinkageTest < Test::Unit::TestCase
|
18
|
+
|
19
|
+
include Ai4r::Clusterers
|
20
|
+
include Ai4r::Data
|
21
|
+
|
22
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
23
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
24
|
+
|
25
|
+
@@expected_distance_matrix = [
|
26
|
+
[98.0],
|
27
|
+
[89.0, 5.0],
|
28
|
+
[68.0, 26.0, 9.0],
|
29
|
+
[74.0, 4.0, 1.0, 10.0],
|
30
|
+
[0.0, 98.0, 89.0, 68.0, 74.0],
|
31
|
+
[81.0, 53.0, 26.0, 5.0, 29.0, 81.0],
|
32
|
+
[8.0, 106.0, 85.0, 52.0, 74.0, 8.0, 53.0],
|
33
|
+
[100.0, 2.0, 1.0, 16.0, 2.0, 100.0, 37.0, 100.0],
|
34
|
+
[68.0, 26.0, 9.0, 0.0, 10.0, 68.0, 5.0, 52.0, 16.0],
|
35
|
+
[49.0, 49.0, 26.0, 5.0, 25.0, 49.0, 4.0, 29.0, 37.0, 5.0],
|
36
|
+
[2.0, 72.0, 65.0, 50.0, 52.0, 2.0, 65.0, 10.0, 74.0, 50.0, 37.0]]
|
37
|
+
|
38
|
+
def setup
|
39
|
+
Ai4r::Clusterers::CompleteLinkage.send(:public,
|
40
|
+
*Ai4r::Clusterers::CompleteLinkage.protected_instance_methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_linkage_distance
|
44
|
+
clusterer = Ai4r::Clusterers::CompleteLinkage.new
|
45
|
+
clusterer.distance_matrix = @@expected_distance_matrix
|
46
|
+
assert_equal 98, clusterer.linkage_distance(0,1,2)
|
47
|
+
assert_equal 74, clusterer.linkage_distance(4,2,5)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_distance_between_item_and_cluster
|
51
|
+
clusterer = CompleteLinkage.new
|
52
|
+
assert_equal 32.0, clusterer.distance_between_item_and_cluster([1,2],
|
53
|
+
DataSet.new(:data_items => [[3,4],[5,6]]))
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/diana'
|
12
|
+
|
13
|
+
class Ai4r::Clusterers::Diana
|
14
|
+
attr_accessor :data_set, :number_of_clusters, :clusters
|
15
|
+
end
|
16
|
+
|
17
|
+
class DianaTest < Test::Unit::TestCase
|
18
|
+
|
19
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
20
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
21
|
+
|
22
|
+
include Ai4r::Clusterers
|
23
|
+
include Ai4r::Data
|
24
|
+
|
25
|
+
def setup
|
26
|
+
Diana.send(:public, *Diana.protected_instance_methods)
|
27
|
+
@data_set = DataSet.new(:data_items => @@data.clone)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_build
|
31
|
+
clusterer = Diana.new.build(@data_set, 4)
|
32
|
+
assert_equal 4, clusterer.clusters.length
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_eval
|
36
|
+
clusterer = Diana.new.build(@data_set, 4)
|
37
|
+
assert_equal 0, clusterer.eval([0, 0])
|
38
|
+
assert_equal 1, clusterer.eval([9, 3])
|
39
|
+
assert_equal 2, clusterer.eval([0,10])
|
40
|
+
assert_equal 3, clusterer.eval([8, 0])
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_cluster_diameter
|
44
|
+
clusterer = Diana.new
|
45
|
+
assert_equal 106, clusterer.cluster_diameter(@data_set)
|
46
|
+
assert_equal 98, clusterer.cluster_diameter(@data_set[0..2])
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_max_diameter_cluster
|
50
|
+
clusterer = Diana.new
|
51
|
+
assert_equal 0, clusterer.max_diameter_cluster([@data_set, @data_set[0..2]])
|
52
|
+
assert_equal 1, clusterer.max_diameter_cluster([@data_set[0..2], @data_set])
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_init_splinter_cluster
|
56
|
+
clusterer = Diana.new
|
57
|
+
assert_equal [10,3], clusterer.
|
58
|
+
init_splinter_cluster(@data_set).data_items[0]
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_max_distance_difference
|
62
|
+
clusterer = Diana.new
|
63
|
+
data_set_a = @data_set[1..-1]
|
64
|
+
data_set_b = @data_set[0]
|
65
|
+
assert_equal [63.7, 4], clusterer.
|
66
|
+
max_distance_difference(data_set_a, data_set_b)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Author:: Sergio Fierens (implementation)
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
6
|
+
# You can redistribute it and/or modify it under the terms of
|
7
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/clusterers/k_means'
|
12
|
+
|
13
|
+
class KMeansTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
include Ai4r::Clusterers
|
16
|
+
include Ai4r::Data
|
17
|
+
|
18
|
+
@@data = [ [10, 3], [3, 10], [2, 8], [2, 5], [3, 8], [10, 3],
|
19
|
+
[1, 3], [8, 1], [2, 9], [2, 5], [3, 3], [9, 4]]
|
20
|
+
|
21
|
+
def test_build
|
22
|
+
data_set = DataSet.new(:data_items => @@data, :data_labels => ["X", "Y"])
|
23
|
+
clusterer = KMeans.new.build(data_set, 4)
|
24
|
+
#draw_map(clusterer)
|
25
|
+
# Verify that all 4 clusters are created
|
26
|
+
assert_equal 4, clusterer.clusters.length
|
27
|
+
assert_equal 4, clusterer.centroids.length
|
28
|
+
# The addition of all instances of every cluster must be equal than
|
29
|
+
# the number of data points
|
30
|
+
total_length = 0
|
31
|
+
clusterer.clusters.each do |cluster|
|
32
|
+
total_length += cluster.data_items.length
|
33
|
+
end
|
34
|
+
assert_equal @@data.length, total_length
|
35
|
+
# Data inside clusters must be the same as orifinal data
|
36
|
+
clusterer.clusters.each do |cluster|
|
37
|
+
cluster.data_items.each do |data_item|
|
38
|
+
assert @@data.include?(data_item)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_eval
|
44
|
+
data_set = DataSet.new(:data_items => @@data, :data_labels => ["X", "Y"])
|
45
|
+
clusterer = KMeans.new.build(data_set, 4)
|
46
|
+
item = [10,0]
|
47
|
+
cluster_index = clusterer.eval(item)
|
48
|
+
# Must return a valid cluster index [0-3]
|
49
|
+
assert cluster_index >= 0 && cluster_index < 4
|
50
|
+
# Distance to cluster centroid must be less than distance to any other
|
51
|
+
# centroid
|
52
|
+
min_distance = clusterer.distance(clusterer.centroids[cluster_index], item)
|
53
|
+
clusterer.centroids.each do |centroid|
|
54
|
+
assert clusterer.distance(centroid, item) >= min_distance
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_distance
|
59
|
+
clusterer = KMeans.new
|
60
|
+
# By default, distance returns the eucledian distance to the power of 2
|
61
|
+
assert_equal 2385, clusterer.distance(
|
62
|
+
[1, 10, "Chicago", 2],
|
63
|
+
[10, 10, "London", 50])
|
64
|
+
# Test new distance definition
|
65
|
+
manhattan_distance = lambda do |a, b|
|
66
|
+
dist = 0.0
|
67
|
+
a.each_index do |index|
|
68
|
+
if a[index].is_a?(Numeric) && b[index].is_a?(Numeric)
|
69
|
+
dist = dist + (a[index]-b[index]).abs
|
70
|
+
end
|
71
|
+
end
|
72
|
+
dist
|
73
|
+
end
|
74
|
+
clusterer.set_parameters({:distance_function => manhattan_distance})
|
75
|
+
assert_equal 57, clusterer.distance(
|
76
|
+
[1, 10, "Chicago", 2],
|
77
|
+
[10, 10, "London", 50])
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_max_iterations
|
81
|
+
data_set = DataSet.new(:data_items => @@data, :data_labels => ["X", "Y"])
|
82
|
+
clusterer = KMeans.new.
|
83
|
+
set_parameters({:max_iterations=>1}).
|
84
|
+
build(data_set, 4)
|
85
|
+
assert_equal 1, clusterer.iterations
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def draw_map(clusterer)
|
90
|
+
map = Array.new(11) {Array.new(11, 0)}
|
91
|
+
clusterer.clusters.each_index do |i|
|
92
|
+
clusterer.clusters[i].data_items.each do |point|
|
93
|
+
map[point.first][point.last]=(i+1)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
map.each { |row| puts row.inspect}
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|