gauss
Version:
JavaScript statistics, analytics, and set library
712 lines (703 loc) • 21.5 kB
JavaScript
/**
* Test Vector data structure and methods.
*/
var vows = require('vows'),
assert = require('assert');
var Vector = require('../lib/gauss').Vector;
var set = new Vector([10, 82, 67, 17, 36, 3, 1, 61, 33, 20,
18, 35, 15, 39, 52, 85, 17, 92, 88, 70,
66, 85, 93, 81, 70, 41, 40, 22, 38, 52,
86, 60, 64, 38, 87, 15, 92, 61, 93, 17,
38, 68, 11, 98, 62, 75, 94, 63, 49, 97]);
var majority = new Vector([10, 82, 67, 17, 36, 3, 1, 61, 33, 20,
18, 35, 15, 39, 52, 85, 17, 92, 88, 70,
66, 85, 93, 81, 70, 41, 40, 22, 38, 52,
86, 60, 64, 38, 87, 15, 92, 61, 93, 17,
38, 68, 11, 98, 62, 75, 94, 63, 17]);
var heterogeneous = new Vector([72, -15, -50, 19, -33, -2, 60, -28, -13, -2,
17, -20, 24, 13, 33, -68, 75, -4, -18, -4,
19, 8, -12, -11, -29, -1, -18, 16, 14, 34,
-26, 4, -26, 49, -72, 77, -31, 32, -76, 21,
30, -57, 87, -36, 13, 19, -31, -14, 48]);
var prices = new Vector([22.2734, 22.194, 22.0847, 22.1741, 22.184, 22.1344,
22.2337, 22.4323, 22.2436, 22.2933, 22.1542, 22.3926,
22.3816, 22.6109, 23.3558, 24.0519, 23.753, 23.8324,
23.9516, 23.6338, 23.8225, 23.8722, 23.6537, 23.187,
23.0976, 23.326, 22.6805, 23.0976, 22.4025, 22.1725]);
function deepEqualWithNaN(actual, expected) {
if (actual.length !== expected.length) {
throw new RangeError();
}
else {
for (var i = 0; i < expected.length; i++) {
assert.deepEqual(isNaN(actual[i]) ? true : actual[i], isNaN(expected[i]) ? true : expected[i]);
}
}
}
vows.describe('Vector').addBatch({
// Statistical methods
'Minimum': {
topic: set.min(),
'1': function(topic) {
assert.equal(topic, 1);
}
},
'Maximum': {
topic: set.max(),
'98': function(topic) {
assert.equal(topic, 98);
}
},
'Sum': {
topic: set.sum(),
'2697': function(topic) {
assert.equal(topic, 2697);
}
},
'Product': {
topic: set.product(),
'7.120776302117291e+80': function(topic) {
assert.equal(topic, 7.120776302117291e+80);
}
},
'Arithmetic Mean': {
topic: set.mean(),
'53.94': function(topic) {
assert.equal(topic, 53.94);
}
},
'Geometric Mean': {
topic: set.gmean(),
'41.40478623971778': function(topic) {
assert.equal(topic, 41.40478623971778);
}
},
'Harmonic Mean': {
topic: set.hmean(),
'19.0456068931919': function(topic) {
assert.equal(topic, 19.0456068931919);
}
},
'Quadratic Mean': {
topic: set.qmean(),
'61.26548783777046': function(topic) {
assert.equal(topic, 61.26548783777046);
}
},
'Power Mean': {
topic: set,
'p = -1, harmonic mean': function(topic) {
assert.equal(topic.pmean(-1), topic.hmean());
},
'p = 1, arithmetic mean': function(topic) {
assert.equal(topic.pmean(1), topic.mean());
},
'p = 2, quadratic mean': function(topic) {
assert.equal(topic.pmean(2), topic.qmean());
}
},
'Median': {
'(Even number of elements)': {
topic: set.median(),
'60.5, mean of the two middle numbers':
function(topic) {
assert.equal(topic, 60.5);
}
},
'(Odd number of elements)': {
topic: function() {
var odd = set.copy();
odd.pop();
return odd;
},
'60': function(topic) {
assert.equal(topic.median(), 60);
}
}
},
'Mode': {
'(Tie)': {
topic: set.mode().toArray(),
'[17, 38]': function(topic) {
assert.deepEqual(topic, [17, 38]);
}
},
'(Majority)': {
topic: majority.mode(),
'17': function(topic) {
assert.equal(topic, 17);
}
},
'(Uniform)': {
topic: new Vector([1, 2, 3, 4]).mode().toArray(),
'[1, 2, 3, 4]': function(topic) {
assert.deepEqual(topic, [1, 2, 3, 4]);
}
}
},
'Range': {
topic: set.range(),
'97': function(topic) {
assert.equal(topic, 97);
}
},
'Variance': {
topic: set.variance(),
'843.9364': function(topic) {
assert.equal(topic, 843.9364);
}
},
'Standard Deviation': {
'(Population)': {
topic: set.stdev(),
'29.050583470904677': function(topic) {
assert.equal(topic, 29.050583470904677);
}
},
'(Percentage - 95%)': {
topic: set.stdev(.95),
'28.226483574827384': function(topic) {
assert.equal(topic, 28.226483574827384);
}
}
},
'Frequency': {
topic: set.frequency(17),
'3': function(topic) {
assert.equal(topic, 3);
}
},
'Percentile': {
topic: set.percentile(.90),
'93': function(topic) {
assert.equal(topic, 93);
}
},
'Density': {
topic: set.density(.25).toArray(),
'Array of values (25% density)': function(topic) {
assert.deepEqual(topic, [39, 40, 41, 49, 52, 52, 60, 61, 61, 62, 63, 64]);
}
},
'Distribution': {
'(Absolute Frequency)': {
topic: set.distribution(),
'{Element: Count}': function(topic) {
assert.deepEqual(topic, {
1: 1, 3: 1, 10: 1, 11: 1, 15: 2,
17: 3, 18: 1, 20: 1, 22: 1, 33: 1,
35: 1, 36: 1, 38: 3, 39: 1, 40: 1,
41: 1, 49: 1, 52: 2, 60: 1, 61: 2,
62: 1, 63: 1, 64: 1, 66: 1, 67: 1,
68: 1, 70: 2, 75: 1, 81: 1, 82: 1,
85: 2, 86: 1, 87: 1, 88: 1, 92: 2,
93: 2, 94: 1, 97: 1, 98: 1
});
}
},
'(Relative Frequency)': {
topic: set.distribution('relative'),
'{Element: Ratio}': function(topic) {
assert.deepEqual(topic, {
1: 0.02, 3: 0.02, 10: 0.02, 11: 0.02, 15: 0.04,
17: 0.06, 18: 0.02, 20: 0.02, 22: 0.02, 33: 0.02,
35: 0.02, 36: 0.02, 38: 0.06, 39: 0.02, 40: 0.02,
41: 0.02, 49: 0.02, 52: 0.04, 60: 0.02, 61: 0.04,
62: 0.02, 63: 0.02, 64: 0.02, 66: 0.02, 67: 0.02,
68: 0.02, 70: 0.04, 75: 0.02, 81: 0.02, 82: 0.02,
85: 0.04, 86: 0.02, 87: 0.02, 88: 0.02, 92: 0.04,
93: 0.04, 94: 0.02, 97: 0.02, 98: 0.02
});
}
}
},
'Quantile': {
'(Quartile: 4-quantiles)': {
topic: set.quantile(4).toArray(),
'[ 33, 60, 82 ]': function(topic) {
assert.deepEqual(topic, [ 33, 60, 82 ]);
}
},
'(Decile: 10-quantiles)': {
topic: set.quantile(10).toArray(),
'[15, 18, 36, 40, 60, 64, 70, 85, 92]': function(topic) {
assert.deepEqual(topic, [
15, 18, 36, 40, 60,
64, 70, 85, 92
]);
}
}
},
'Delta': {
topic: set.delta().toArray(),
'Sequential differences': function(topic) {
assert.deepEqual(topic, [
72, -15, -50, 19, -33, -2, 60, -28, -13, -2,
17, -20, 24, 13, 33, -68, 75, -4, -18, -4,
19, 8, -12, -11, -29, -1, -18, 16, 14, 34,
-26, 4, -26, 49, -72, 77, -31, 32, -76, 21,
30, -57, 87, -36, 13, 19, -31, -14, 48
]);
}
},
'Simple Moving Average': {
'(10)': {
topic: prices.sma(10).toArray(),
'10-period SMA': function(topic) {
assert.deepEqual(topic, [
22.22475, 22.21283, 22.232689999999998,
22.26238, 22.30606, 22.42324,
22.61499, 22.76692, 22.90693,
23.07773, 23.211779999999997, 23.37861,
23.52657, 23.653779999999998, 23.711389999999998,
23.68557, 23.61298, 23.50573,
23.43225, 23.27734, 23.13121
]);
}
},
'(1)': {
topic: prices.sma(1),
'Single-period SMA': function(topic) {
assert.deepEqual(topic, prices);
}
}
},
'Exponential Moving Average': {
'(1)': {
topic: prices.ema(1).toArray(),
'Single-period EMA': function(topic) {
assert.deepEqual(topic, prices.toArray());
}
},
'(10)': {
topic: prices.ema(10).toArray(),
'10-period EMA': function(topic) {
assert.deepEqual(topic, [
22.22475, 22.21192272727273, 22.24477314049587,
22.269650751314803, 22.331696069257568, 22.51789678393801,
22.796806459585646, 22.970659830570074, 23.127339861375514,
23.27720534112542, 23.34204073364807, 23.429396963893875,
23.509906606822263, 23.536050860127308, 23.47258706737689,
23.40440760058109, 23.390151673202713, 23.261124096256765,
23.231392442391897, 23.080684725593372, 22.91556023003094
]);
}
},
'(10 Wilder)': {
topic: prices.ema({ period: 10, ratio: function(n) { return 1 / n; } }).toArray(),
'10-period Welles Wilder EMA': function(topic) {
assert.deepEqual(topic, [
22.22475, 22.217695, 22.2351855,
22.24982695, 22.285934255, 22.3929208295,
22.55881874655, 22.678236871895, 22.793653184705498,
22.90944786623495, 22.981883079611453, 23.065944771650308,
23.146570294485276, 23.19728326503675, 23.196254938533073,
23.186389444679765, 23.20035050021179, 23.14836545019061,
23.14328890517155, 23.069210014654395, 22.979539013188955
]);
}
}
},
'Sample': {
'(Arithmetic Mean)': {
topic: set.sample.mean(),
'55.04081632653061': function(topic) {
assert.equal(topic, (set.mean() * set.length) / (set.length - 1));
}
},
'(Geometric Mean)': {
topic: set.sample.gmean(),
'44.67366451004403': function(topic) {
assert.equal(topic, 44.67366451004403);
}
},
'(Harmonic Mean)': {
topic: set.sample.hmean(),
'18.66469475532806': function(topic) {
assert.equal(topic, 18.66469475532806);
}
},
'(Quadratic Mean)': {
topic: set.sample.qmean(),
'61.88748843255635': function(topic) {
assert.equal(topic, 61.88748843255635);
}
},
'(Power Mean)': {
topic: set.sample.pmean(1),
'55.04081632653061': function(topic) {
assert.equal(topic, set.sample.mean());
}
},
'(Variance)': {
topic: set.sample.variance(),
'861.1595918367346': function(topic) {
assert.equal(topic, 861.1595918367346);
}
},
'(Standard Deviation)': {
topic: set.sample.stdev(),
'29.34552081386075': function(topic) {
assert.equal(topic, 29.34552081386075);
}
}
},
// Math methods
'Math': {
topic: (function() {
return new Vector(-10, 82, 67.1, 17.3, 36, 3.5, 1, 61, 33, 20);
})(),
'max': function(topic) {
assert.deepEqual(topic.max(), 82);
},
'min': function(topic) {
assert.equal(topic.min(), -10);
},
'abs': function(topic) {
assert.deepEqual(topic.abs(), new Vector(10, 82, 67.1, 17.3, 36, 3.5, 1, 61, 33, 20));
},
'acos': function(topic) {
deepEqualWithNaN(topic.acos(), topic.map(Math.acos));
},
'asin': function(topic) {
deepEqualWithNaN(topic.asin(), topic.map(Math.asin));
},
'atan': function(topic) {
deepEqualWithNaN(topic.atan(), topic.map(Math.atan));
},
'ceil': function(topic) {
assert.deepEqual(topic.ceil(), topic.map(Math.ceil));
},
'cos': function(topic) {
assert.deepEqual(topic.cos(), topic.map(Math.cos));
},
'exp': function(topic) {
assert.deepEqual(topic.exp(), topic.map(Math.exp));
},
'floor': function(topic) {
assert.deepEqual(topic.floor(), topic.map(Math.floor));
},
'log': function(topic) {
deepEqualWithNaN(topic.log(), topic.map(Math.log));
},
'pow': function(topic) {
assert.deepEqual(topic.pow(2), topic.map(function(x) { return Math.pow(x, 2); }));
},
'round': function(topic) {
assert.deepEqual(topic.round(), topic.map(Math.round));
},
'sin': function(topic) {
assert.deepEqual(topic.sin(), topic.map(Math.sin));
},
'sqrt': function(topic) {
deepEqualWithNaN(topic.sqrt(), topic.map(Math.sqrt));
},
'tan': function(topic) {
assert.deepEqual(topic.tan(), topic.map(Math.tan));
}
},
// Binary methods
'Add': {
topic: (function() {
return new Vector(-2, 2, 3.1);
})(),
'add vector': function(topic) {
assert.deepEqual(topic.add([1, 2, 3]), new Vector([-1, 4, 6.1]));
},
'add scalar': function(topic) {
assert.deepEqual(topic.add(2), new Vector([0, 4, 5.1]));
},
'fail on adding different lengths': function(topic) {
assert.throws(function () {topic.add([1, 2]);}, RangeError);
}
},
'Subtract': {
topic: (function() {
return new Vector(-2, 2, 3.1);
})(),
'subtract vector': function(topic) {
assert.deepEqual(topic.subtract([1, 2, 2]), new Vector([-3, 0, 1.1]));
},
'subtract scalar': function(topic) {
assert.deepEqual(topic.subtract(2), new Vector([-4, 0, 1.1]));
},
'fail on subtracting different lengths': function(topic) {
assert.throws(function () {topic.subtract([1, 2]);}, RangeError);
}
},
'Multiply': {
topic: (function() {
return new Vector(-2, 2, 3.1);
})(),
'multiply vector': function(topic) {
assert.deepEqual(topic.multiply([1, 2, 2]), new Vector([-2, 4, 6.2]));
},
'multiply scalar': function(topic) {
assert.deepEqual(topic.multiply(2), new Vector([-4, 4, 6.2]));
},
'fail on multiply different lengths': function(topic) {
assert.throws(function () {topic.multiply([1, 2]);}, RangeError);
}
},
'Divide': {
topic: (function() {
return new Vector(-2, 2, 3.0);
})(),
'divide vector': function(topic) {
assert.deepEqual(topic.divide([1, 2, 2]), new Vector([-2, 1, 1.5]));
},
'divide scalar': function(topic) {
assert.deepEqual(topic.divide(2), new Vector([-1, 1, 1.5]));
},
'fail on divide different lengths': function(topic) {
assert.throws(function () {topic.divide([1, 2]);}, RangeError);
}
},
'Push': {
'(Return)': {
topic: (function() {
return new Vector().push(1, 2, 3);
})(),
'Vector.push equivalent to Array.push': function(topic) {
assert.equal(topic, [].push(1, 2, 3));
}
},
'(Values)': {
topic: (function() {
var numbers = new Vector();
numbers.push(
22.2734, 22.194, 22.0847, 22.1741, 22.184, 22.1344,
22.2337, 22.4323, 22.2436, 22.2933, 22.1542, 22.3926,
22.3816, 22.6109, 23.3558, 24.0519, 23.753, 23.8324,
23.9516, 23.6338, 23.8225, 23.8722, 23.6537, 23.187,
23.0976, 23.326, 22.6805, 23.0976, 22.4025, 22.1725
);
return numbers.toArray();
})(),
'Push equivalent to instantiation': function(topic) {
assert.deepEqual(topic, prices.toArray());
}
},
'(Sum)': {
topic: (function() {
var numbers = new Vector();
numbers.push(
22.2734, 22.194, 22.0847, 22.1741, 22.184, 22.1344,
22.2337, 22.4323, 22.2436, 22.2933, 22.1542, 22.3926,
22.3816, 22.6109, 23.3558, 24.0519, 23.753, 23.8324,
23.9516, 23.6338, 23.8225, 23.8722, 23.6537, 23.187,
23.0976, 23.326, 22.6805, 23.0976, 22.4025, 22.1725
);
return numbers.sum();
})(),
'685.6774': function(topic) {
assert.equal(topic, (function() {
var sum = 0.0;
for (var i = 0; i < prices.length; i++) {
sum += prices[i];
}
return sum;
})());
}
},
'(Product)': {
topic: (function() {
var numbers = new Vector();
numbers.push(
22.2734, 22.194, 22.0847, 22.1741, 22.184, 22.1344,
22.2337, 22.4323, 22.2436, 22.2933, 22.1542, 22.3926,
22.3816, 22.6109, 23.3558, 24.0519, 23.753, 23.8324,
23.9516, 23.6338, 23.8225, 23.8722, 23.6537, 23.187,
23.0976, 23.326, 22.6805, 23.0976, 22.4025, 22.1725
);
return numbers.product();
})(),
'5.809803264589068e+40': function(topic) {
assert.equal(topic, (function() {
var product = 1.0;
for (var i = 0; i < prices.length; i++) {
product *= prices[i];
}
return product;
})());
}
},
'(Variance)': {
topic: (function() {
var numbers = new Vector();
numbers.push(
22.2734, 22.194, 22.0847, 22.1741, 22.184, 22.1344,
22.2337, 22.4323, 22.2436, 22.2933, 22.1542, 22.3926,
22.3816, 22.6109, 23.3558, 24.0519, 23.753, 23.8324,
23.9516, 23.6338, 23.8225, 23.8722, 23.6537, 23.187,
23.0976, 23.326, 22.6805, 23.0976, 22.4025, 22.1725
);
return numbers.variance();
})(),
'0.468596215155556': function(topic) {
assert.equal(topic, prices.variance());
}
},
'(Heterogeneous Variance)': {
topic: (function() {
var mixed = new Vector();
mixed.push(
72, -15, -50, 19, -33, -2, 60, -28, -13, -2,
17, -20, 24, 13, 33, -68, 75, -4, -18, -4,
19, 8, -12, -11, -29, -1, -18, 16, 14, 34,
-26, 4, -26, 49, -72, 77, -31, 32, -76, 21,
30, -57, 87, -36, 13, 19, -31, -14, 48
);
return mixed.variance().toFixed(12) === heterogeneous.variance().toFixed(12);
})(),
'1437.684298209080': function(topic) {
assert.equal(topic, (function() {
var variance = 0.0;
for (var i = 0; i < heterogeneous.length; i++) {
variance += Math.pow((heterogeneous[i] - heterogeneous.mean()), 2);
}
variance /= heterogeneous.length;
return variance.toFixed(12) === heterogeneous.variance().toFixed(12);
})());
}
}
},
'Copy': {
topic: set.copy().toArray(),
'Copy of parent Vector': function(topic) {
assert.deepEqual(topic, set.toArray());
}
},
'Extend': {
topic: set.extend({
identity: function() {
return this;
},
tail: function() {
return this.slice(1);
}
}),
'Multiple Extensions': function(topic) {
assert.deepEqual([topic.toArray(), topic.tail().toArray(), topic.identity().toArray()],
[set.toArray(), set.slice(1).toArray(), set.toArray()]
);
},
'Extensions Maintain Parent Methods': function(topic) {
assert.equal(topic.identity().sum(), set.sum());
}
},
// Collection methods
'Indices Of': {
topic: heterogeneous.indicesOf(-2),
'[5, 9]': function(topic) {
assert.deepEqual(topic, [5, 9]);
}
},
'Indices By': {
topic: heterogeneous.indicesBy(function(e) { return e === -2 }),
'[5, 9]': function(topic) {
assert.deepEqual(topic, [5, 9]);
}
},
'Find One': {
topic: new Vector([{ x: 0, y: 1 }]),
'Returns object': function(topic) {
assert.deepEqual(topic.findOne({ x: 0 }), { x: 0, y: 1 });
}
},
'Concatenate': {
topic: new Vector(1, 2, 3).concat(4),
'Add element to set': function(topic) {
assert.deepEqual(topic, [1, 2, 3, 4]);
}
},
'Slice': {
topic: new Vector(1, 2, 3),
'Fetch all of set': function(topic) {
assert.deepEqual(topic.slice(), topic);
},
'Fetch part of set': function(topic) {
assert.deepEqual(topic.slice(0, 2), [1, 2]);
}
},
'Split': {
topic: new Vector(1, 2, 3, 4).split(function(e) { return (e % 2) === 0; }),
'Divisible by 2': function(topic) {
assert.deepEqual(topic, [[1, 3], [2, 4]]);
}
},
'Unique': {
topic: new Vector(1, 1, 2, 2, 3, 4, 7, 7).unique(),
'One of each element in array': function(topic) {
assert.deepEqual(topic, [1, 2, 3, 4, 7]);
}
},
'Filter': {
topic: new Vector(1, 2, 3).filter(function(e) { return e < 3; }),
'Less than 3': function(topic) {
assert.deepEqual(topic, [1, 2]);
}
},
'Every': {
topic: (function() {
return new Vector(-3, -5, -1, -8, -10, -1);
})(),
'All values are negative': function(topic) {
assert.equal(topic.every(function(e) { return e < 0; }), true);
},
'All values are not positive': function(topic) {
assert.equal(topic.every(function(e) { return e > 0; }), false);
}
},
'Append': {
topic: (function() {
var appended = new Vector(1, 2, 3);
appended.append([1, 2, 3]);
return appended;
})(),
'[1, 2, 3, 1, 2, 3]': function(topic) {
assert.deepEqual(topic, new Vector(1, 2, 3, 1, 2, 3));
},
'Sum of numbers after append': function(topic) {
assert.equal(topic.sum(), new Vector(1, 2, 3, 1, 2, 3).sum());
}
},
'Some': {
topic: (function() {
return new Vector(3, 5, 1, -8, 10, 1);
})(),
'Some values are negative': function(topic) {
assert.equal(topic.some(function(e) { return e < 0; }), true);
},
'Some values are positive': function(topic) {
assert.equal(topic.some(function(e) { return e > 0; }), true);
}
},
'Reduce': {
topic: set,
'Left to right': function(topic) {
assert.equal(topic.reduce(function(a, b) { return a + b; }), set.sum());
},
'Right to left': function(topic) {
assert.equal(topic.reduceRight(function(a, b) { return a + b; }), set.sum());
}
},
'Equal': {
topic: new Vector(1, 2, 3).equal(new Vector(1, 2, 3)),
'True': function(topic) {
assert.equal(topic, true);
}
},
'Clone': {
topic: set.clone(),
'Instance of parent Vector': function(topic) {
assert.instanceOf(topic, Array);
}
},
'toVector from Array': {
topic: [1, 2, 3, 4],
'Returns new Vector': function(topic) {
assert.equal(topic.toVector().sum(), 10);
}
}
}).export(module);