carmenere 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7c713abdf23594300b13fe5ea26f28f668256315
4
+ data.tar.gz: a2fea35a639d442ce30e881292ebadc320525f8e
5
+ SHA512:
6
+ metadata.gz: 63fb5e4e6e823df504690f770a9c3c7b0f84899b1d6faec790d06052517b0ba9b358c45d6bb6953d1ea698b1fcaad7029c6087cadb484cab3286ab6829b895eb
7
+ data.tar.gz: 47a6dfbc4b553adaaf09febfefedad64ca19f5d5de9a89206f80bf1062bf369a5610be5d48ef229e5a548da8442dfc0446e2ff00763f7664c1eba75ccefd559b
@@ -0,0 +1,28 @@
1
+ module Carmenere
2
+
3
+ class Algorithm
4
+
5
+ attr_reader :nodes
6
+
7
+ attr_reader :distance_matrix
8
+
9
+ def initialize k, nodes
10
+ @k = k
11
+ @nodes = Set.new(nodes).freeze
12
+ @clusters = Set.new(@nodes.map{ |n| Cluster.new([n]) })
13
+ @distance_matrix = @nodes.each.with_object({}) do |i, row|
14
+ row[i] = @nodes.each.with_object({}) do |j, col|
15
+ col[j] = i.distance(j) if i != j
16
+ end
17
+ end
18
+ end
19
+
20
+ # If a block is provided, it should yield each iteration of the clusters,
21
+ # otherwise, just returns the final state of the space.
22
+ def run
23
+ raise NotImplementedError.new
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'set'
2
+
3
+ module Carmenere
4
+
5
+ class Cluster < Set
6
+
7
+ # Minimum distance to other cluster
8
+ def distance other
9
+ self.reduce(nil) do |m, i|
10
+ other.reduce(m) do |m, j|
11
+ d = i.distance j
12
+ m = if m.nil? or m > d then d else m end
13
+ end
14
+ end
15
+ end
16
+
17
+ def to_s
18
+ "{" + self.reduce("") do |m, n|
19
+ m += ", " unless m.empty?
20
+ m += n.to_s
21
+ end + "}"
22
+ end
23
+
24
+ def inspect
25
+ self.to_s
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,57 @@
1
+ require 'set'
2
+
3
+ module Carmenere::KMeans
4
+
5
+ def self.centroids_eql? old_centroids, new_centroids
6
+ [old_centroids, new_centroids].each do |i|
7
+ unless i.is_a?(Set) and i.all?{ |j| j.is_a?(Carmenere::Node) }
8
+ raise TypeError.new("#{i.class} is not Set of Node")
9
+ end
10
+ end
11
+ old_centroids.count == new_centroids.count and old_centroids.all? do |i|
12
+ new_centroids.any? do |j|
13
+ i.attr_eql? j
14
+ end
15
+ end
16
+ end
17
+
18
+ class Algorithm < Carmenere::Algorithm
19
+
20
+ attr_reader :centroids
21
+
22
+ def initialize centroids, nodes, &mean
23
+ super centroids.count, nodes
24
+ @mean = mean
25
+ @centroids = centroids
26
+ end
27
+
28
+ def run
29
+ old_centroids = Set.new
30
+ centroids = Set.new @centroids
31
+ centroid_clusters = {nil => nil}
32
+ until Carmenere::KMeans::centroids_eql? old_centroids, centroids
33
+ old_centroids = centroids
34
+ node_centroids = @nodes.each.with_object({}) do |node, h|
35
+ h[node] = centroids.min_by do |c|
36
+ node.distance(c)
37
+ end
38
+ end
39
+ centroid_clusters = centroids.each.with_object({}) do |centroid, h|
40
+ nodes = node_centroids.lazy.select do |_, c|
41
+ c.attr_eql?(centroid)
42
+ end.map do |n, c|
43
+ n
44
+ end
45
+ h[centroid] = Carmenere::KMeans::Cluster.new nodes
46
+ end
47
+ yield centroid_clusters if block_given?
48
+ centroids = Set.new centroid_clusters.values.map do |cluster|
49
+ @mean.call(cluster)
50
+ end
51
+ end
52
+ centroid_clusters
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,6 @@
1
+ module Carmenere::KMeans
2
+
3
+ class Cluster < Carmenere::Cluster
4
+ end
5
+
6
+ end
@@ -0,0 +1,54 @@
1
+ module Carmenere
2
+
3
+ # Represents any node that can be used regardless of algorithm or cluster.
4
+ # The nodes attributes, problem domain-specific, are stored in
5
+ # Node#attributes.
6
+ class Node
7
+
8
+ include Comparable
9
+
10
+ attr_reader :name
11
+ attr_reader :attributes
12
+
13
+ def initialize name, attributes
14
+ @name = name
15
+ @cache = Hash.new
16
+ @attributes = attributes
17
+ end
18
+
19
+ def <=> node
20
+ @name <=> node.name
21
+ end
22
+
23
+ def hash
24
+ @name.hash
25
+ end
26
+
27
+ def to_s
28
+ @name
29
+ end
30
+
31
+ def inspect
32
+ @name
33
+ end
34
+
35
+ def distance other
36
+ c = @cache[other]
37
+ if c
38
+ c
39
+ else
40
+ @cache[other] = other.cache[self] = yield
41
+ end
42
+ end
43
+
44
+ def attr_eql? other
45
+ return @attributes == other.attributes
46
+ end
47
+
48
+ protected
49
+
50
+ attr_reader :cache
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,41 @@
1
+ module Carmenere::SingleLinkage
2
+
3
+ class Algorithm < Carmenere::Algorithm
4
+
5
+ attr_reader :nodes
6
+
7
+ attr_reader :distance_matrix
8
+
9
+ def initialize k, nodes
10
+ super k, nodes
11
+ end
12
+
13
+ # If step is true, then yields a copy of the array of clusters
14
+ def run
15
+ (nodes.size - @k).times do
16
+ a, b = self.closest_clusters
17
+ @clusters.delete a
18
+ @clusters.delete b
19
+ @clusters.add Cluster.new(a | b)
20
+ yield @clusters.to_a if block_given?
21
+ end
22
+ @clusters.to_a
23
+ end
24
+
25
+ def closest_clusters
26
+ @clusters.reduce(nil) do |m, i|
27
+ @clusters.reduce(m) do |m, j|
28
+ d = i.distance j
29
+ m = if i != j and (m.nil? or m[2] > d)
30
+ [i, j, d]
31
+ else
32
+ m
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,33 @@
1
+ module Carmenere::SingleLinkage
2
+
3
+ class Cluster < Carmenere::Cluster
4
+
5
+ # Minimum distance to other cluster
6
+ def distance other
7
+ self.reduce(nil) do |m, i|
8
+ other.reduce(m) do |m, j|
9
+ d = i.distance j
10
+ m = if m.nil? or m > d then d else m end
11
+ end
12
+ end
13
+ end
14
+
15
+ def to_s
16
+ "{" + self.reduce("") do |m, n|
17
+ m += ", " unless m.empty?
18
+ m += n.to_s
19
+ end + "}"
20
+ end
21
+
22
+ def inspect
23
+ self.to_s
24
+ end
25
+
26
+ def run
27
+ raise NotImplementedError.new
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
data/lib/carmenere.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'carmenere/algorithm'
2
+ require 'carmenere/cluster'
3
+ require 'carmenere/node'
4
+ require 'carmenere/kmeans/algorithm'
5
+ require 'carmenere/kmeans/cluster'
6
+ require 'carmenere/singlelinkage/algorithm'
7
+ require 'carmenere/singlelinkage/cluster'
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: carmenere
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Johnny Lee Othon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Cluster algorithms
14
+ email: jleeothon@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/carmenere.rb
20
+ - lib/carmenere/algorithm.rb
21
+ - lib/carmenere/cluster.rb
22
+ - lib/carmenere/kmeans/algorithm.rb
23
+ - lib/carmenere/kmeans/cluster.rb
24
+ - lib/carmenere/node.rb
25
+ - lib/carmenere/singlelinkage/algorithm.rb
26
+ - lib/carmenere/singlelinkage/cluster.rb
27
+ homepage: https://github.com/jleeothon/carmenere
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.2.2
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Single Linkage and K-means algorithms for clustering
51
+ test_files: []