davidrichards-sirb 0.6.14

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.
Files changed (38) hide show
  1. data/README.rdoc +227 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sirb +33 -0
  4. data/lib/overrides/array.rb +6 -0
  5. data/lib/overrides/file.rb +17 -0
  6. data/lib/overrides/module.rb +70 -0
  7. data/lib/overrides/symbol.rb +39 -0
  8. data/lib/sirb/enumerable_statistics.rb +350 -0
  9. data/lib/sirb/functional.rb +114 -0
  10. data/lib/sirb/general_statistics.rb +72 -0
  11. data/lib/sirb/inter_enumerable_statistics.rb +139 -0
  12. data/lib/sirb/lib_loader.rb +45 -0
  13. data/lib/sirb/runner.rb +274 -0
  14. data/lib/sirb/sproc/proc.rb +5 -0
  15. data/lib/sirb/sproc/proc_source.rb +130 -0
  16. data/lib/sirb/sproc/sproc.rb +79 -0
  17. data/lib/sirb/sproc/usage_notes.txt +25 -0
  18. data/lib/sirb/sproc.rb +29 -0
  19. data/lib/sirb/thread_support.rb +20 -0
  20. data/lib/sirb/unbound_method.rb +5 -0
  21. data/lib/sirb.rb +52 -0
  22. data/lib/stored_procedures.rb +10 -0
  23. data/spec/lib/overrides/array_spec.rb +7 -0
  24. data/spec/lib/overrides/file_spec.rb +13 -0
  25. data/spec/lib/overrides/module_spec.rb +86 -0
  26. data/spec/lib/overrides/symbol_spec.rb +39 -0
  27. data/spec/lib/sirb/enumerable_statistics_spec.rb +85 -0
  28. data/spec/lib/sirb/functional_spec.rb +75 -0
  29. data/spec/lib/sirb/general_statistics_spec.rb +40 -0
  30. data/spec/lib/sirb/inter_enumerable_statistics_spec.rb +55 -0
  31. data/spec/lib/sirb/lib_loader_spec.rb +39 -0
  32. data/spec/lib/sirb/runner_spec.rb +9 -0
  33. data/spec/lib/sirb/sproc/proc_spec.rb +9 -0
  34. data/spec/lib/sirb/sproc/sproc_spec.rb +25 -0
  35. data/spec/lib/sirb/unbound_method_spec.rb +12 -0
  36. data/spec/lib/sirb_spec.rb +9 -0
  37. data/spec/spec_helper.rb +15 -0
  38. metadata +97 -0
@@ -0,0 +1,350 @@
1
+ module Sirb #:nodoc:
2
+
3
+ # These are the standard R vector functions that I want to add to any
4
+ # Enumerable class for Ruby. I started by borrowing heavily from
5
+ # Gotoken' math/statistics project
6
+ # (http://raa.ruby-lang.org/project/math-statistics/). There were a few
7
+ # changes that don't make sense in the idiomatic Ruby that I now use (a
8
+ # few things have changed since 2001).
9
+ #
10
+ # The following is a table of values from R to my methods
11
+ #
12
+ # max | max
13
+ # min | min
14
+ # sum | sum
15
+ # mean | mean
16
+ # median | median
17
+ # range | range
18
+ # var | var variance
19
+ # cor | cor correlation
20
+ # sort | sort
21
+ # rank | rank
22
+ # order | order
23
+ # quantile | quantile
24
+ # cumsum | cum_sum cumulative_sum
25
+ # cumprod | cum_prod cumulative_product
26
+ # cummax | cum_max cumulative_max
27
+ # cummin | cum_min cumulative_min
28
+ # pmax | p_max
29
+ # pmin | p_min
30
+
31
+ module EnumerableStatistics
32
+
33
+ # There are issues with this...
34
+ include GeneralStatistics
35
+
36
+ def self.append_features(mod)
37
+
38
+ alias :original_max :max
39
+ alias :original_min :min
40
+
41
+ unless mod < Enumerable
42
+ raise TypeError,
43
+ "`#{self}' can't be included non Enumerable (#{mod})"
44
+ end
45
+
46
+ def mod.default_block= (block)
47
+ self.const_set("STAT_BLOCK", block)
48
+ end
49
+
50
+ def mod.default_block
51
+ defined?(self::STAT_BLOCK) && self::STAT_BLOCK
52
+ end
53
+
54
+ super
55
+ end
56
+
57
+ def default_block
58
+ @stat_block || self.class.default_block
59
+ end
60
+
61
+ def default_block=(block)
62
+ @stat_block = block
63
+ end
64
+
65
+ def sum
66
+ sum = 0.0
67
+ if block_given?
68
+ each{|i| sum += yield(i)}
69
+ elsif default_block
70
+ each{|i| sum += default_block[*i]}
71
+ else
72
+ each{|i| sum += i}
73
+ end
74
+ sum
75
+ end
76
+
77
+ def average(&block)
78
+ sum(&block)/size
79
+ end
80
+ alias :mean :average
81
+ alias :avg :average
82
+
83
+ def variance(&block)
84
+ m = mean(&block)
85
+ sum_of_differences = if block_given?
86
+ sum{ |i| j=yield(i); (m - j) ** 2 }
87
+ elsif default_block
88
+ sum{ |i| j=default_block[*i]; (m - j) ** 2 }
89
+ else
90
+ sum{ |i| (m - i) ** 2 }
91
+ end
92
+ sum_of_differences / (size - 1)
93
+ end
94
+ alias :var :variance
95
+
96
+ def standard_deviation(&block)
97
+ Math::sqrt(variance(&block))
98
+ end
99
+ alias :std :standard_deviation
100
+
101
+ def min(&block)
102
+ list = if block_given?
103
+ map{|x| yield(x) }
104
+ elsif default_block
105
+ map{|x| default_block[*x] }
106
+ else
107
+ self
108
+ end
109
+ Object.min(*list)
110
+ end
111
+
112
+ def min_index
113
+ index(min)
114
+ end
115
+
116
+ def max
117
+ list = if block_given?
118
+ map{|x| yield(x) }
119
+ elsif default_block
120
+ map{|x| default_block[*x] }
121
+ else
122
+ self
123
+ end
124
+ Object.max(*list)
125
+ end
126
+
127
+ def max_index
128
+ index(max)
129
+ end
130
+
131
+ # The slow way is to iterate up to the middle point. A faster way is to
132
+ # use the index, when available. If a block is supplied, always iterate
133
+ # to the middle point.
134
+ def median(ratio=0.5, &block)
135
+ return iterate_midway(ratio, &block) if block_given?
136
+ begin
137
+ mid1, mid2 = middle_two
138
+ sorted = new_sort
139
+ med1, med2 = sorted[mid1], sorted[mid2]
140
+ return med1 if med1 == med2
141
+ return med1 + ((med2 - med1) * ratio)
142
+ rescue
143
+ iterate_midway(ratio, &block)
144
+ end
145
+ end
146
+
147
+ def middle_two
148
+ mid2 = size.div(2)
149
+ mid1 = (size % 2 == 0) ? mid2 - 1 : mid2
150
+ return mid1, mid2
151
+ end
152
+ protected :middle_two
153
+
154
+ def median_position
155
+ middle_two.last
156
+ end
157
+ protected :median_position
158
+
159
+ def first_half(&block)
160
+ fh = self[0..median_position].dup
161
+ end
162
+ protected :first_half
163
+
164
+ def second_half(&block)
165
+ # Total crap, but it's the way R does things, and this will most likely
166
+ # only be used to feed R some numbers to plot, if at all.
167
+ sh = size <= 5 ? self[median_position..-1].dup : self[median_position - 1..-1].dup
168
+ end
169
+ protected :second_half
170
+
171
+ # An iterative version of median
172
+ def iterate_midway(ratio, &block)
173
+ mid1, mid2, last_value, j, sorted, sort1, sort2 = middle_two, nil, 0, new_sort, nil, nil
174
+
175
+ if block_given?
176
+ sorted.each do |i|
177
+ last_value = yield(i)
178
+ j += 1
179
+ sort1 = last_value if j == mid1
180
+ sort2 = last_value if j == mid2
181
+ break if j >= mid2
182
+ end
183
+ elsif default_block
184
+ sorted.each do |i|
185
+ last_value = default_block[*i]
186
+ j += 1
187
+ sort1 = last_value if j == mid1
188
+ sort2 = last_value if j == mid2
189
+ break if j >= mid2
190
+ end
191
+ else
192
+ sorted.each do |i|
193
+ last_value = i
194
+ sort1 = last_value if j == mid1
195
+ sort2 = last_value if j == mid2
196
+ j += 1
197
+ break if j >= mid2
198
+ end
199
+ end
200
+ return med1 if med1 == med2
201
+ return med1 + ((med2 - med1) * ratio)
202
+ end
203
+ protected :iterate_midway
204
+
205
+ # Just an array of [min, max] to comply with R uses of the work. Use
206
+ # range_as_range if you want a real Range.
207
+ def range(&block)
208
+ [min(&block), max(&block)]
209
+ end
210
+
211
+ # Useful for setting a real range class (FixedRange).
212
+ def range_class=(klass)
213
+ @range_class = klass
214
+ end
215
+
216
+ def range_class
217
+ @range_class ||= Range
218
+ end
219
+
220
+ def range_as_range(&block)
221
+ range_class.new(min(&block), max(&block))
222
+ end
223
+
224
+ # I don't pass the block to the sort, because a sort block needs to look
225
+ # something like: {|x,y| x <=> y}. To get around this, set the default
226
+ # block on the object.
227
+ def new_sort(&block)
228
+ if block_given?
229
+ map { |i| yield(i) }.sort.dup
230
+ elsif default_block
231
+ map { |i| default_block[*i] }.sort.dup
232
+ else
233
+ sort().dup
234
+ end
235
+ end
236
+
237
+ # Doesn't overwrite things like Matrix#rank
238
+ def rank(&block)
239
+
240
+ sorted = new_sort
241
+
242
+ if block_given?
243
+ map { |i| sorted.index(yield(i)) + 1 }
244
+ elsif default_block
245
+ map { |i| sorted.index(default_block[*i]) + 1 }
246
+ else
247
+ map { |i| sorted.index(i) + 1 }
248
+ end
249
+
250
+ end unless defined?(rank)
251
+
252
+ # Given values like [10,5,5,1]
253
+ # Rank should produce something like [4,2,2,1]
254
+ # And order should produce something like [4,2,3,1]
255
+ # The trick is that rank skips as many as were duplicated, so there
256
+ # could not be a 3 in the rank from the example above.
257
+ def order(&block)
258
+ hold= []
259
+ rank(&block).each_with_index do |x, i|
260
+ j = i
261
+ while hold.include?(j) do
262
+ j += 1
263
+ end
264
+ hold << j
265
+ end
266
+ end
267
+
268
+ # First quartile: nth_split_by_m(1, 4)
269
+ # Third quartile: nth_split_by_m(3, 4)
270
+ # Median: nth_split_by_m(1, 2)
271
+ # Doesn't match R, and it's silly to try to.
272
+ # def nth_split_by_m(n, m)
273
+ # sorted = new_sort
274
+ # dividers = m - 1
275
+ # if size % m == dividers # Divides evenly
276
+ # # Because we have a 0-based list, we get the floor
277
+ # i = ((size / m.to_f) * n).floor
278
+ # j = i
279
+ # else
280
+ # # This reflects R's approach, which I don't think I agree with.
281
+ # i = (((size / m.to_f) * n) - 1)
282
+ # i = i > (size / m.to_f) ? i.floor : i.ceil
283
+ # j = i + 1
284
+ # end
285
+ # sorted[i] + ((n / m.to_f) * (sorted[j] - sorted[i]))
286
+ # end
287
+
288
+ def quantile(&block)
289
+ [
290
+ min(&block),
291
+ first_half(&block).median(0.25, &block),
292
+ median(&block),
293
+ second_half(&block).median(0.75, &block),
294
+ max(&block)
295
+ ]
296
+ end
297
+
298
+ def cum_sum(sorted=false, &block)
299
+ sum = 0.0
300
+ obj = sorted ? self.new_sort : self
301
+ if block_given?
302
+ obj.map { |i| sum += yield(i) }
303
+ elsif default_block
304
+ obj.map { |i| sum += default_block[*i] }
305
+ else
306
+ obj.map { |i| sum += i }
307
+ end
308
+ end
309
+ alias :cumulative_sum :cum_sum
310
+
311
+ def cum_prod(sorted=false, &block)
312
+ prod = 1.0
313
+ obj = sorted ? self.new_sort : self
314
+ if block_given?
315
+ obj.map { |i| prod *= yield(i) }
316
+ elsif default_block
317
+ obj.map { |i| prod *= default_block[*i] }
318
+ else
319
+ obj.map { |i| prod *= i }
320
+ end
321
+ end
322
+ alias :cumulative_product :cum_prod
323
+
324
+ def cum_max(&block)
325
+ current_max = nil
326
+ if block_given?
327
+ map {|i| current_max = Object.max(current_max, yield(i)) }
328
+ elsif default_block
329
+ map {|i| current_max = Object.max(current_max, default_block[*i]) }
330
+ else
331
+ map {|i| current_max = Object.max(current_max, i) }
332
+ end
333
+ end
334
+ alias :cumulative_max :cum_max
335
+
336
+ def cum_min(&block)
337
+ current_min = nil
338
+ if block_given?
339
+ map {|i| current_min = Object.min(current_min, yield(i)) }
340
+ elsif default_block
341
+ map {|i| current_min = Object.min(current_min, default_block[*i]) }
342
+ else
343
+ map {|i| current_min = Object.min(current_min, i) }
344
+ end
345
+ end
346
+ alias :cumulative_min :cum_min
347
+
348
+ end
349
+
350
+ end
@@ -0,0 +1,114 @@
1
+ # This is probably border-line for what O'Reilly meant for using their
2
+ # code. I grabbed six methods from The Ruby Programming Language,
3
+ # section 6.8. I want to experiment with how this could change some of
4
+ # my methods.
5
+ # This module defines methods and operators for functional programming.
6
+ module Functional
7
+
8
+ # Apply this function to each element of the specified Enumerable,
9
+ # returning an array of results. This is the reverse of Enumerable.map.
10
+ # Use | as an operator alias. Read "|" as "over" or "applied over".
11
+ #
12
+ # Example:
13
+ # a = [[1,2],[3,4]]
14
+ # sum = lambda {|x,y| x+y}
15
+ # sums = sum|a # => [3,7]
16
+ def apply(enum)
17
+ enum.respond_to?(:map) ? enum.map(&self) : self.call(enum)
18
+ end
19
+ alias | apply
20
+
21
+ # Use this function to "reduce" an enumerable to a single quantity.
22
+ # This is the inverse of Enumerable.inject.
23
+ # Use <= as an operator alias.
24
+ # Mnemonic: <= looks like a needle for injections
25
+ # Example:
26
+ # data = [1,2,3,4]
27
+ # sum = lambda {|x,y| x+y}
28
+ # total = sum<=data # => 10
29
+ def reduce(enum)
30
+ enum.inject &self
31
+ end
32
+ alias <= reduce
33
+
34
+ # Return a new lambda that computes self[f[args]].
35
+ # Use * as an operator alias for compose.
36
+ # Examples, using the * alias for this method.
37
+ #
38
+ # f = lambda {|x| x*x }
39
+ # g = lambda {|x| x+1 }
40
+ # (f*g)[2] # => 9
41
+ # (g*f)[2] # => 5
42
+ #
43
+ # def polar(x,y)
44
+ # [Math.hypot(y,x), Math.atan2(y,x)]
45
+ # end
46
+ # def cartesian(magnitude, angle)
47
+ # [magnitude*Math.cos(angle), magnitude*Math.sin(angle)]
48
+ # end
49
+ # p,c = method :polar, method :cartesian
50
+ # (c*p)[3,4] # => [3,4]
51
+ #
52
+ def compose(f)
53
+ if self.respond_to?(:arity) && self.arity == 1
54
+ lambda {|*args| self[f[*args]] }
55
+ else
56
+ lambda {|*args| self[*f[*args]] }
57
+ end
58
+ end
59
+
60
+ # * is the natural operator for function composition.
61
+ alias * compose
62
+
63
+ # Return a lambda equivalent to this one with one or more initial
64
+ # arguments applied. When only a single argument
65
+ # is being specified, the >> alias may be simpler to use.
66
+ # Example:
67
+ # product = lambda {|x,y| x*y}
68
+ # doubler = product >> 2
69
+ #
70
+ def apply_head(*first)
71
+ lambda {|*rest| self[*first.concat(rest)]}
72
+ end
73
+
74
+ #
75
+ # Return a lambda equivalent to this one with one or more final arguments
76
+ # applied. When only a single argument is being specified,
77
+ # the << alias may be simpler.
78
+ # Example:
79
+ # difference = lambda {|x,y| x-y }
80
+ # decrement = difference << 1
81
+ #
82
+ def apply_tail(*last)
83
+ lambda {|*rest| self[*rest.concat(last)]}
84
+ end
85
+
86
+ # Here are operator alternatives for these methods. The angle brackets
87
+ # point to the side on which the argument is shifted in.
88
+ # alias >> apply_head # g = f >> 2 -- set first arg to 2
89
+ # alias << apply_tail # g = f << 2 -- set last arg to 2
90
+
91
+ # Return a new lambda that caches the results of this function and
92
+ # only calls the function when new arguments are supplied.
93
+ #
94
+ def memoize
95
+ cache = {} # An empty cache. The lambda captures this in its closure.
96
+ lambda {|*args|
97
+ # notice that the hash key is the entire array of arguments!
98
+ unless cache.has_key?(args) # If no cached result for these args
99
+ cache[args] = self[*args] # Compute and cache the result
100
+ end
101
+ cache[args] # Return result from cache
102
+ }
103
+ end
104
+ # A (probably unnecessary) unary + operator for memoization
105
+ # Mnemonic: the + operator means "improved"
106
+ alias +@ memoize # cached_f = +f
107
+
108
+ end
109
+
110
+ # I add these here, instead of in overrides because it makes things a
111
+ # lot simpler for sirb.rb to figure out the load order.
112
+ # Add these functional programming methods to Proc and Method classes.
113
+ class Proc; include Functional; end
114
+ class Method; include Functional; end
@@ -0,0 +1,72 @@
1
+ module Sirb #:nodoc:
2
+ # These are general statistics, things that should stand on their own as
3
+ # concepts unattached to vectors or scalars or whatever.
4
+ module GeneralStatistics #:nodoc:
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ archive_method(:max)
9
+ archive_method(:min)
10
+ include InstanceMethods
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+ # Returns the max, the non-nil value, or nil (if both are nil). A block
16
+ # can be passed if a special comparison is wanted (not typically).
17
+ def max(*x, &block)
18
+ return x.first if x.size == 1
19
+ return max2(x[0], x[1], &block) if x.size == 2
20
+ a = x.first
21
+ (1...x.size).each { |b|
22
+ a = max2(a,x[b], &block) }
23
+ a
24
+ end
25
+
26
+ # Returns the first index of the max value
27
+ def max_index(*x, &block)
28
+ x.index(max(*x, &block))
29
+ end
30
+
31
+ # Returns the max, the non-nil value, or nil (if both are nil). A block
32
+ # can be passed if a special comparison is wanted (not typically).
33
+ def max2(x,y, &block)
34
+ return y if x.nil?
35
+ return x if y.nil?
36
+ if block_given?
37
+ yield(x,y)
38
+ else
39
+ (x <=> y) > 0 ? x : y
40
+ end
41
+ end
42
+
43
+ # Min of any number of items
44
+ def min(*x, &block)
45
+ return x.first if x.size == 1
46
+ return min2(x[0], x[1], &block) if x.size == 2
47
+ a = x.first
48
+ (1...x.size).each { |b|
49
+ a = min2(a,x[b], &block) }
50
+ a
51
+ end
52
+
53
+ # Returns the first index of the min value
54
+ def min_index(*x, &block)
55
+ x.index(min(*x, &block))
56
+ end
57
+
58
+ # Returns the min, the non-nil value, or nil (if both are nil). A block
59
+ # can be passed if a special comparison is wanted (not typically).
60
+ def min2(x,y, &block)
61
+ return y if x.nil?
62
+ return x if y.nil?
63
+ if block_given?
64
+ yield(x,y)
65
+ else
66
+ (x <=> y) < 0 ? x : y
67
+ end
68
+ end
69
+
70
+ end # InstanceMethods
71
+ end # GeneralStatistics
72
+ end # Sirb
@@ -0,0 +1,139 @@
1
+ module Sirb #:nodoc:
2
+ # These are general methods for comparing enumerables. This list seems
3
+ # to grow quite a bit as I build up other libraries, so expect this to
4
+ # grow.
5
+ module InterEnumerableStatistics #:nodoc:
6
+
7
+ # Multiplies the values:
8
+ # >> product(1,2,3)
9
+ # => 6.0
10
+ def product(*x)
11
+ x.inject(1.0) {|sum, a| sum *= a}
12
+ end
13
+
14
+ # There are going to be a lot more of these kinds of things, so pay
15
+ # attention.
16
+ def to_pairs(x, y, &block)
17
+ n = min(x.size, y.size)
18
+ (0...n).map {|i| block.call(x[i], y[i]) }
19
+ end
20
+
21
+ # Finds the tanimoto coefficient: the intersection set size / union set
22
+ # size. This is used to find the distance between two vectors.
23
+ # >> cor([1,2,3], [2,3,5])
24
+ # => 0.981980506061966
25
+ # >> tanimoto_pairs([1,2,3], [2,3,5])
26
+ # => 0.5
27
+ def tanimoto_pairs(x,y)
28
+ intersect(x,y).size / union(x,y).size.to_f
29
+ end
30
+
31
+ # Sometimes it just helps to have things spelled out. These are all
32
+ # part of the Array class.
33
+
34
+ # All of the left and right hand sides, excluding duplicates.
35
+ # "The union of x and y"
36
+ def union(x,y)
37
+ x | y
38
+ end
39
+
40
+ # What's shared on the left and right hand sides
41
+ # "The intersection of x and y"
42
+ def intersect(x,y)
43
+ x & y
44
+ end
45
+
46
+ # Everything on the left hand side except what's shared on the right
47
+ # hand side.
48
+ # "The relative compliment of y in x"
49
+ def compliment(x,y)
50
+ x - y
51
+ end
52
+
53
+ # Everything but what's shared
54
+ def exclusive_not(x,y)
55
+ (x | y) - (x & y)
56
+ end
57
+
58
+ # Finds the cartesian product, excluding duplicates items and self-
59
+ # referential pairs. Yields the block value if given.
60
+ def cartesian_product(x,y, &block)
61
+ x,y = x.uniq.dup, y.uniq.dup
62
+ pairs = x.inject([]) do |cp, i|
63
+ cp | y.map{|b| i == b ? nil : [i,b]}.compact
64
+ end
65
+ return pairs unless block_given?
66
+ pairs.map{|p| yield p.first, p.last}
67
+ end
68
+ alias :cp :cartesian_product
69
+ alias :permutations :cartesian_product
70
+
71
+ # Sigma of pairs. Returns a single float, or whatever object is sent in.
72
+ # Example: sigma_pairs([1,2,3], [4,5,6], 0) {|x, y| x + y}
73
+ # returns 21 instead of 21.0.
74
+ def sigma_pairs(x, y, z=0.0, &block)
75
+ to_pairs(x,y,&block).inject(z) {|sum, i| sum += i}
76
+ end
77
+
78
+ # Takes any number of enumerables and returns the range for all of them.
79
+ # This is an O(n*3) operation.
80
+ def range_for(*args)
81
+ range_pairs(p_max(*args), p_min(*args))
82
+ end
83
+
84
+ # Returns the range of each position between the two pairs.
85
+ def range_pairs(x,y)
86
+ to_pairs(x,y) {|a,b| max(a,b) - min(a,b)}
87
+ end
88
+
89
+ # Returns the Euclidian distance between all points of a set of enumerables
90
+ def euclidian_distance(x,y)
91
+ Math.sqrt(sigma_pairs(x,y) {|a, b| (a - b) ** 2})
92
+ end
93
+
94
+ # Returns a random integer in the range for any number of lists. This
95
+ # is a way to get a random vector that is tenable based on the sample
96
+ # data. For example, given two sets of numbers:
97
+ #
98
+ # a = [1,2,3]; b = [8,8,8]
99
+ #
100
+ # rand_in_pair_range will return a value >= 1 and <= 8 in the first
101
+ # place, >= 2 and <= 8 in the second place, and >= 3 and <= 8 in the
102
+ # last place.
103
+ # Works for integers. Rethink this for floats. May consider setting up
104
+ # FixedRange for floats. O(n*5)
105
+ def rand_in_range(*args)
106
+ range = range_for(*args)
107
+ min = p_min(*args)
108
+ (0...range.size).inject([]) do |ary, i|
109
+ ary << (rand(range[i] + 1) + min[i])
110
+ end
111
+ end
112
+
113
+ # Finds the correlation between two enumerables.
114
+ # Example: cor([1,2,3], [2,3,5)
115
+ # return 0.981980506061966
116
+ def correlation(x, y)
117
+ n = min(x.size, y.size)
118
+ ( sigma_pairs(x,y) { |a,b| a * b } - (( x.sum * y.sum ) / n.to_f)) / ((n - 1 ) * x.std * y.std)
119
+ end
120
+ alias :cor :correlation
121
+
122
+ # Returns the max of two or more enumerables.
123
+ # >> p_max([1,2,3], [4,5,6], [0,2,9])
124
+ # => [4, 5, 9]
125
+ def p_max(*enums)
126
+ n = min(*enums.map{ |x| x.size} )
127
+ (0...n).map { |i| max(*enums.map{ |x| x[i] }) }
128
+ end
129
+
130
+ # Returns the min of two or more enumerables.
131
+ # >> p_min([1,2,3], [4,5,6], [0,2,9])
132
+ # => [0, 2, 3]
133
+ def p_min(*enums)
134
+ n = min(*enums.map{ |x| x.size} )
135
+ (0...n).map { |i| min(*enums.map{ |x| x[i] }) }
136
+ end
137
+ end
138
+
139
+ end