davidrichards-just_enumerable_stats 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,8 @@
1
+ $: << File.join(File.dirname(__FILE__), "/../lib")
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'just_enumerable_stats'
5
+
6
+ Spec::Runner.configure do |config|
7
+
8
+ end