morphological_metrics 1.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 38630c4a00ff6c2cbe5dedbc373d664d6b4f33b6
4
+ data.tar.gz: 5e78dfc1aacd53e528215819c0fe6d8bd30bc2b6
5
+ SHA512:
6
+ metadata.gz: 1d24565d2115f43919ae9ca4960225555f991cfa97edbe5bf07534fbd038f88f929283f2025e572cab51aa542765412cdc0d7bf028dad31c25d3abfc81461ae6
7
+ data.tar.gz: 0ddbb32a053f6ad71e70eea8390df5570f979b0ee197e3589874cc02980777dec04dce5e19bc171b54ef445f9e29c9ca2989f942c5315fec199a88f12a29ad98
data/.autotest ADDED
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = ".minitest"
7
+ #
8
+ # at.extra_files << "../some/external/dependency.rb"
9
+ #
10
+ # at.libs << ":../some/external"
11
+ #
12
+ # at.add_exception "vendor"
13
+ #
14
+ # at.add_mapping(/dependency.rb/) do |f, _|
15
+ # at.files_matching(/test_.*rb$/)
16
+ # end
17
+ #
18
+ # %w(TestA TestB).each do |klass|
19
+ # at.extra_class_map[klass] = "test/test_misc.rb"
20
+ # end
21
+ end
22
+
23
+ # Autotest.add_hook :run_command do |at|
24
+ # system "rake build"
25
+ # end
26
+
27
+ Autotest.add_hook :all_good do |at|
28
+ system "rake rcov_info"
29
+ end if ENV['RCOV']
30
+
data/.gemtest ADDED
File without changes
data/.minitest.rb ADDED
@@ -0,0 +1,3 @@
1
+ gem "minitest"
2
+ require "minitest/autorun"
3
+
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ env:
3
+ - CODECLIMATE_REPO_TOKEN=d05b64cdc405df4f1632a3656a1062ea4873a20ff4463732c47c4d811884fde6
4
+ rvm:
5
+ - "2.0.0"
6
+ - "2.1.2"
7
+ addons:
8
+ code_climate:
9
+ repo_token: d05b64cdc405df4f1632a3656a1062ea4873a20ff4463732c47c4d811884fde6
10
+
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'hoe-travis'
4
+ gem 'rake'
5
+ gem 'rake-compiler'
6
+ gem 'hoe'
7
+
8
+ group :test do
9
+ gem 'minitest', '~> 5.0'
10
+ gem 'codeclimate-test-reporter', require: nil
11
+ end
12
+
13
+ group :development do
14
+ gem 'flog'
15
+ gem 'flay'
16
+ end
17
+
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ codeclimate-test-reporter (0.3.0)
5
+ simplecov (>= 0.7.1, < 1.0.0)
6
+ docile (1.1.5)
7
+ flay (2.5.0)
8
+ ruby_parser (~> 3.0)
9
+ sexp_processor (~> 4.0)
10
+ flog (4.2.1)
11
+ ruby_parser (~> 3.1, > 3.1.0)
12
+ sexp_processor (~> 4.4)
13
+ hashr (0.0.22)
14
+ hoe (3.12.0)
15
+ rake (>= 0.8, < 11.0)
16
+ hoe-travis (1.2)
17
+ hoe (~> 3.0)
18
+ travis-lint (~> 1.2)
19
+ minitest (5.4.0)
20
+ multi_json (1.10.1)
21
+ rake (10.3.2)
22
+ rake-compiler (0.9.2)
23
+ rake
24
+ ruby_parser (3.6.1)
25
+ sexp_processor (~> 4.1)
26
+ safe_yaml (0.9.7)
27
+ sexp_processor (4.4.3)
28
+ simplecov (0.8.2)
29
+ docile (~> 1.1.0)
30
+ multi_json
31
+ simplecov-html (~> 0.8.0)
32
+ simplecov-html (0.8.0)
33
+ travis-lint (1.8.0)
34
+ hashr (~> 0.0.22)
35
+ safe_yaml (~> 0.9.0)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ codeclimate-test-reporter
42
+ flay
43
+ flog
44
+ hoe
45
+ hoe-travis
46
+ minitest (~> 5.0)
47
+ rake
48
+ rake-compiler
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2014-04-09
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,27 @@
1
+ .autotest
2
+ .minitest.rb
3
+ .travis.yml
4
+ Gemfile
5
+ Gemfile.lock
6
+ History.txt
7
+ Manifest.txt
8
+ README.rdoc
9
+ ROADMAP.txt
10
+ Rakefile
11
+ bin/mm
12
+ lib/mm.rb
13
+ lib/mm/deltas.rb
14
+ lib/mm/metric.rb
15
+ lib/mm/pairs.rb
16
+ lib/mm/ratio.rb
17
+ lib/mm/scaling.rb
18
+ lib/mm/search.rb
19
+ lib/shortcuts.yml
20
+ test/helpers.rb
21
+ test/mm/test_deltas.rb
22
+ test/mm/test_metric.rb
23
+ test/mm/test_mm.rb
24
+ test/mm/test_pairs.rb
25
+ test/mm/test_ratio.rb
26
+ test/mm/test_scaling.rb
27
+ test/mm/test_search.rb
data/README.rdoc ADDED
@@ -0,0 +1,66 @@
1
+ {<img src="https://codeclimate.com/github/andrewcsmith/mm.png"
2
+ />}[https://codeclimate.com/github/andrewcsmith/mm]
3
+
4
+ {<img src="https://travis-ci.org/andrewcsmith/mm.svg?branch=master"
5
+ />}[https://travis-ci.org/andrewcsmith/mm]
6
+
7
+ = mm
8
+
9
+ https://www.github.com/andrewcsmith/mm
10
+
11
+ == DESCRIPTION:
12
+
13
+ Implements some Morphological Metrics, described by Larry Polansky.
14
+
15
+ == FEATURES/PROBLEMS:
16
+
17
+ * Works on anything Enumerable
18
+ * Can get a number of different MMs
19
+ * Works extra fast on large NMatrix objects
20
+
21
+ == SYNOPSIS:
22
+
23
+ Code sample, LOL
24
+
25
+ == REQUIREMENTS:
26
+
27
+ * nmatrix
28
+ * ruby
29
+
30
+ == INSTALL:
31
+
32
+ * FIX (sudo gem install, anything else)
33
+
34
+ == DEVELOPERS:
35
+
36
+ After checking out the source, run:
37
+
38
+ $ rake newb
39
+
40
+ This task will install any missing dependencies, run the tests/specs,
41
+ and generate the RDoc.
42
+
43
+ == LICENSE:
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2014 FIX
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/ROADMAP.txt ADDED
@@ -0,0 +1,63 @@
1
+ - Complete functions for getting combinatorial and adjacent pairs
2
+ - fix any problems with shapes, test more thoroughly
3
+ - add the ability to recurse throughout an Array tree
4
+ - Create a selection of distance functions that work with NMatrix etc
5
+ - These should be implementation-agnostic
6
+ - Ideally they should be of the symbol/method format, where the user passes a
7
+ symbol of the method to the metric
8
+ - Create a Metric class, perhaps as a subclass of Proc
9
+ - What does it mean to subclass Proc?
10
+ - Do some research.
11
+ - What about subclassing Method? What would that give us?
12
+ - Instead of a "factory" class, the creation of the metric should take place in
13
+ its initialization.
14
+ - Metric.new should take arguments similar to DistConfig, but with a
15
+ specific implementation of OLM, OCM, etc.
16
+ - This is out of a desire to tie the selection of the logic into the
17
+ distance function.
18
+ - However, there could be MM.ocm, MM.olm, etc., functions that are
19
+ "factories" that call the normal instantiation method. But it should
20
+ not be possible to create a metric without the DistConfig in instantiation.
21
+
22
+ Implementation and interface requirements:
23
+ - Objects should implement [] and #each
24
+ - Objects should implement whatever distance method is called by the Metric
25
+
26
+ Additional distance functions should (ideally) be in their own gems.
27
+
28
+ == Metric Class Methods
29
+
30
+ === ::new(ordering, pairing, scaling, delta)
31
+
32
+ Generates a new morphological metric based on various parameters
33
+
34
+ **ordered**: can be true or false. Corresponds to ordered/unordered.
35
+
36
+ **pairing**: Similar to the delta. If it is a Proc, then the pairing function is
37
+ called on each vector (or however it is handled by the ordering). If it is a
38
+ Symbol, then we look it up in MM::PAIRING_FUNCTIONS.
39
+
40
+ **scaling**: *always* a Proc. There are prebaked scaling functions for absolute,
41
+ relative, and unscaled that are available for use. If a Symbol is passed instead
42
+ of a Proc, it looks for the Proc in the Hash MM::SCALING_FUNCTIONS
43
+
44
+ **delta functions**: If it is a Proc, then the delta function is used as d(x, y)
45
+ on each element. If it is a symbol, then the symbol is first looked up in the
46
+ MM::DELTA_FUNCTIONS hash. If it is not found, then x.symbol(y) is attempted.
47
+ Delta functions should aspire to being as general as possible; let the source
48
+ implement the method.
49
+
50
+ **order**: this is confusing terminology - first-order and second-order
51
+ difference functions.
52
+
53
+ **interval functions**: We should instead brew the int_func into the method of
54
+ finding pairs. This is where the Pairs class might come in handy; it could
55
+ generate functions (Procs, whatever) as a factory from various values, taking
56
+ into account what each value should be compared to. It's not [element, element],
57
+ but [element, comparison-element] that matters.
58
+
59
+ === ::olm(scaling, delta)
60
+
61
+ Generates a new morphological metric calling ::new with the given ordering and
62
+ pairing
63
+
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ # -*- ruby -*-
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+
6
+ # Hoe.plugin :compiler
7
+ # Hoe.plugin :gem_prelude_sucks
8
+ # Hoe.plugin :racc
9
+ # Hoe.plugin :rcov
10
+ Hoe.plugin :flay
11
+ Hoe.plugin :flog
12
+
13
+ Hoe.plugin :travis
14
+ Hoe.plugin :inline
15
+ Hoe.plugin :minitest
16
+ Hoe.plugin :package
17
+
18
+ hoe = Hoe.spec "morphological_metrics" do |h|
19
+ developer("Andrew C. Smith", "andrewchristophersmith@gmail.com")
20
+ license "MIT" # this should match the license in the README
21
+ self.readme_file = "README.rdoc"
22
+ require_rubygems_version '>= 1.4'
23
+ end
24
+
25
+ if ENV['CODECLIMATE_REPO_TOKEN']
26
+ hoe.test_prelude = <<CC
27
+ require "codeclimate-test-reporter"
28
+ CodeClimate::TestReporter.start
29
+ CC
30
+ end
31
+
32
+ # vim: syntax=ruby
data/bin/mm ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
data/lib/mm/deltas.rb ADDED
@@ -0,0 +1,35 @@
1
+ module MM
2
+ module Deltas
3
+ def self.abs n
4
+ (n[0] - n[1]).abs
5
+ end
6
+
7
+ def self.diff n
8
+ n[0] - n[1]
9
+ end
10
+
11
+ def self.mean n
12
+ n.inject(0.0, :+) / n.size
13
+ end
14
+
15
+ # Have to scale by 0.5 in order to normalize to a max distance of 1.0
16
+ def self.direction n
17
+ (n[0] <=> n[1]) * 0.5
18
+ end
19
+
20
+ # Accepts a tuple where the quotient responds to #numerator and #denominator
21
+ def self.tenney n
22
+ ->(r) { Math.log2(r.numerator * r.denominator) }.call(n[0] / n[1])
23
+ end
24
+
25
+ # Accepts a tuple of anything that Math.log2 can handle
26
+ def self.log_ratio n
27
+ Math.log2((n[0] / n[1]).to_f).abs
28
+ end
29
+
30
+ def self.ratio n
31
+ n[0] / n[1]
32
+ end
33
+ end
34
+ end
35
+
data/lib/mm/metric.rb ADDED
@@ -0,0 +1,191 @@
1
+ require 'yaml'
2
+
3
+ module MM
4
+ class Metric
5
+ # Constructor for the Metric object.
6
+ #
7
+ # ordered - [Boolean]
8
+ # Controls whether metric is ordered
9
+ # pair - [Symbol, #call]
10
+ # Method of +MM::Deltas+, or where +Object#call+ returns an +Array+ of
11
+ # pairs.
12
+ # scale - [Symbol, #call]
13
+ # Method of +MM::Scaling+, or where +Object#call+ returns a scaled diff
14
+ # +Array+
15
+ # intra_delta - [Symbol, #call]
16
+ # Method of +MM::Deltas+, or where +Object#call+ returns +Array+ of
17
+ # differences between pairs of elements
18
+ # inter_delta - [Symbol, #call]
19
+ # Method of +MM::Deltas+, or where +Object#call+ returns +Array+ of
20
+ # differences between the diffs of the two input morphologies
21
+ #
22
+ def initialize(ordered: true, pair: nil, scale: nil, intra_delta: nil, inter_delta: nil)
23
+ @ordered = ordered
24
+ self.pair = pair
25
+ self.scale = scale
26
+ self.intra_delta = intra_delta
27
+ self.inter_delta = inter_delta
28
+ end
29
+
30
+ attr_accessor :ordered
31
+
32
+ # Public: Gets the distance between two vectors, according to the Metric
33
+ # object. Since +Metric+ can be duck-typed to work with +intra_delta+ and
34
+ # +inter_delta+, it should be possible to nest +Metric+ objects.
35
+ #
36
+ # v1 - The vector to call on.
37
+ # v2 - The vector to compare against.
38
+ #
39
+ # Returns a [Float] distance between the two vectors.
40
+ def call v1, v2
41
+ # "Readable" method provided for the parenthetically inclined
42
+ # inter_delta(scale(intra_delta(get_pairs(v1, v2))))
43
+ inter_delta scale intra_delta get_pairs v1, v2
44
+ end
45
+
46
+ # Public: Setter method for pair.
47
+ #
48
+ # pair - [Symbol, #call]
49
+ # Method of +MM::Deltas+, or where +Object#call+ returns an +Array+ of
50
+ # pairs.
51
+ #
52
+ # Returns a [Proc] pair.
53
+ def pair= pair
54
+ protected_use_method(MM::Pairs.new, :@pair, pair)
55
+ end
56
+
57
+ # Public: Setter method for scale.
58
+ #
59
+ # scale - Either a Proc that can process scaling, or a Symbol to look up in
60
+ # MM::Scaling.
61
+ #
62
+ # Returns scale.
63
+ def scale= scale
64
+ protected_use_method(MM::Scaling, :@scale, scale)
65
+ end
66
+
67
+ # Public: Setter method for intra_delta.
68
+ #
69
+ # intra_delta - Either a Proc that can process the intra_delta, or a Symbol
70
+ # to look up in MM::Deltas.
71
+ #
72
+ # Returns intra_delta.
73
+ def intra_delta= intra_delta
74
+ protected_use_method(MM::Deltas, :@intra_delta, intra_delta)
75
+ end
76
+
77
+ # Public: Setter method for inter_delta.
78
+ #
79
+ # inter_delta - Either a Proc that can process as an inter_delta, or a
80
+ # Symbol where +MM::Deltas.respond_to? Symbol == true+
81
+ #
82
+ # Returns itself. Sets the instance variable @inter_delta.
83
+ def inter_delta= inter_delta
84
+ protected_use_method(MM::Deltas, :@inter_delta, inter_delta)
85
+ end
86
+
87
+ private
88
+
89
+ # Private: Calls the get_pairs Proc on each of v1 and v2. In lp's
90
+ # terminology, the get_pairs Proc should return either adjacent pairs
91
+ # of each of the two vectors (for linear metrics) or all possible pair
92
+ # combinations (for combinatorial metrics). For more, see Polansky 1992.
93
+ #
94
+ # v1 - the metric to use as a base
95
+ # v2 - the metric to compare to
96
+ #
97
+ # Returns an Array of Arrays of pairs.
98
+ def get_pairs v1, v2
99
+ [v1, v2].map {|x| @pair.call(x)}
100
+ end
101
+
102
+ # Private: Applies the delta to each pair of elements in a collection
103
+ # where each pair is [elem1, elem2]
104
+ #
105
+ # vp - vector pairs
106
+ #
107
+ # Returns the vector_deltas, which is the difference between each pair of
108
+ # elements in a given vector.
109
+ def intra_delta vp
110
+ vp.map {|x| x.map {|n| @intra_delta.call(n)}}
111
+ end
112
+
113
+ # Private: Calls the scaling Proc. It's a Method, so if you want to subclass it when
114
+ # subclassing Metric (in order to do something fast and crazy) you totally can.
115
+ #
116
+ # pairs - A sequence of pairs.
117
+ #
118
+ # Returns the output of the scaling Proc, ideally a sequence of pairs.
119
+ def scale pairs
120
+ @scale.call pairs
121
+ end
122
+
123
+ # Private: Accepts a series of vectors, either a sequence of pairs or two full
124
+ # collections, and reduces them to a single vector. Does not do any scaling.
125
+ #
126
+ # diffs - [Enumerable] Series of vectors, either a sequence of pairs or two full
127
+ # collections.
128
+ #
129
+ # Returns a single vector of the diffs between the two.
130
+ def inter_delta diffs
131
+ if @ordered
132
+ # Ordered Metrics take the mean of differences
133
+ Deltas.mean(diffs[0].zip(diffs[1]).map {|x| @inter_delta.call x})
134
+ else
135
+ # Unordered Metrics take the difference of means
136
+ Deltas.abs(diffs.map {|x| @inter_delta.call x})
137
+ end
138
+ end
139
+
140
+ # Private: Performs final averaging on the output of inter_delta. Can be overwritten
141
+ # when subclassed, in case you want to use a different method of averaging
142
+ # (sum of squares, etc.)
143
+ #
144
+ # diffs - The vector of the differences between the two vector deltas. Essentially
145
+ # the output of inter_delta. Should respond to #reduce.
146
+ #
147
+ # Returns distance [Numeric] The distance calculated by the diff
148
+ def post_scale diffs
149
+ diffs.reduce(0, :+).to_f / diffs.size
150
+ end
151
+
152
+ # Private: Assigns the Method named sym, if mod responds to it, to the
153
+ # instance variable var. Otherwise, assumes that the sym is actually a Proc
154
+ # and just tries to use it straight.
155
+ #
156
+ # mod - Object to see whether it has a method.
157
+ # var - instance variable to assign to.
158
+ # sym - Symbol to lookup in mod's exposed methods.
159
+ #
160
+ # Returns +sym+.
161
+ def protected_use_method mod, var, sym
162
+ if sym.is_a?(Symbol) && mod.respond_to?(sym)
163
+ self.instance_variable_set(var, mod.method(sym))
164
+ else
165
+ self.instance_variable_set(var, sym)
166
+ end
167
+ end
168
+
169
+ ### CONVENIENCE CREATION METHODS ###
170
+ # All of the following methods are created using the YAML definition file.
171
+ # See shortcuts.yml for the full definition.
172
+ # ::olm
173
+ # ::ocm
174
+ # ::ulm
175
+ # ::ucm
176
+ # ::old
177
+ # ::ocd
178
+ # ::uld
179
+ # ::ucd
180
+ METHOD_SHORTCUTS = YAML.load(File.read(File.join(File.dirname(__FILE__), '..', 'shortcuts.yml')))
181
+
182
+ class << self
183
+ METHOD_SHORTCUTS.each do |k, v|
184
+ define_method k do |other = {}|
185
+ new((v.merge other))
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
data/lib/mm/pairs.rb ADDED
@@ -0,0 +1,11 @@
1
+ module MM
2
+ class Pairs
3
+ def linear vector
4
+ vector.each_cons(2).to_a
5
+ end
6
+ def combinatorial vector
7
+ vector.combination(2).to_a
8
+ end
9
+ end
10
+ end
11
+