davidrichards-just_enumerable_stats 0.0.8 → 0.0.11

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.
@@ -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