affinity_propagation 0.1.0

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: 26f8c5ca0ecef4d7c7e89341a5039eadefef5871
4
+ data.tar.gz: cd466acdd9604ca6e547558438203b88283d21c9
5
+ SHA512:
6
+ metadata.gz: 72c71cf377b691208ffeb714022216bb3d4b259ec09157feded788f49858d8e5260538ab73dec78e1a126b9914c9eb8c251a0c44818235ba35c366b81f24cd28
7
+ data.tar.gz: 4fdcd1c0742094f837c95ebcd4366045006deb97dd358fe55a6575244ad07eb9207a3b5331cf9f8035fb2ec9dca2ad0b46d68188eb23d79b0a87646e79542ab5
data/.gitignore ADDED
@@ -0,0 +1,54 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+ # IDEs
53
+ .idea
54
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ affinity_propagation
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.4.1
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in affinity_propagation.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ affinity_propagation (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (5.1.4)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (~> 0.7)
12
+ minitest (~> 5.1)
13
+ tzinfo (~> 1.1)
14
+ concurrent-ruby (1.0.5)
15
+ diff-lcs (1.3)
16
+ i18n (0.8.6)
17
+ minitest (5.10.3)
18
+ rake (10.5.0)
19
+ rspec (3.6.0)
20
+ rspec-core (~> 3.6.0)
21
+ rspec-expectations (~> 3.6.0)
22
+ rspec-mocks (~> 3.6.0)
23
+ rspec-core (3.6.0)
24
+ rspec-support (~> 3.6.0)
25
+ rspec-expectations (3.6.0)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.6.0)
28
+ rspec-mocks (3.6.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.6.0)
31
+ rspec-support (3.6.0)
32
+ thread_safe (0.3.6)
33
+ tzinfo (1.2.3)
34
+ thread_safe (~> 0.1)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ activesupport
41
+ affinity_propagation!
42
+ bundler (~> 1.15)
43
+ rake (~> 10.0)
44
+ rspec (~> 3.0)
45
+
46
+ BUNDLED WITH
47
+ 1.15.4
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Shahbaz Javeed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # AffinityPropagation
2
+
3
+ This gem allows you to use the affinity propagation algorithm (see BJ Frey and D Dueck,
4
+ [Science 315, Feb 16, 2007](http://www.psi.toronto.edu/affinitypropagation/FreyDueckScience07.pdf)) to cluster arbitrary
5
+ data by providing the data and a block to calculate the similarity between any two data points in that list.
6
+
7
+ For more information on affinity propagation, visit the [FAQ over at University of Toronto](http://www.psi.toronto.edu/affinitypropagation/faq.html).
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'affinity_propagation'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install affinity_propagation
24
+
25
+ ## Usage
26
+
27
+ A simple example of usage is:
28
+
29
+ ```ruby
30
+ data = [1, 2, 3, 4, 500, 525, 602, 630, 1000, 1100, 1065, 1150]
31
+ apc = AffinityPropagation::Calculator.new(data) do |datum, exemplar|
32
+ # A recommended similarity function from the original paper:
33
+ # negative square of the distance between two points
34
+ # However you can choose anything here and even make it asymmetrical if your data needs it
35
+ -((datum - exemplar) ** 2)
36
+ end
37
+ apc.run(stable_iterations: 50)
38
+ apc.clusters
39
+ ```
40
+ This will result in the following (beautified) output:
41
+ ```ruby
42
+ [
43
+ {
44
+ :exemplar=>2,
45
+ :members=>[1, 2, 4]
46
+ },
47
+ {
48
+ :exemplar=>3,
49
+ :members=>[3]
50
+ },
51
+ {
52
+ :exemplar=>602,
53
+ :members=>[500, 525, 602, 630]
54
+ },
55
+ {
56
+ :exemplar=>1065,
57
+ :members=>[1000, 1100, 1065, 1150]
58
+ }
59
+ ]
60
+ ```
61
+ A more real-world example would be something like the following:
62
+ ```ruby
63
+ require 'active_support/all'
64
+ data = [
65
+ { value: 'A', timestamp: 1.year.ago },
66
+ { value: 'B', timestamp: 1.year.ago },
67
+ { value: 'C', timestamp: 1.year.ago },
68
+ { value: 'D', timestamp: 1.year.ago + 1.hour },
69
+ { value: 'E', timestamp: 1.year.ago + 1.day },
70
+
71
+ { value: 'a', timestamp: 1.month.ago },
72
+ { value: 'b', timestamp: 1.month.ago + 1.hour },
73
+ { value: 'c', timestamp: 1.month.ago + 1.day },
74
+
75
+ { value: 'd', timestamp: 2.weeks.ago },
76
+ { value: 'e', timestamp: 2.weeks.ago - 3.hours },
77
+ { value: 'f', timestamp: 2.weeks.ago - 10.hours },
78
+ { value: 'g', timestamp: 2.weeks.ago + 5.hours },
79
+
80
+ { value: 'h', timestamp: 24.hours.ago },
81
+ { value: 'i', timestamp: 40.hours.ago },
82
+ { value: 'j', timestamp: 12.hours.ago },
83
+ { value: 'k', timestamp: 12.minutes.ago }
84
+ ]
85
+ apc = AffinityPropagation::Calculator.new(data, lambda: 0.9) do |datum, exemplar|
86
+ difference = datum[:timestamp] - exemplar[:timestamp]
87
+
88
+ -(difference ** 2)
89
+ end
90
+ apc.run(stable_iterations: 25)
91
+ apc.clusters.map { |center| center[:members].map { |item| item[:value] } }
92
+ ```
93
+ ## Tweaking the Result
94
+ There are at least two ways to tweak the algorithm to generate clusters closer to any expectations you might have for
95
+ your data:
96
+
97
+ 1. Changing the dampening factor, `lambda`, which can be provided to the constructor for `AffinityPropagation::Calculator`.
98
+ This helps prevent numerical oscillations while updating the responsibilities and similarities matrices. This defaults
99
+ 0.75 but can be changed by specifying the lambda name argument as in the second example above.
100
+ 1. Assigning custom values to the self-similarity nodes in the similarity matrix generated when you instantiate the
101
+ `AffinityPropagation::Calculator` object. These self-similarity values are currently assigned a common value to
102
+ not suggest any one exemplar over another. This common value is the median of all similarities found in the entire
103
+ similarity matrix. This is not currently tweakable from your code but there's a plan to allow this to be tweaked.
104
+
105
+ ## Development
106
+
107
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
108
+
109
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
110
+
111
+ ## Contributing
112
+
113
+ This is my initial take on this problem and I have still to add a few usability, configuration and performance tweaks
114
+ but wanted to get this out there so people can use it. If you find it useful and are able to contribute to remove some
115
+ of the clearly vast set of issues that this codebase has, please send me a pull request and let's talk!
116
+
117
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sjaveed/affinity_propagation.
118
+
119
+ ## License
120
+
121
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "affinity_propagation/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "affinity_propagation"
8
+ spec.version = AffinityPropagation::VERSION
9
+ spec.authors = ["Shahbaz Javeed"]
10
+ spec.email = ["sjaveed@gmail.com"]
11
+
12
+ spec.summary = %q{An implementation of the affinity propagation clustering algorithm by Frey and Dueck}
13
+ spec.description = %q{
14
+ Affinity Propagation is a clustering algorithm that does not require pre-specifying the number of clusters like
15
+ k-means and other similar algorithms do. This is a ruby implementation of the original version defined by Frey and
16
+ Dueck in http://www.psi.toronto.edu/affinitypropagation/FreyDueckScience07.pdf
17
+ }
18
+ spec.homepage = "https://github.com/sjaveed/affinity_propagation"
19
+ spec.license = "MIT"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.15"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ spec.add_development_dependency "activesupport"
32
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "affinity_propagation"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require 'affinity_propagation/version'
2
+ require 'affinity_propagation/calculator'
3
+
4
+ module AffinityPropagation
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,180 @@
1
+ require 'matrix'
2
+
3
+ module AffinityPropagation
4
+ refine Array do
5
+ def median
6
+ relevant_elements = if size % 2 == 0
7
+ # Even number of items in this list => let's get the middle two and return their mean
8
+ slice(size / 2, 2)
9
+ else
10
+ slice(size / 2, 1)
11
+ end
12
+
13
+ relevant_elements.sum / relevant_elements.size
14
+ end
15
+ end
16
+
17
+ refine Matrix do
18
+ def to_matlab
19
+ output = row_vectors.map(&:to_a).map { |row| row.join(', ') }.join('; ')
20
+
21
+ "[#{output}]"
22
+ end
23
+ end
24
+
25
+ class Calculator
26
+ using AffinityPropagation
27
+
28
+ LAMBDA = 0.75
29
+
30
+ attr_accessor :raw_clusters
31
+
32
+ def initialize(data, lambda: LAMBDA, &block)
33
+ @data = data
34
+ @lambda = lambda
35
+
36
+ raise 'no block provide to calculate similarities within data!' unless block_given?
37
+
38
+ @similarities = similarity_matrix(&block)
39
+ reset
40
+ end
41
+
42
+ def reset
43
+ @raw_clusters = {}
44
+ @stable_cluster_iterations = 0
45
+ @total_iterations = 0
46
+
47
+ @availabilities = Matrix.zero(@data.size, @data.size)
48
+ @responsibilities = Matrix.zero(@data.size, @data.size)
49
+ end
50
+
51
+ def iterate
52
+ @availabilities = availability_matrix
53
+ @responsibilities = responsibility_matrix
54
+ update_clusters
55
+
56
+ @total_iterations += 1
57
+ end
58
+
59
+ def clusters
60
+ clusters = []
61
+
62
+ raw_clusters.each do |exemplar_id, data_ids|
63
+ clusters << {
64
+ exemplar: @data[exemplar_id],
65
+ members: data_ids.map { |datum_id| @data[datum_id] }
66
+ }
67
+ end
68
+
69
+ clusters
70
+ end
71
+
72
+ def run(iterations: 100, stable_iterations: 10)
73
+ iterate while @total_iterations < iterations && @stable_cluster_iterations < stable_iterations
74
+ end
75
+
76
+ private
77
+
78
+ def similarity_matrix(&block)
79
+ similarity_array = []
80
+ similarities = Matrix.build(@data.size, @data.size) do |row_idx, col_idx|
81
+ exemplar = @data[row_idx]
82
+ datum = @data[col_idx]
83
+
84
+ similarity = block.call(datum, exemplar)
85
+ similarity_array << similarity
86
+ similarity
87
+ end
88
+
89
+ median_similarity = similarity_array.median
90
+
91
+ # Matrices can't be modified once created so we have to use this hack
92
+ (0...@data.size).each { |idx| similarities.send(:[]=, idx, idx, median_similarity) }
93
+
94
+ similarities
95
+ end
96
+
97
+ def dampen(new_value, existing_value)
98
+ (1 - @lambda) * new_value + @lambda * existing_value
99
+ end
100
+
101
+ def responsibility_matrix
102
+ Matrix.build(@similarities.row_count, @similarities.column_count) do |row_idx, col_idx|
103
+ exemplar_idx = row_idx
104
+ datum_idx = col_idx
105
+
106
+ availability_column = @availabilities.column(col_idx).to_a
107
+ availability_column.slice!(exemplar_idx)
108
+ similarity_column = @similarities.column(col_idx).to_a
109
+ similarity_column.slice!(exemplar_idx)
110
+
111
+ availability_plus_similarity = []
112
+ availability_column.zip(similarity_column) { |data| availability_plus_similarity << data.sum }
113
+
114
+ dampen(@similarities[row_idx, col_idx] - availability_plus_similarity.max, @responsibilities[exemplar_idx, datum_idx])
115
+ end
116
+ end
117
+
118
+ def availability_matrix
119
+ Matrix.build(@responsibilities.row_count, @responsibilities.column_count) do |row_idx, col_idx|
120
+ exemplar_idx = row_idx
121
+ datum_idx = col_idx
122
+ responsibility_column = @responsibilities.row(exemplar_idx).to_a
123
+
124
+ if exemplar_idx == datum_idx
125
+ # self-availability
126
+ responsibility_column.slice!(exemplar_idx)
127
+
128
+ dampen(responsibility_column.inject(0) { |sum, item| sum += [0, item].max },@availabilities[exemplar_idx, datum_idx])
129
+ else
130
+ self_responsibility = @responsibilities[exemplar_idx, exemplar_idx]
131
+ if datum_idx > exemplar_idx
132
+ # Slice out the datum index first since in this case it won't affect the exemplar index
133
+ responsibility_column.slice!(datum_idx)
134
+ responsibility_column.slice!(exemplar_idx)
135
+ else
136
+ responsibility_column.slice!(exemplar_idx)
137
+ responsibility_column.slice!(datum_idx)
138
+ end
139
+
140
+ responsibility_column_sum = responsibility_column.inject(0) { |sum, item| sum += [0, item].max }
141
+
142
+ dampen([0, self_responsibility + responsibility_column_sum].min, @availabilities[exemplar_idx, datum_idx])
143
+ end
144
+ end
145
+ end
146
+
147
+ def identify_raw_clusters
148
+ clusters = {}
149
+
150
+ @data.each_with_index do |item, datum_idx|
151
+ availability_column = @availabilities.column(datum_idx).to_a
152
+ responsibility_column = @responsibilities.column(datum_idx).to_a
153
+
154
+ availability_and_responsibility = @data.size.times.map do |exemplar_idx|
155
+ availability_column[exemplar_idx] + responsibility_column[exemplar_idx]
156
+ end
157
+ exemplar_idx = availability_and_responsibility.index(availability_and_responsibility.max)
158
+
159
+ if clusters.key?(exemplar_idx)
160
+ clusters[exemplar_idx] << datum_idx
161
+ else
162
+ clusters[exemplar_idx] = [datum_idx]
163
+ end
164
+ end
165
+
166
+ clusters
167
+ end
168
+
169
+ def update_clusters
170
+ new_clusters = identify_raw_clusters
171
+
172
+ if new_clusters == @raw_clusters
173
+ @stable_cluster_iterations += 1
174
+ else
175
+ @raw_clusters = new_clusters
176
+ @stable_cluster_iterations = 0
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,3 @@
1
+ module AffinityPropagation
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: affinity_propagation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shahbaz Javeed
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: "\n Affinity Propagation is a clustering algorithm that does not require
70
+ pre-specifying the number of clusters like\n k-means and other similar algorithms
71
+ do. This is a ruby implementation of the original version defined by Frey and\n
72
+ \ Dueck in http://www.psi.toronto.edu/affinitypropagation/FreyDueckScience07.pdf\n
73
+ \ "
74
+ email:
75
+ - sjaveed@gmail.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".gitignore"
81
+ - ".rspec"
82
+ - ".ruby-gemset"
83
+ - ".ruby-version"
84
+ - ".travis.yml"
85
+ - Gemfile
86
+ - Gemfile.lock
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - affinity_propagation.gemspec
91
+ - bin/console
92
+ - bin/setup
93
+ - lib/affinity_propagation.rb
94
+ - lib/affinity_propagation/calculator.rb
95
+ - lib/affinity_propagation/version.rb
96
+ homepage: https://github.com/sjaveed/affinity_propagation
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.6.11
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: An implementation of the affinity propagation clustering algorithm by Frey
120
+ and Dueck
121
+ test_files: []