morphological_metrics 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/mm CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
- abort "you need to write me"
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
data/lib/mm.rb CHANGED
@@ -1,10 +1,10 @@
1
- module MM
2
- VERSION = "1.2.0"
3
- end
4
-
5
- require 'mm/deltas'
6
- require 'mm/metric'
7
- require 'mm/pairs'
8
- require 'mm/ratio'
9
- require 'mm/scaling'
10
- require 'mm/
1
+ module MM
2
+ VERSION = "1.3.1"
3
+ end
4
+
5
+ require 'mm/deltas'
6
+ require 'mm/metric'
7
+ require 'mm/pairs'
8
+ require 'mm/ratio'
9
+ require 'mm/scaling'
10
+ require 'mm/search'
@@ -1,35 +1,34 @@
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
-
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
@@ -1,180 +1,191 @@
1
- require 'psych'
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 = Psych.load(File.read(File.join(File.dirname(__FILE__), '..', 'shortcut
1
+ require 'psych'
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 = Psych.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
+