cluster_eval 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +24 -0
- data/Gemfile +10 -0
- data/README.md +74 -0
- data/Rakefile +10 -0
- data/bin/cluster_eval +52 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/cluster_eval.gemspec +35 -0
- data/ext/ConfusionMatrix.cpp +191 -0
- data/ext/ConfusionMatrix.hpp +56 -0
- data/ext/clusteval.cpp +25 -0
- data/ext/extconf.rb +9 -0
- data/ext/prettyprint.hpp +445 -0
- data/lib/cluster_eval.rb +6 -0
- data/lib/cluster_eval/version.rb +3 -0
- data/tests/test_confusion_matrix_small.rb +54 -0
- data/tests/test_confusion_matrix_small_random.rb +54 -0
- data/tests/test_helper.rb +17 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9de67d89a54d6c2bf313a057797e3b56c74f0c6c
|
4
|
+
data.tar.gz: 4d5c3e6d8a5765078a214c8a14234e21526d8659
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8b451b4f4f0e123ff50bc276e5870969ac067348ac36789a25f8269fff769b2dbd00fbc76ab908478c39865e509804a94b15ba88228e4c6bfd2800d1ff56c1c
|
7
|
+
data.tar.gz: 662a605c5267911780356e2f4727f424addd0971457797075458d787ead697f1e92b534bae5100318b050bd39cdf160b4bddb4a489121bcc2f07f03a0c7774ca
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
language: ruby
|
2
|
+
compiler: gcc
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1.1
|
7
|
+
- ruby-head
|
8
|
+
|
9
|
+
before_install:
|
10
|
+
- gem install thor
|
11
|
+
- gem install rice
|
12
|
+
- gem install bundler
|
13
|
+
- gem install rake
|
14
|
+
- gem install minitest
|
15
|
+
- gem install minitest-reporters
|
16
|
+
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
17
|
+
- sudo apt-get update -qq
|
18
|
+
- sudo apt-get install -qq g++-4.8
|
19
|
+
- export CXX="g++-4.8"
|
20
|
+
- export CC="gcc-4.8"
|
21
|
+
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
|
22
|
+
|
23
|
+
install:
|
24
|
+
- cd ext/ && ruby extconf.rb && make
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# ClusterEval
|
2
|
+
|
3
|
+
Evaluates clusterings of a dataset using a variety of scores.
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/sbonisso/cluster_eval.svg?branch=master)](https://travis-ci.org/sbonisso/cluster_eval)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'cluster_eval'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install cluster_eval
|
22
|
+
|
23
|
+
Note, you will need a version of g++ installed that is compatible with [C++11](https://gcc.gnu.org/projects/cxx0x.html).
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
This can be used as a library, or through the installed command line program `cluster_eval`.
|
28
|
+
|
29
|
+
```
|
30
|
+
$ cluster_eval help eval
|
31
|
+
Usage:
|
32
|
+
cluster_eval eval [options]
|
33
|
+
|
34
|
+
Options:
|
35
|
+
-a, [--cluster-file-a=CLUSTER_FILE_A] # cluster file A
|
36
|
+
-b, [--cluster-file-b=CLUSTER_FILE_B] # cluster file B
|
37
|
+
-y, [--type=TYPE] # type of index to compute
|
38
|
+
```
|
39
|
+
|
40
|
+
The `type` argument specifies the index to compute either the [Rand index](http://en.wikipedia.org/wiki/Rand_index), [Jaccard index](http://en.wikipedia.org/wiki/Jaccard_index), [Fowlkes-Mallows index](http://en.wikipedia.org/wiki/Fowlkes%E2%80%93Mallows_index), adjusted [Rand index](http://en.wikipedia.org/wiki/Rand_index#Adjusted_Rand_index), or all.
|
41
|
+
It can take on values of: [rand/jaccard/fm/adj_rand/all]
|
42
|
+
|
43
|
+
Each cluster file must contain two columns of integers, the first column representing the sample ID, the second column the cluster ID. The sample IDs need not be sorted, but must contain all sample IDs from 0 to n samples.
|
44
|
+
|
45
|
+
For example if we have two files clust_a.tab and clust_b.tab, we could run the following:
|
46
|
+
|
47
|
+
```
|
48
|
+
$ cluster_eval eval -a clust_a.tab -b clust_b.tab -y rand
|
49
|
+
0.643
|
50
|
+
$ cluster_eval eval -a clust_a.tab -b clust_b.tab -y jaccard
|
51
|
+
0.286
|
52
|
+
$ cluster_eval eval -a clust_a.tab -b clust_b.tab -y fm
|
53
|
+
0.456
|
54
|
+
$ cluster_eval eval -a clust_a.tab -b clust_b.tab -y adj_rand
|
55
|
+
0.200
|
56
|
+
$ cluster_eval eval -a clust_a.tab -b clust_b.tab -y all
|
57
|
+
rand jaccard fm adj_rand
|
58
|
+
0.643 0.286 0.456 0.200
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
## Development
|
63
|
+
|
64
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
65
|
+
|
66
|
+
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` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
1. Fork it ( https://github.com/[my-github-username]/cluster_eval/fork )
|
71
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
72
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
73
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
74
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/cluster_eval
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'cluster_eval'
|
5
|
+
|
6
|
+
class CLUSTER_EVAL_BIN < Thor
|
7
|
+
|
8
|
+
method_option :cluster_file_a, :aliases => "-a", :desc => "cluster file A"
|
9
|
+
method_option :cluster_file_b, :aliases => "-b", :desc => "cluster file B"
|
10
|
+
method_option :type, :aliases => "-y",
|
11
|
+
:desc => "type of index to compute [rand/jaccard/fm/adj_rand/all]"
|
12
|
+
desc 'eval [options]', 'evaluate clusterings'
|
13
|
+
def eval()
|
14
|
+
clust_a_f = options["cluster_file_a"]
|
15
|
+
clust_b_f = options["cluster_file_b"]
|
16
|
+
index_type = options["type"]
|
17
|
+
|
18
|
+
clust_a_h = {}
|
19
|
+
IO.foreach(clust_a_f) do |line|
|
20
|
+
ary = line.chomp.split("\t")
|
21
|
+
clust_a_h[ary[0].to_i] = ary[1].to_i
|
22
|
+
end
|
23
|
+
clust_b_h = {}
|
24
|
+
IO.foreach(clust_b_f) do |line|
|
25
|
+
ary = line.chomp.split("\t")
|
26
|
+
clust_b_h[ary[0].to_i] = ary[1].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
cm = ClusterEval::ConfusionMatrix.new(clust_a_h, clust_b_h)
|
30
|
+
|
31
|
+
if index_type == "rand" then
|
32
|
+
puts '%0.3f' % cm.get_rand_index
|
33
|
+
elsif index_type == "jaccard" then
|
34
|
+
puts '%0.3f' % cm.get_jaccard_index
|
35
|
+
elsif index_type == "fm" then
|
36
|
+
puts '%0.3f' % cm.get_fm_index
|
37
|
+
elsif index_type == "adj_rand" then
|
38
|
+
puts '%0.3f' % cm.get_adj_rand_index
|
39
|
+
elsif index_type == "all" then
|
40
|
+
puts ['rand', 'jaccard', 'fm', 'adj_rand'].join("\t")
|
41
|
+
puts ['%0.3f' % cm.get_rand_index,
|
42
|
+
'%0.3f' % cm.get_jaccard_index,
|
43
|
+
'%0.3f' % cm.get_fm_index,
|
44
|
+
'%0.3f' % cm.get_adj_rand_index].join("\t")
|
45
|
+
else
|
46
|
+
raise 'invalid index type'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
#
|
52
|
+
CLUSTER_EVAL_BIN.start(ARGV)
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cluster_eval"
|
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
|
data/bin/setup
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cluster_eval/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cluster_eval"
|
8
|
+
spec.version = ClusterEval::VERSION
|
9
|
+
spec.authors = ["sbonisso"]
|
10
|
+
spec.email = ["sbonisso@ucsd.edu"]
|
11
|
+
|
12
|
+
# if spec.respond_to?(:metadata)
|
13
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
|
14
|
+
# end
|
15
|
+
|
16
|
+
spec.summary = %q{Evaluation of clusterings}
|
17
|
+
spec.description = %q{Evaluate partitionings of different clustering approaches. Provides different metrics to use.}
|
18
|
+
spec.homepage = "https://github.com/sbonisso/cluster_eval"
|
19
|
+
spec.license = "MIT"
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
spec.bindir = "bin"
|
23
|
+
#spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.executables = "cluster_eval"
|
25
|
+
spec.require_paths = ["lib", "ext"]
|
26
|
+
spec.extensions = ["ext/extconf.rb"]
|
27
|
+
|
28
|
+
spec.add_dependency "thor", '~> 0.19'
|
29
|
+
spec.add_dependency "rice", '~> 1.7'
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
32
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
33
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
34
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.0"
|
35
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
#include "ConfusionMatrix.hpp"
|
2
|
+
/**
|
3
|
+
*
|
4
|
+
*/
|
5
|
+
ConfusionMatrix::ConfusionMatrix() {
|
6
|
+
a_ = 0;
|
7
|
+
b_ = 0;
|
8
|
+
c_ = 0;
|
9
|
+
d_ = 0;
|
10
|
+
}
|
11
|
+
/**
|
12
|
+
*
|
13
|
+
*/
|
14
|
+
ConfusionMatrix::~ConfusionMatrix() {}
|
15
|
+
/**
|
16
|
+
* each ruby hash are pairs of {read_id => clust_id}
|
17
|
+
*/
|
18
|
+
ConfusionMatrix::ConfusionMatrix(Rice::Hash c1_hsh, Rice::Hash c2_hsh) {
|
19
|
+
a_ = 0;
|
20
|
+
b_ = 0;
|
21
|
+
c_ = 0;
|
22
|
+
d_ = 0;
|
23
|
+
populate_vect(c1_hsh, clust_1_);
|
24
|
+
populate_vect(c2_hsh, clust_2_);
|
25
|
+
//
|
26
|
+
int max_clust_1 = 0;
|
27
|
+
for(int i = 0; i < (int)clust_1_.size(); i++) {
|
28
|
+
if(clust_1_[i] > max_clust_1) {
|
29
|
+
max_clust_1 = clust_1_[i];
|
30
|
+
}
|
31
|
+
}
|
32
|
+
int max_clust_2 = 0;
|
33
|
+
for(int i = 0; i < (int)clust_2_.size(); i++) {
|
34
|
+
if(clust_2_[i] > max_clust_2) {
|
35
|
+
max_clust_2 = clust_2_[i];
|
36
|
+
}
|
37
|
+
}
|
38
|
+
// set up clust_id => [id_1, id_2, ...]
|
39
|
+
clust_map_1_.resize(max_clust_1+1);
|
40
|
+
clust_map_2_.resize(max_clust_2+1);
|
41
|
+
//
|
42
|
+
for(int i = 0; i < (int)clust_1_.size(); i++) {
|
43
|
+
clust_map_1_[clust_1_[i]].push_back(i);
|
44
|
+
}
|
45
|
+
for(int i = 0; i < (int)clust_2_.size(); i++) {
|
46
|
+
clust_map_2_[clust_2_[i]].push_back(i);
|
47
|
+
}
|
48
|
+
//
|
49
|
+
compute_confusion_matrix();
|
50
|
+
}
|
51
|
+
/**
|
52
|
+
* given a ruby hash, populate a vector of read_id as index, cluster_id as value
|
53
|
+
*/
|
54
|
+
void ConfusionMatrix::populate_vect(Rice::Hash &hsh,
|
55
|
+
std::vector<int> &clust_v) {
|
56
|
+
int len = hsh.size();
|
57
|
+
clust_v.resize(len);
|
58
|
+
//
|
59
|
+
Rice::Hash::iterator it = hsh.begin();
|
60
|
+
Rice::Hash::iterator it_end = hsh.end();
|
61
|
+
int i = 0;
|
62
|
+
for(; it != it_end; ++it) {
|
63
|
+
Rice::Hash::Entry en(*it);
|
64
|
+
int x = FIX2INT(en.key);
|
65
|
+
int y = FIX2INT(en.value.value());
|
66
|
+
clust_v[x] = y;
|
67
|
+
i++;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
/**
|
71
|
+
*
|
72
|
+
*/
|
73
|
+
void ConfusionMatrix::print_matrix() {
|
74
|
+
std::cout<<a_<<"\t"<<b_<<"\t"<<c_<<"\t"<<d_<<std::endl;
|
75
|
+
}
|
76
|
+
/**
|
77
|
+
* return ruby array with contents of confusion matrix: [a, b, c, d]
|
78
|
+
*/
|
79
|
+
Rice::Array ConfusionMatrix::get_confusion_matrix() {
|
80
|
+
Rice::Array ary;
|
81
|
+
ary.push(a_);
|
82
|
+
ary.push(b_);
|
83
|
+
ary.push(c_);
|
84
|
+
ary.push(d_);
|
85
|
+
return ary;
|
86
|
+
}
|
87
|
+
/**
|
88
|
+
* compute confusion matrix entries a, b, c, and d
|
89
|
+
* for the matrix:
|
90
|
+
* ---------
|
91
|
+
* | a | b |
|
92
|
+
* ---------
|
93
|
+
* | c | d |
|
94
|
+
* -------
|
95
|
+
*/
|
96
|
+
void ConfusionMatrix::compute_confusion_matrix() {
|
97
|
+
std::pair<int,int> v1 = int_and_diff(clust_1_, clust_map_2_);
|
98
|
+
std::pair<int,int> v2 = int_and_diff(clust_2_, clust_map_1_);
|
99
|
+
//
|
100
|
+
int n = (int)clust_1_.size();
|
101
|
+
a_ = v1.first;
|
102
|
+
c_ = v1.second;
|
103
|
+
b_ = v2.second;
|
104
|
+
d_ = ((n*(n-1))/2) - (a_+c_+b_);
|
105
|
+
}
|
106
|
+
/**
|
107
|
+
* return Rand index: (a+b)/(a+b+c+d)
|
108
|
+
*/
|
109
|
+
double ConfusionMatrix::get_rand_index() {
|
110
|
+
return (double)(a_+d_)/(double)(a_+b_+c_+d_);
|
111
|
+
}
|
112
|
+
/**
|
113
|
+
* return Jaccard index: a/(a+c+b)
|
114
|
+
*/
|
115
|
+
double ConfusionMatrix::get_jaccard_index() {
|
116
|
+
return (double)(a_)/(double)(a_+c_+b_);
|
117
|
+
}
|
118
|
+
/**
|
119
|
+
* returns Fowlkes-Mallows (FM) index: a/sqrt((a+c)*(a+b))
|
120
|
+
*/
|
121
|
+
double ConfusionMatrix::get_fm_index() {
|
122
|
+
double d1 = (double)(a_+c_);
|
123
|
+
double d2 = (double)(a_+b_);
|
124
|
+
return (double)a_/sqrt(d1*d2);
|
125
|
+
}
|
126
|
+
/**
|
127
|
+
* returns the adjusted Rand index using the confusion matrix
|
128
|
+
*/
|
129
|
+
double ConfusionMatrix::get_adj_rand_index() {
|
130
|
+
double total = a_+b_+c_+d_;
|
131
|
+
double n1 = ((b_+a_)*(c_+a_) + (b_+d_)*(c_+d_));
|
132
|
+
|
133
|
+
double num = (total*((double)(a_+d_))) - n1;
|
134
|
+
double denom = (total*total - n1);
|
135
|
+
return (num/denom);
|
136
|
+
}
|
137
|
+
/**
|
138
|
+
* get counts of: <intersection, difference> between the vector of
|
139
|
+
* cluster assignments (A) and cluster assignment groupings (B)
|
140
|
+
*/
|
141
|
+
std::pair<int,int> ConfusionMatrix::int_and_diff(std::vector<int> clust_v,
|
142
|
+
std::vector<std::vector<int>> clust_map) {
|
143
|
+
//
|
144
|
+
int intsect = 0;
|
145
|
+
int diff = 0;
|
146
|
+
for(int i = 0; i < (int)clust_map.size(); i++) {
|
147
|
+
//
|
148
|
+
std::vector<int> vs = clust_map[i];
|
149
|
+
int len = (int) vs.size();
|
150
|
+
for(int i = 0; i < len; i++) {
|
151
|
+
int val_i = vs[i];
|
152
|
+
for(int j = 0; j < len; j++) {
|
153
|
+
if(i < j) {
|
154
|
+
int val_j = vs[j];
|
155
|
+
bool t1 = clust_v[val_i] == clust_v[val_j];
|
156
|
+
if(t1) { intsect++; }
|
157
|
+
else { diff++; }
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
//
|
163
|
+
return std::pair<int,int>(intsect, diff);
|
164
|
+
}
|
165
|
+
/**
|
166
|
+
* compute confusion matrix in naive N**2 time, used for comparing
|
167
|
+
* (i.e., sanity check) testing results
|
168
|
+
*/
|
169
|
+
Rice::Array ConfusionMatrix::get_confusion_matrix_naive() {
|
170
|
+
int len = (int)clust_1_.size();
|
171
|
+
int a = 0; int b = 0; int c = 0; int d = 0;
|
172
|
+
for(int i = 0; i < len; i++) {
|
173
|
+
for(int j = 0; j < len; j++) {
|
174
|
+
if(i >= j) { continue; }
|
175
|
+
bool t1 = (clust_1_[i] == clust_1_[j]);
|
176
|
+
bool t2 = (clust_2_[i] == clust_2_[j]);
|
177
|
+
if(t1 && t2) { a += 1; }
|
178
|
+
else if(!t1 && !t2) { d += 1; }
|
179
|
+
else if(!t1 && t2) { c += 1; }
|
180
|
+
else if(t1 && !t2) { b += 1; }
|
181
|
+
else {}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
//
|
185
|
+
Rice::Array ary;
|
186
|
+
ary.push(a);
|
187
|
+
ary.push(b);
|
188
|
+
ary.push(c);
|
189
|
+
ary.push(d);
|
190
|
+
return ary;
|
191
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#ifndef CONFUSION_MATRIX_HPP
|
2
|
+
#define CONFUSION_MATRIX_HPP
|
3
|
+
|
4
|
+
#include <iostream>
|
5
|
+
#include <string>
|
6
|
+
#include <vector>
|
7
|
+
#include <utility>
|
8
|
+
#include <cmath>
|
9
|
+
#include "prettyprint.hpp"
|
10
|
+
|
11
|
+
#ifndef CPPPROG
|
12
|
+
#include "rice/String.hpp"
|
13
|
+
#include "rice/Array.hpp"
|
14
|
+
#include "rice/Hash.hpp"
|
15
|
+
#endif
|
16
|
+
|
17
|
+
class ConfusionMatrix {
|
18
|
+
protected:
|
19
|
+
|
20
|
+
void compute_confusion_matrix();
|
21
|
+
int a_;
|
22
|
+
int b_;
|
23
|
+
int c_;
|
24
|
+
int d_;
|
25
|
+
//
|
26
|
+
std::vector<int> clust_1_;
|
27
|
+
std::vector<int> clust_2_;
|
28
|
+
//
|
29
|
+
std::vector<std::vector<int> > clust_map_1_;
|
30
|
+
std::vector<std::vector<int> > clust_map_2_;
|
31
|
+
// fill in clust_vect from hash (ruby hash)
|
32
|
+
void populate_vect(Rice::Hash &hsh, std::vector<int> &clust_v);
|
33
|
+
//
|
34
|
+
std::pair<int,int> int_and_diff(std::vector<int> clust_v,
|
35
|
+
std::vector<std::vector<int>> clust_map);
|
36
|
+
|
37
|
+
public:
|
38
|
+
ConfusionMatrix();
|
39
|
+
ConfusionMatrix(Rice::Hash c1_hsh, Rice::Hash c2_hsh);
|
40
|
+
virtual ~ConfusionMatrix();
|
41
|
+
|
42
|
+
void print_matrix();
|
43
|
+
|
44
|
+
Rice::Array get_confusion_matrix();
|
45
|
+
// for computing various indices
|
46
|
+
double get_rand_index();
|
47
|
+
double get_jaccard_index();
|
48
|
+
double get_fm_index();
|
49
|
+
double get_adj_rand_index();
|
50
|
+
|
51
|
+
// compute using naive N^2 for testing purposes
|
52
|
+
Rice::Array get_confusion_matrix_naive();
|
53
|
+
|
54
|
+
};
|
55
|
+
|
56
|
+
#endif
|
data/ext/clusteval.cpp
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#include "rice/Data_Type.hpp"
|
2
|
+
#include "rice/Constructor.hpp"
|
3
|
+
#include "rice/Hash.hpp"
|
4
|
+
#include "ConfusionMatrix.hpp"
|
5
|
+
|
6
|
+
using namespace Rice;
|
7
|
+
|
8
|
+
extern "C"
|
9
|
+
|
10
|
+
void Init_ClusterEval()
|
11
|
+
{
|
12
|
+
Module rb_cModule = define_module("ClusterEval");
|
13
|
+
Data_Type<ConfusionMatrix> rb_cConfusionMatrix =
|
14
|
+
define_class_under<ConfusionMatrix>(rb_cModule,"ConfusionMatrix")
|
15
|
+
.define_constructor(Constructor<ConfusionMatrix>())
|
16
|
+
//.define_constructor(Constructor<ConfusionMatrix,std::string,std::string>())
|
17
|
+
.define_constructor(Constructor<ConfusionMatrix,Hash,Hash>())
|
18
|
+
.define_method("get_rand_index", &ConfusionMatrix::get_rand_index)
|
19
|
+
.define_method("get_jaccard_index", &ConfusionMatrix::get_jaccard_index)
|
20
|
+
.define_method("get_fm_index", &ConfusionMatrix::get_fm_index)
|
21
|
+
.define_method("get_adj_rand_index", &ConfusionMatrix::get_adj_rand_index)
|
22
|
+
.define_method("print_matrix", &ConfusionMatrix::print_matrix)
|
23
|
+
.define_method("get_confusion_matrix", &ConfusionMatrix::get_confusion_matrix)
|
24
|
+
.define_method("get_confusion_matrix_naive", &ConfusionMatrix::get_confusion_matrix_naive);
|
25
|
+
}
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'mkmf-rice'
|
2
|
+
|
3
|
+
$CPPFLAGS << ' -std=c++11'
|
4
|
+
$CPPFLAGS << ' -Wno-deprecated'
|
5
|
+
|
6
|
+
$warnflags = $warnflags.split("-Wimplicit-function-declaration").join(" ")
|
7
|
+
$warnflags = $warnflags.split("-Wdeclaration-after-statement").join(" ")
|
8
|
+
|
9
|
+
create_makefile('ClusterEval')
|
data/ext/prettyprint.hpp
ADDED
@@ -0,0 +1,445 @@
|
|
1
|
+
// Copyright Louis Delacroix 2010 - 2014.
|
2
|
+
// Distributed under the Boost Software License, Version 1.0.
|
3
|
+
// (See accompanying file LICENSE_1_0.txt or copy at
|
4
|
+
// http://www.boost.org/LICENSE_1_0.txt)
|
5
|
+
//
|
6
|
+
// A pretty printing library for C++
|
7
|
+
//
|
8
|
+
// Usage:
|
9
|
+
// Include this header, and operator<< will "just work".
|
10
|
+
|
11
|
+
#ifndef H_PRETTY_PRINT
|
12
|
+
#define H_PRETTY_PRINT
|
13
|
+
|
14
|
+
#include <cstddef>
|
15
|
+
#include <iterator>
|
16
|
+
#include <memory>
|
17
|
+
#include <ostream>
|
18
|
+
#include <set>
|
19
|
+
#include <tuple>
|
20
|
+
#include <type_traits>
|
21
|
+
#include <unordered_set>
|
22
|
+
#include <utility>
|
23
|
+
#include <valarray>
|
24
|
+
|
25
|
+
namespace pretty_print
|
26
|
+
{
|
27
|
+
namespace detail
|
28
|
+
{
|
29
|
+
// SFINAE type trait to detect whether T::const_iterator exists.
|
30
|
+
|
31
|
+
struct sfinae_base
|
32
|
+
{
|
33
|
+
using yes = char;
|
34
|
+
using no = yes[2];
|
35
|
+
};
|
36
|
+
|
37
|
+
template <typename T>
|
38
|
+
struct has_const_iterator : private sfinae_base
|
39
|
+
{
|
40
|
+
private:
|
41
|
+
template <typename C> static yes & test(typename C::const_iterator*);
|
42
|
+
template <typename C> static no & test(...);
|
43
|
+
public:
|
44
|
+
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
|
45
|
+
using type = T;
|
46
|
+
};
|
47
|
+
|
48
|
+
template <typename T>
|
49
|
+
struct has_begin_end : private sfinae_base
|
50
|
+
{
|
51
|
+
private:
|
52
|
+
template <typename C>
|
53
|
+
static yes & f(typename std::enable_if<
|
54
|
+
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
|
55
|
+
typename C::const_iterator(C::*)() const>::value>::type *);
|
56
|
+
|
57
|
+
template <typename C> static no & f(...);
|
58
|
+
|
59
|
+
template <typename C>
|
60
|
+
static yes & g(typename std::enable_if<
|
61
|
+
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
|
62
|
+
typename C::const_iterator(C::*)() const>::value, void>::type*);
|
63
|
+
|
64
|
+
template <typename C> static no & g(...);
|
65
|
+
|
66
|
+
public:
|
67
|
+
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
|
68
|
+
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
|
69
|
+
};
|
70
|
+
|
71
|
+
} // namespace detail
|
72
|
+
|
73
|
+
|
74
|
+
// Holds the delimiter values for a specific character type
|
75
|
+
|
76
|
+
template <typename TChar>
|
77
|
+
struct delimiters_values
|
78
|
+
{
|
79
|
+
using char_type = TChar;
|
80
|
+
const char_type * prefix;
|
81
|
+
const char_type * delimiter;
|
82
|
+
const char_type * postfix;
|
83
|
+
};
|
84
|
+
|
85
|
+
|
86
|
+
// Defines the delimiter values for a specific container and character type
|
87
|
+
|
88
|
+
template <typename T, typename TChar>
|
89
|
+
struct delimiters
|
90
|
+
{
|
91
|
+
using type = delimiters_values<TChar>;
|
92
|
+
static const type values;
|
93
|
+
};
|
94
|
+
|
95
|
+
|
96
|
+
// Functor to print containers. You can use this directly if you want
|
97
|
+
// to specificy a non-default delimiters type. The printing logic can
|
98
|
+
// be customized by specializing the nested template.
|
99
|
+
|
100
|
+
template <typename T,
|
101
|
+
typename TChar = char,
|
102
|
+
typename TCharTraits = ::std::char_traits<TChar>,
|
103
|
+
typename TDelimiters = delimiters<T, TChar>>
|
104
|
+
struct print_container_helper
|
105
|
+
{
|
106
|
+
using delimiters_type = TDelimiters;
|
107
|
+
using ostream_type = std::basic_ostream<TChar, TCharTraits>;
|
108
|
+
|
109
|
+
template <typename U>
|
110
|
+
struct printer
|
111
|
+
{
|
112
|
+
static void print_body(const U & c, ostream_type & stream)
|
113
|
+
{
|
114
|
+
using std::begin;
|
115
|
+
using std::end;
|
116
|
+
|
117
|
+
auto it = begin(c);
|
118
|
+
const auto the_end = end(c);
|
119
|
+
|
120
|
+
if (it != the_end)
|
121
|
+
{
|
122
|
+
for ( ; ; )
|
123
|
+
{
|
124
|
+
stream << *it;
|
125
|
+
|
126
|
+
if (++it == the_end) break;
|
127
|
+
|
128
|
+
if (delimiters_type::values.delimiter != NULL)
|
129
|
+
stream << delimiters_type::values.delimiter;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
};
|
134
|
+
|
135
|
+
print_container_helper(const T & container)
|
136
|
+
: container_(container)
|
137
|
+
{ }
|
138
|
+
|
139
|
+
inline void operator()(ostream_type & stream) const
|
140
|
+
{
|
141
|
+
if (delimiters_type::values.prefix != NULL)
|
142
|
+
stream << delimiters_type::values.prefix;
|
143
|
+
|
144
|
+
printer<T>::print_body(container_, stream);
|
145
|
+
|
146
|
+
if (delimiters_type::values.postfix != NULL)
|
147
|
+
stream << delimiters_type::values.postfix;
|
148
|
+
}
|
149
|
+
|
150
|
+
private:
|
151
|
+
const T & container_;
|
152
|
+
};
|
153
|
+
|
154
|
+
// Specialization for pairs
|
155
|
+
|
156
|
+
template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
|
157
|
+
template <typename T1, typename T2>
|
158
|
+
struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::pair<T1, T2>>
|
159
|
+
{
|
160
|
+
using ostream_type = print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
|
161
|
+
|
162
|
+
static void print_body(const std::pair<T1, T2> & c, ostream_type & stream)
|
163
|
+
{
|
164
|
+
stream << c.first;
|
165
|
+
if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
|
166
|
+
stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
|
167
|
+
stream << c.second;
|
168
|
+
}
|
169
|
+
};
|
170
|
+
|
171
|
+
// Specialization for tuples
|
172
|
+
|
173
|
+
template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
|
174
|
+
template <typename ...Args>
|
175
|
+
struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::tuple<Args...>>
|
176
|
+
{
|
177
|
+
using ostream_type = print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
|
178
|
+
using element_type = std::tuple<Args...>;
|
179
|
+
|
180
|
+
template <std::size_t I> struct Int { };
|
181
|
+
|
182
|
+
static void print_body(const element_type & c, ostream_type & stream)
|
183
|
+
{
|
184
|
+
tuple_print(c, stream, Int<0>());
|
185
|
+
}
|
186
|
+
|
187
|
+
static void tuple_print(const element_type &, ostream_type &, Int<sizeof...(Args)>)
|
188
|
+
{
|
189
|
+
}
|
190
|
+
|
191
|
+
static void tuple_print(const element_type & c, ostream_type & stream,
|
192
|
+
typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type)
|
193
|
+
{
|
194
|
+
stream << std::get<0>(c);
|
195
|
+
tuple_print(c, stream, Int<1>());
|
196
|
+
}
|
197
|
+
|
198
|
+
template <std::size_t N>
|
199
|
+
static void tuple_print(const element_type & c, ostream_type & stream, Int<N>)
|
200
|
+
{
|
201
|
+
if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
|
202
|
+
stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
|
203
|
+
|
204
|
+
stream << std::get<N>(c);
|
205
|
+
|
206
|
+
tuple_print(c, stream, Int<N + 1>());
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
// Prints a print_container_helper to the specified stream.
|
211
|
+
|
212
|
+
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
|
213
|
+
inline std::basic_ostream<TChar, TCharTraits> & operator<<(
|
214
|
+
std::basic_ostream<TChar, TCharTraits> & stream,
|
215
|
+
const print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper)
|
216
|
+
{
|
217
|
+
helper(stream);
|
218
|
+
return stream;
|
219
|
+
}
|
220
|
+
|
221
|
+
|
222
|
+
// Basic is_container template; specialize to derive from std::true_type for all desired container types
|
223
|
+
|
224
|
+
template <typename T>
|
225
|
+
struct is_container : public std::integral_constant<bool,
|
226
|
+
detail::has_const_iterator<T>::value &&
|
227
|
+
detail::has_begin_end<T>::beg_value &&
|
228
|
+
detail::has_begin_end<T>::end_value> { };
|
229
|
+
|
230
|
+
template <typename T, std::size_t N>
|
231
|
+
struct is_container<T[N]> : std::true_type { };
|
232
|
+
|
233
|
+
template <std::size_t N>
|
234
|
+
struct is_container<char[N]> : std::false_type { };
|
235
|
+
|
236
|
+
template <typename T>
|
237
|
+
struct is_container<std::valarray<T>> : std::true_type { };
|
238
|
+
|
239
|
+
template <typename T1, typename T2>
|
240
|
+
struct is_container<std::pair<T1, T2>> : std::true_type { };
|
241
|
+
|
242
|
+
template <typename ...Args>
|
243
|
+
struct is_container<std::tuple<Args...>> : std::true_type { };
|
244
|
+
|
245
|
+
|
246
|
+
// Default delimiters
|
247
|
+
|
248
|
+
template <typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
|
249
|
+
template <typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" };
|
250
|
+
template <typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
|
251
|
+
template <typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"[", L", ", L"]" };
|
252
|
+
|
253
|
+
|
254
|
+
// Delimiters for (multi)set and unordered_(multi)set
|
255
|
+
|
256
|
+
template <typename T, typename TComp, typename TAllocator>
|
257
|
+
struct delimiters< ::std::set<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
|
258
|
+
|
259
|
+
template <typename T, typename TComp, typename TAllocator>
|
260
|
+
const delimiters_values<char> delimiters< ::std::set<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
|
261
|
+
|
262
|
+
template <typename T, typename TComp, typename TAllocator>
|
263
|
+
struct delimiters< ::std::set<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
264
|
+
|
265
|
+
template <typename T, typename TComp, typename TAllocator>
|
266
|
+
const delimiters_values<wchar_t> delimiters< ::std::set<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
|
267
|
+
|
268
|
+
template <typename T, typename TComp, typename TAllocator>
|
269
|
+
struct delimiters< ::std::multiset<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
|
270
|
+
|
271
|
+
template <typename T, typename TComp, typename TAllocator>
|
272
|
+
const delimiters_values<char> delimiters< ::std::multiset<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
|
273
|
+
|
274
|
+
template <typename T, typename TComp, typename TAllocator>
|
275
|
+
struct delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
276
|
+
|
277
|
+
template <typename T, typename TComp, typename TAllocator>
|
278
|
+
const delimiters_values<wchar_t> delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
|
279
|
+
|
280
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
281
|
+
struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
|
282
|
+
|
283
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
284
|
+
const delimiters_values<char> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
|
285
|
+
|
286
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
287
|
+
struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
288
|
+
|
289
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
290
|
+
const delimiters_values<wchar_t> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
|
291
|
+
|
292
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
293
|
+
struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
|
294
|
+
|
295
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
296
|
+
const delimiters_values<char> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
|
297
|
+
|
298
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
299
|
+
struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
300
|
+
|
301
|
+
template <typename T, typename THash, typename TEqual, typename TAllocator>
|
302
|
+
const delimiters_values<wchar_t> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
|
303
|
+
|
304
|
+
|
305
|
+
// Delimiters for pair and tuple
|
306
|
+
|
307
|
+
template <typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
|
308
|
+
template <typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
|
309
|
+
template <typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
310
|
+
template <typename T1, typename T2> const delimiters_values<wchar_t> delimiters< ::std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };
|
311
|
+
|
312
|
+
template <typename ...Args> struct delimiters<std::tuple<Args...>, char> { static const delimiters_values<char> values; };
|
313
|
+
template <typename ...Args> const delimiters_values<char> delimiters<std::tuple<Args...>, char>::values = { "(", ", ", ")" };
|
314
|
+
template <typename ...Args> struct delimiters< ::std::tuple<Args...>, wchar_t> { static const delimiters_values<wchar_t> values; };
|
315
|
+
template <typename ...Args> const delimiters_values<wchar_t> delimiters< ::std::tuple<Args...>, wchar_t>::values = { L"(", L", ", L")" };
|
316
|
+
|
317
|
+
|
318
|
+
// Type-erasing helper class for easy use of custom delimiters.
|
319
|
+
// Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar.
|
320
|
+
// Usage: "cout << pretty_print::custom_delims<MyDelims>(x)".
|
321
|
+
|
322
|
+
struct custom_delims_base
|
323
|
+
{
|
324
|
+
virtual ~custom_delims_base() { }
|
325
|
+
virtual std::ostream & stream(::std::ostream &) = 0;
|
326
|
+
virtual std::wostream & stream(::std::wostream &) = 0;
|
327
|
+
};
|
328
|
+
|
329
|
+
template <typename T, typename Delims>
|
330
|
+
struct custom_delims_wrapper : custom_delims_base
|
331
|
+
{
|
332
|
+
custom_delims_wrapper(const T & t_) : t(t_) { }
|
333
|
+
|
334
|
+
std::ostream & stream(std::ostream & s)
|
335
|
+
{
|
336
|
+
return s << print_container_helper<T, char, std::char_traits<char>, Delims>(t);
|
337
|
+
}
|
338
|
+
|
339
|
+
std::wostream & stream(std::wostream & s)
|
340
|
+
{
|
341
|
+
return s << print_container_helper<T, wchar_t, std::char_traits<wchar_t>, Delims>(t);
|
342
|
+
}
|
343
|
+
|
344
|
+
private:
|
345
|
+
const T & t;
|
346
|
+
};
|
347
|
+
|
348
|
+
template <typename Delims>
|
349
|
+
struct custom_delims
|
350
|
+
{
|
351
|
+
template <typename Container>
|
352
|
+
custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { }
|
353
|
+
|
354
|
+
std::unique_ptr<custom_delims_base> base;
|
355
|
+
};
|
356
|
+
|
357
|
+
template <typename TChar, typename TCharTraits, typename Delims>
|
358
|
+
inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & s, const custom_delims<Delims> & p)
|
359
|
+
{
|
360
|
+
return p.base->stream(s);
|
361
|
+
}
|
362
|
+
|
363
|
+
|
364
|
+
// A wrapper for a C-style array given as pointer-plus-size.
|
365
|
+
// Usage: std::cout << pretty_print_array(arr, n) << std::endl;
|
366
|
+
|
367
|
+
template<typename T>
|
368
|
+
struct array_wrapper_n
|
369
|
+
{
|
370
|
+
typedef const T * const_iterator;
|
371
|
+
typedef T value_type;
|
372
|
+
|
373
|
+
array_wrapper_n(const T * const a, size_t n) : _array(a), _n(n) { }
|
374
|
+
inline const_iterator begin() const { return _array; }
|
375
|
+
inline const_iterator end() const { return _array + _n; }
|
376
|
+
|
377
|
+
private:
|
378
|
+
const T * const _array;
|
379
|
+
size_t _n;
|
380
|
+
};
|
381
|
+
|
382
|
+
|
383
|
+
// A wrapper for hash-table based containers that offer local iterators to each bucket.
|
384
|
+
// Usage: std::cout << bucket_print(m, 4) << std::endl; (Prints bucket 5 of container m.)
|
385
|
+
|
386
|
+
template <typename T>
|
387
|
+
struct bucket_print_wrapper
|
388
|
+
{
|
389
|
+
typedef typename T::const_local_iterator const_iterator;
|
390
|
+
typedef typename T::size_type size_type;
|
391
|
+
|
392
|
+
const_iterator begin() const
|
393
|
+
{
|
394
|
+
return m_map.cbegin(n);
|
395
|
+
}
|
396
|
+
|
397
|
+
const_iterator end() const
|
398
|
+
{
|
399
|
+
return m_map.cend(n);
|
400
|
+
}
|
401
|
+
|
402
|
+
bucket_print_wrapper(const T & m, size_type bucket) : m_map(m), n(bucket) { }
|
403
|
+
|
404
|
+
private:
|
405
|
+
const T & m_map;
|
406
|
+
const size_type n;
|
407
|
+
};
|
408
|
+
|
409
|
+
} // namespace pretty_print
|
410
|
+
|
411
|
+
|
412
|
+
// Global accessor functions for the convenience wrappers
|
413
|
+
|
414
|
+
template<typename T>
|
415
|
+
inline pretty_print::array_wrapper_n<T> pretty_print_array(const T * const a, size_t n)
|
416
|
+
{
|
417
|
+
return pretty_print::array_wrapper_n<T>(a, n);
|
418
|
+
}
|
419
|
+
|
420
|
+
template <typename T> pretty_print::bucket_print_wrapper<T>
|
421
|
+
bucket_print(const T & m, typename T::size_type n)
|
422
|
+
{
|
423
|
+
return pretty_print::bucket_print_wrapper<T>(m, n);
|
424
|
+
}
|
425
|
+
|
426
|
+
|
427
|
+
// Main magic entry point: An overload snuck into namespace std.
|
428
|
+
// Can we do better?
|
429
|
+
|
430
|
+
namespace std
|
431
|
+
{
|
432
|
+
// Prints a container to the stream using default delimiters
|
433
|
+
|
434
|
+
template<typename T, typename TChar, typename TCharTraits>
|
435
|
+
inline typename enable_if< ::pretty_print::is_container<T>::value,
|
436
|
+
basic_ostream<TChar, TCharTraits> &>::type
|
437
|
+
operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container)
|
438
|
+
{
|
439
|
+
return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container);
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
443
|
+
|
444
|
+
|
445
|
+
#endif // H_PRETTY_PRINT
|
data/lib/cluster_eval.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'cluster_eval'
|
3
|
+
#
|
4
|
+
# test tiny example
|
5
|
+
#
|
6
|
+
class TestConfusionMatrixSmall < MiniTest::Test
|
7
|
+
#
|
8
|
+
def setup
|
9
|
+
@h1= {0 => 0, 1 => 0, 2 => 1, 3 => 1, 4 => 1, 5 => 2, 6 => 2, 7 => 2}
|
10
|
+
@h2= {0 => 0, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 2, 7 => 2}
|
11
|
+
end
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def test_small_confusion_matrix()
|
15
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
16
|
+
cm_ary = cm.get_confusion_matrix
|
17
|
+
assert_equal(4, cm_ary[0], 'Incorrect num for a')
|
18
|
+
assert_equal(3, cm_ary[1], 'Incorrect num for b')
|
19
|
+
assert_equal(7, cm_ary[2], 'Incorrect num for c')
|
20
|
+
assert_equal(14, cm_ary[3], 'Incorrect num for d')
|
21
|
+
#
|
22
|
+
cm_naive = cm.get_confusion_matrix_naive
|
23
|
+
assert_equal(cm_ary, cm_naive, 'confusion matrices do not agree')
|
24
|
+
end
|
25
|
+
#
|
26
|
+
#
|
27
|
+
def test_small_jaccard()
|
28
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
29
|
+
ji_val = cm.get_jaccard_index
|
30
|
+
assert_in_delta(0.28571, ji_val, 0.0001, 'incorrect Jaccard index')
|
31
|
+
end
|
32
|
+
#
|
33
|
+
#
|
34
|
+
def test_small_rand()
|
35
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
36
|
+
rand_val = cm.get_rand_index
|
37
|
+
assert_in_delta(0.64285, rand_val, 0.0001, 'incorrect Rand index')
|
38
|
+
end
|
39
|
+
#
|
40
|
+
#
|
41
|
+
def test_small_fm()
|
42
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
43
|
+
fm_val = cm.get_fm_index
|
44
|
+
assert_in_delta(0.45584, fm_val, 0.0001, 'incorrect FM index')
|
45
|
+
end
|
46
|
+
#
|
47
|
+
#
|
48
|
+
def test_small_rand_adj_rand()
|
49
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
50
|
+
rand_val = cm.get_adj_rand_index
|
51
|
+
assert_in_delta(0.2, rand_val, 0.0001, 'incorrect adjusted Rand index')
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'cluster_eval'
|
3
|
+
#
|
4
|
+
# test two random (small) clusterings
|
5
|
+
#
|
6
|
+
class TestConfusionMatrixSmallRandom < MiniTest::Test
|
7
|
+
#
|
8
|
+
def setup
|
9
|
+
@h1 = {0 => 0, 1 => 1, 2 => 2, 3 => 0, 4 => 3, 5 => 4, 6 => 5, 7 => 1}
|
10
|
+
@h2 = {0 => 1, 1 => 1, 2 => 0, 3 => 0, 4 => 2, 5 => 2, 6 => 2, 7 => 2}
|
11
|
+
end
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def test_small_rand_confusion_matrix()
|
15
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
16
|
+
cm_ary = cm.get_confusion_matrix
|
17
|
+
assert_equal(0, cm_ary[0], 'Incorrect num for a')
|
18
|
+
assert_equal(2, cm_ary[1], 'Incorrect num for b')
|
19
|
+
assert_equal(8, cm_ary[2], 'Incorrect num for c')
|
20
|
+
assert_equal(18, cm_ary[3], 'Incorrect num for d')
|
21
|
+
#
|
22
|
+
cm_naive = cm.get_confusion_matrix_naive
|
23
|
+
assert_equal(cm_ary, cm_naive, 'confusion matrices do not agree')
|
24
|
+
end
|
25
|
+
#
|
26
|
+
#
|
27
|
+
def test_small_rand_jaccard()
|
28
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
29
|
+
ji_val = cm.get_jaccard_index
|
30
|
+
assert_in_delta(0.000, ji_val, 0.0001, 'incorrect Jaccard index')
|
31
|
+
end
|
32
|
+
#
|
33
|
+
#
|
34
|
+
def test_small_rand_rand()
|
35
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
36
|
+
rand_val = cm.get_rand_index
|
37
|
+
assert_in_delta(0.64285, rand_val, 0.0001, 'incorrect Rand index')
|
38
|
+
end
|
39
|
+
#
|
40
|
+
#
|
41
|
+
def test_small_rand_fm()
|
42
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
43
|
+
fm_val = cm.get_fm_index
|
44
|
+
assert_in_delta(0.0000, fm_val, 0.0001, 'incorrect FM index')
|
45
|
+
end
|
46
|
+
#
|
47
|
+
#
|
48
|
+
def test_small_rand_adj_rand()
|
49
|
+
cm = ClusterEval::ConfusionMatrix.new(@h1,@h2)
|
50
|
+
rand_val = cm.get_adj_rand_index
|
51
|
+
assert_in_delta(-0.129032, rand_val, 0.0001, 'incorrect adjusted Rand index')
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# minitest stuff
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'minitest'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
# minitest-reporters
|
7
|
+
require "minitest/reporters"
|
8
|
+
#Minitest::Reporters.use!
|
9
|
+
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
10
|
+
|
11
|
+
def get_data_dir()
|
12
|
+
"#{File.dirname(__FILE__)}/data/"
|
13
|
+
end
|
14
|
+
|
15
|
+
## if/when create a gem, remove this
|
16
|
+
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib/")
|
17
|
+
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../ext/")
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cluster_eval
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sbonisso
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.19'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rice
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest-reporters
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
description: Evaluate partitionings of different clustering approaches. Provides different
|
98
|
+
metrics to use.
|
99
|
+
email:
|
100
|
+
- sbonisso@ucsd.edu
|
101
|
+
executables:
|
102
|
+
- cluster_eval
|
103
|
+
extensions:
|
104
|
+
- ext/extconf.rb
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- ".travis.yml"
|
109
|
+
- Gemfile
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/cluster_eval
|
113
|
+
- bin/console
|
114
|
+
- bin/setup
|
115
|
+
- cluster_eval.gemspec
|
116
|
+
- ext/ConfusionMatrix.cpp
|
117
|
+
- ext/ConfusionMatrix.hpp
|
118
|
+
- ext/clusteval.cpp
|
119
|
+
- ext/extconf.rb
|
120
|
+
- ext/prettyprint.hpp
|
121
|
+
- lib/cluster_eval.rb
|
122
|
+
- lib/cluster_eval/version.rb
|
123
|
+
- tests/test_confusion_matrix_small.rb
|
124
|
+
- tests/test_confusion_matrix_small_random.rb
|
125
|
+
- tests/test_helper.rb
|
126
|
+
homepage: https://github.com/sbonisso/cluster_eval
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
- ext
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.4.6
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: Evaluation of clusterings
|
151
|
+
test_files: []
|
152
|
+
has_rdoc:
|