davidrichards-just_enumerable_stats 0.0.2
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/README.rdoc +147 -0
- data/VERSION.yml +4 -0
- data/bin/jes +27 -0
- data/lib/fixed_range.rb +46 -0
- data/lib/just_enumerable_stats/stats.rb +503 -0
- data/lib/just_enumerable_stats.rb +498 -0
- data/spec/fixed_range_spec.rb +77 -0
- data/spec/just_enumerable_stats/stats_spec.rb +459 -0
- data/spec/just_enumerable_stats_spec.rb +449 -0
- data/spec/spec_helper.rb +8 -0
- metadata +65 -0
@@ -0,0 +1,449 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "JustEnumerableStats" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@a = [1,2,3]
|
7
|
+
@b = [8,4,2]
|
8
|
+
@doubler = lambda{|e| e * 2}
|
9
|
+
@inverser = lambda{|e| 1/e.to_f}
|
10
|
+
@inverse_matcher = lambda{|a, b| 1/a <=> 1/b}
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be able to generate a random number between two integers" do
|
14
|
+
val = (1..100).map {rand_between(1,10)}
|
15
|
+
(val.min >= 1).should be_true
|
16
|
+
(val.max <= 10).should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be able to generate a random number between two floats" do
|
20
|
+
val = (1..100).map {rand_in_floats(1.0,10.0)}
|
21
|
+
(val.min >= 1).should be_true
|
22
|
+
(val.max <= 10).should be_true
|
23
|
+
val.all? {|v| v.should be_is_a(Float)}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to work with floats from rand_between" do
|
27
|
+
val = (1..100).map {rand_between(1.0,10.0)}
|
28
|
+
(val.min >= 1).should be_true
|
29
|
+
(val.max <= 10).should be_true
|
30
|
+
val.all? {|v| v.should be_is_a(Float)}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have an original max" do
|
34
|
+
@a.original_max.should eql(3)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have an original min" do
|
38
|
+
@a.original_min.should eql(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have a max" do
|
42
|
+
@a.max.should eql(3)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should have a max that takes a block" do
|
46
|
+
val = @a.max &@inverse_matcher
|
47
|
+
val.should eql(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be able to use a default block for max" do
|
51
|
+
@a.default_block = @inverse_matcher
|
52
|
+
@a.max.should eql(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should know the index of the max value" do
|
56
|
+
@a.max_index.should eql(2)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should find the first index value with max_index, in case there are duplicates" do
|
60
|
+
[1,2,3,3].max_index.should eql(2)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should use a block to find the max index" do
|
64
|
+
val = @a.max_index &@inverse_matcher
|
65
|
+
val.should eql(0)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should be able to use a default block to find the max index" do
|
69
|
+
@a.default_block = @inverse_matcher
|
70
|
+
@a.max_index.should eql(0)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have a min" do
|
74
|
+
@a.min.should eql(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should have a min that takes a block" do
|
78
|
+
val = @a.min &@inverse_matcher
|
79
|
+
val.should eql(3)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should be able to use a default block for min" do
|
83
|
+
@a.default_block = @inverse_matcher
|
84
|
+
@a.min.should eql(3)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should know the index of the min value" do
|
88
|
+
@a.min_index.should eql(0)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should find the first index value with min_index, in case there are duplicates" do
|
92
|
+
[1,1,2,3].min_index.should eql(0)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should use a block to find the min index" do
|
96
|
+
val = @a.min_index &@inverse_matcher
|
97
|
+
val.should eql(2)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should be able to use a default block to find the min index" do
|
101
|
+
@a.default_block = @inverse_matcher
|
102
|
+
@a.min_index.should eql(2)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should be able to sum a list" do
|
106
|
+
@a.sum.should eql(6)
|
107
|
+
[1, 2, 3.0].sum.should eql(6.0)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should offer sum with a precision of 1.0e-15" do
|
111
|
+
[0.1, 0.2, 0.3].sum.should be_close(0.6, 1.0e-15)
|
112
|
+
[0.1, 0.2, 0.3].sum.should_not be_close(0.6, 1.0e-16)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should be able to evaluate a sum with a block" do
|
116
|
+
@a.sum(&@doubler).should eql(12)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should be able to use the default block to evaluate sum" do
|
120
|
+
@a.default_block = @doubler
|
121
|
+
@a.sum.should eql(12)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be able to find the arithmetic average, mean, or avg" do
|
125
|
+
@a.average.should eql(2)
|
126
|
+
@a.mean.should eql(2)
|
127
|
+
@a.avg.should eql(2)
|
128
|
+
[1, 2, 3.0].average.should eql(2.0)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be able to calculate average with a block" do
|
132
|
+
@a.average(&@doubler).should eql(4)
|
133
|
+
@a.mean(&@doubler).should eql(4)
|
134
|
+
@a.avg(&@doubler).should eql(4)
|
135
|
+
[1, 2, 3.0].average(&@doubler).should eql(4.0)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should be able to calculate average with a default block" do
|
139
|
+
@a.default_block = @doubler
|
140
|
+
@a.average.should eql(4)
|
141
|
+
@a.mean.should eql(4)
|
142
|
+
@a.avg.should eql(4)
|
143
|
+
b = [1, 2, 3.0]
|
144
|
+
b.default_block = @doubler
|
145
|
+
b.average.should eql(4.0)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should be able to calculate the variance" do
|
149
|
+
@a.variance.should eql(1)
|
150
|
+
@a.var.should eql(1)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be able to calculate the variance with a block" do
|
154
|
+
@a.variance(&@doubler).should eql(4)
|
155
|
+
@a.var(&@doubler).should eql(4)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should be able to calculate the variance with a default block" do
|
159
|
+
@a.default_block = @doubler
|
160
|
+
@a.variance.should eql(4)
|
161
|
+
@a.var.should eql(4)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should be able to calculate the standard deviation" do
|
165
|
+
@a.standard_deviation.should eql(1.0)
|
166
|
+
@a.std.should eql(1.0)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should be able to calculate the standard deviation with a block" do
|
170
|
+
@a.standard_deviation(&@doubler).should eql(2.0)
|
171
|
+
@a.std(&@doubler).should eql(2.0)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should be able to calculate the standard deviation with a default block" do
|
175
|
+
@a.default_block = @doubler
|
176
|
+
@a.standard_deviation.should eql(2.0)
|
177
|
+
@a.std.should eql(2.0)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should be able to calculate the median value" do
|
181
|
+
@a.median.should eql(2)
|
182
|
+
[1,4,3,2,5].median.should eql(3)
|
183
|
+
[1,9,2,8].median.should eql(5.0)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should be able to get a max and min range from the list" do
|
187
|
+
@a.range.should eql([1, 3])
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should be able to pass a block to the range method" do
|
191
|
+
@a.range(&@inverse_matcher).should eql([3, 1])
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should be able to use a default block for the range method" do
|
195
|
+
@a.default_block = @inverse_matcher
|
196
|
+
@a.range.should eql([3, 1])
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should have a getter and a setter on the range class" do
|
200
|
+
@a.range_class = Array
|
201
|
+
@a.range_class.should eql(Array)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should be able to instantiate a range" do
|
205
|
+
@a.range_as_range.should eql(Range.new(1, 3))
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should be able to instantiate a range with a block" do
|
209
|
+
@a.range_as_range(&@inverse_matcher).should eql(Range.new(3, 1))
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should be able to instantiate a range with a default block" do
|
213
|
+
@a.default_block = @inverse_matcher
|
214
|
+
@a.range_as_range.should eql(Range.new(3, 1))
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should be able to create a new object, sorted" do
|
218
|
+
a = [3,1,2]
|
219
|
+
b = a.new_sort
|
220
|
+
b.object_id.should_not eql(a.object_id)
|
221
|
+
b.should eql([1,2,3])
|
222
|
+
a << 4
|
223
|
+
b.should eql([1,2,3])
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should be able to take a block and still do new_sort" do
|
227
|
+
@a.new_sort(&@doubler).should eql([2,4,6])
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be able to take a default block and still do new_sort" do
|
231
|
+
@a.default_block = @doubler
|
232
|
+
@a.new_sort.should eql([2,4,6])
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should be able to rank a list" do
|
236
|
+
@b.rank.should eql([3,2,1])
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should be able to use a block in rank" do
|
240
|
+
@b.rank(&@inverser).should eql([1,2,3])
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should be able to use a default block in rank" do
|
244
|
+
@b.default_block = @inverser
|
245
|
+
@b.rank.should eql([1,2,3])
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should be able to get the order of values, handling duplicates" do
|
249
|
+
[10,5,5,1].order.should eql([4,2,3,1])
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should be able to take a block in the ordering" do
|
253
|
+
[10,5,5,1].order(&@inverser).should eql([1,2,3,4])
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should be able to take a default block in the ordering" do
|
257
|
+
a = [10,5,5,1]
|
258
|
+
a.default_block = @inverser
|
259
|
+
a.order.should eql([1,2,3,4])
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should be able to calculate the cumulative sum" do
|
263
|
+
@a.cumulative_sum.should eql([1,3,6])
|
264
|
+
@a.cum_sum.should eql([1,3,6])
|
265
|
+
[1,2,3.0].cum_sum.should eql([1.0, 3.0, 6.0])
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should be able to take a block to produce the cumulative sum" do
|
269
|
+
@a.cumulative_sum(&@doubler).should eql([2,6,12])
|
270
|
+
@a.cum_sum(&@doubler).should eql([2,6,12])
|
271
|
+
[1,2,3.0].cum_sum(&@doubler).should eql([2.0, 6.0, 12.0])
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should be able to take a default block to produce the cumlative sum" do
|
275
|
+
@a.default_block = @doubler
|
276
|
+
@a.cumulative_sum.should eql([2,6,12])
|
277
|
+
@a.cum_sum.should eql([2,6,12])
|
278
|
+
b = [1,2,3.0]
|
279
|
+
b.default_block = @doubler
|
280
|
+
b.cum_sum.should eql([2.0, 6.0, 12.0])
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should be able to calculate the cumulative product" do
|
284
|
+
@a.cumulative_product.should eql([1,2,6])
|
285
|
+
@a.cum_prod.should eql([1,2,6])
|
286
|
+
[1,2,3.0].cum_prod.should eql([1.0, 2.0, 6.0])
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should be able to take a block to produce the cumulative product" do
|
290
|
+
@a.cumulative_product(&@doubler).should eql([2,8,48])
|
291
|
+
@a.cum_prod(&@doubler).should eql([2,8,48])
|
292
|
+
[1,2,3.0].cum_prod(&@doubler).should eql([2.0, 8.0, 48.0])
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should be able to take a default block to produce the cumlative product" do
|
296
|
+
@a.default_block = @doubler
|
297
|
+
@a.cumulative_product.should eql([2,8,48])
|
298
|
+
@a.cum_prod.should eql([2,8,48])
|
299
|
+
b = [1,2,3.0]
|
300
|
+
b.default_block = @doubler
|
301
|
+
b.cum_prod.should eql([2.0, 8.0, 48.0])
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should be able to produce the cumulative max" do
|
305
|
+
@a.cumulative_max.should eql([1,2,3])
|
306
|
+
@a.cum_max.should eql([1,2,3])
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should be able to produce the cumulative max with a block" do
|
310
|
+
@a.cumulative_max(&@doubler).should eql([2,4,6])
|
311
|
+
@a.cum_max(&@doubler).should eql([2,4,6])
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should be able to produce the cumulative max with a default block" do
|
315
|
+
@a.default_block = @doubler
|
316
|
+
@a.cumulative_max.should eql([2,4,6])
|
317
|
+
@a.cum_max.should eql([2,4,6])
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should be able to produce the cumulative min" do
|
321
|
+
@a.cumulative_min.should eql([1,1,1])
|
322
|
+
@a.cum_min.should eql([1,1,1])
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should be able to produce the cumulative min with a block" do
|
326
|
+
@a.cumulative_min(&@doubler).should eql([2,2,2])
|
327
|
+
@a.cum_min(&@doubler).should eql([2,2,2])
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should be able to produce the cumulative min with a default block" do
|
331
|
+
@a.default_block = @doubler
|
332
|
+
@a.cumulative_min.should eql([2,2,2])
|
333
|
+
@a.cum_min.should eql([2,2,2])
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should be able to multiply the values in the list" do
|
337
|
+
@a.product.should eql(6)
|
338
|
+
end
|
339
|
+
|
340
|
+
it "should be able to yield an operation on pairs" do
|
341
|
+
val = @a.to_pairs(@b) {|a, b| a + b}
|
342
|
+
val.should eql([9,6,5])
|
343
|
+
end
|
344
|
+
|
345
|
+
# [1,2,3] and [2,3,4] have 2 items in common, and 4 unique items together.
|
346
|
+
# So 2 items / 4 items is 0.5
|
347
|
+
it "should be able to find the tanimoto coefficient" do
|
348
|
+
b = [2,3,4]
|
349
|
+
@a.tanimoto_pairs(b).should eql(0.5)
|
350
|
+
@a.tanimoto_correlation(b).should eql(0.5)
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should have long hand for union" do
|
354
|
+
@a.union(@b).should eql([1, 2, 3, 8, 4])
|
355
|
+
end
|
356
|
+
|
357
|
+
it "should have long hand for intersect" do
|
358
|
+
@a.intersect(@b).should eql([2])
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should have a long hand for compliment" do
|
362
|
+
@a.compliment(@b).should eql([1, 3])
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should have a long hand for exclusive not" do
|
366
|
+
@a.exclusive_not(@b).should eql([1,3,8,4])
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should be able to generate a cartesian product" do
|
370
|
+
@a.cartesian_product(@b).should eql([[1, 8], [1, 4], [1, 2], [2, 8], [2, 4], [3, 8], [3, 4], [3, 2]])
|
371
|
+
@a.cp(@b).should eql([[1, 8], [1, 4], [1, 2], [2, 8], [2, 4], [3, 8], [3, 4], [3, 2]])
|
372
|
+
@a.permutations(@b).should eql([[1, 8], [1, 4], [1, 2], [2, 8], [2, 4], [3, 8], [3, 4], [3, 2]])
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should be able to add pairwise computations" do
|
376
|
+
# Remember:
|
377
|
+
# @a = [1,2,3]
|
378
|
+
# @b = [8,4,2]
|
379
|
+
val = @a.sigma_pairs(@b) {|a, b| a / b}
|
380
|
+
val.should eql(1/8 + 2/4 + 3/2)
|
381
|
+
val = @a.sigma_pairs(@b) {|a, b| a * b}
|
382
|
+
val.should eql(1*8 + 2*4 + 3*2)
|
383
|
+
end
|
384
|
+
|
385
|
+
it "should be able to find the euclidian distance between two lists" do
|
386
|
+
@a.euclidian_distance(@b).should be_close(7.348, 0.001)
|
387
|
+
[1,2,3].euclidian_distance([2,3,4]).should eql(Math.sqrt(3.0))
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should be able to generate a list of random numbers, each within the range between two lists" do
|
391
|
+
a = [1,0,-1]
|
392
|
+
b = [10,0,-20]
|
393
|
+
list_min = a.min_of_lists(b)
|
394
|
+
list_max = a.max_of_lists(b)
|
395
|
+
100.times do
|
396
|
+
val = a.rand_in_range(b)
|
397
|
+
val.each_with_index do |e, i|
|
398
|
+
e >= list_min[i]
|
399
|
+
e <= list_max[i]
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
it "should be able to find random numbers in the range between many lists" do
|
405
|
+
a = [1,0,-2]
|
406
|
+
b = [10,0,-20]
|
407
|
+
c = [2,0,-1]
|
408
|
+
list_min = a.min_of_lists(b)
|
409
|
+
list_max = a.max_of_lists(b)
|
410
|
+
100.times do
|
411
|
+
val = a.rand_in_range(b)
|
412
|
+
val.each_with_index do |e, i|
|
413
|
+
e >= list_min[i]
|
414
|
+
e <= list_max[i]
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should be able to yield a block for columns of values" do
|
420
|
+
a = [1,2,6.0]
|
421
|
+
b = [2,6.0,1]
|
422
|
+
c = [6.0,1,2]
|
423
|
+
val = a.yield_transpose(b, c) {|e| e.mean}
|
424
|
+
val.should eql([3.0, 3.0, 3.0])
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should be able to transpose a list of lists (not dependent on Array#transpose)" do
|
428
|
+
a = [1,2,6.0]
|
429
|
+
b = [2,6.0,1]
|
430
|
+
c = [6.0,1,2]
|
431
|
+
val = a.yield_transpose(b, c)
|
432
|
+
val.should eql( [[1, 2, 6.0], [2, 6.0, 1], [6.0, 1, 2]])
|
433
|
+
end
|
434
|
+
|
435
|
+
it "should be able to get the correlation between two lists" do
|
436
|
+
@a.correlation([2,3,5]).should be_close(0.981, 0.001)
|
437
|
+
@a.cor([2,3,5]).should be_close(0.981, 0.001)
|
438
|
+
end
|
439
|
+
|
440
|
+
it "should be able to return the max of lists" do
|
441
|
+
@a.max_of_lists(@b).should eql([8,4,3])
|
442
|
+
@a.max_of_lists(@b, [10,10,10]).should eql([10,10,10])
|
443
|
+
end
|
444
|
+
|
445
|
+
it "should be able to return the min of lists" do
|
446
|
+
@a.min_of_lists(@b).should eql([1,2,2])
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|