davidrichards-just_enumerable_stats 0.0.8 → 0.0.11

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