121 lines
3.5 KiB
JavaScript

'use strict';
module.exports = function (math) {
var Matrix = math.type.Matrix,
Unit = require('../../type/Unit'),
BigNumber = math.type.BigNumber,
collection = math.collection,
isNumber = require('../../util/number').isNumber,
isCollection = collection.isCollection,
flatten = require('../../util/array').flatten;
/**
* Compute the median of a matrix or a list with values. The values are
* sorted and the middle value is returned. In case of an even number of
* values, the average of the two middle values is returned.
* Supported types of values are: Number, BigNumber, Unit
*
* In case of a (multi dimensional) array or matrix, the median of all
* elements will be calculated.
*
* Syntax:
*
* math.median(a, b, c, ...)
* math.median(A)
*
* Examples:
*
* math.median(5, 2, 7); // returns 5
* math.median([3, -1, 5, 7]); // returns 4
*
* See also:
*
* mean, min, max, sum, prod, std, var
*
* @param {... *} args A single matrix or or multiple scalar values
* @return {*} The median
*/
math.median = function median(args) {
if (arguments.length == 0) {
throw new SyntaxError('Function median requires one or more parameters (0 provided)');
}
if (isCollection(args)) {
if (arguments.length == 1) {
// median([a, b, c, d, ...])
return _median(args.valueOf(), false);
}
else if (arguments.length == 2) {
// median([a, b, c, d, ...], dim)
// TODO: implement median(A, dim)
throw new Error('median(A, dim) is not yet supported');
//return collection.reduce(arguments[0], arguments[1], ...);
}
else {
throw new SyntaxError('Wrong number of parameters');
}
}
else {
// median(a, b, c, d, ...)
var argArr = new Array(arguments.length);
for (var i = 0; i < argArr.length; ++i) {
argArr[i] = arguments[i];
}
return _median(argArr, true);
}
};
/**
* Recursively calculate the median of an n-dimensional array
* @param {Array} array
* @param {Boolean} isFlat
* @return {Number} median
* @private
*/
function _median(array, isFlat) {
if (!isFlat) {
array = flatten(array);
}
var num = array.length;
if (num == 0) {
throw new Error('Cannot calculate median of an empty array');
}
if (num % 2 == 0) {
// even: return the average of the two middle values
var mid = num / 2 - 1;
var right = math.partitionSelect(array, mid + 1);
// array now partitioned at mid + 1, take max of left part
var left = array[mid];
for (var i = 0; i < mid; ++i) {
if (math.compare(array[i], left) > 0) {
left = array[i];
}
}
if (!isNumber(left) && !(left instanceof BigNumber) && !(left instanceof Unit)) {
throw new math.error.UnsupportedTypeError('median', math['typeof'](left));
}
if (!isNumber(right) && !(right instanceof BigNumber) && !(right instanceof Unit)) {
throw new math.error.UnsupportedTypeError('median', math['typeof'](right));
}
return math.divide(math.add(left, right), 2);
}
else {
// odd: return the middle value
var middle = math.partitionSelect(array, (num - 1) / 2);
if (!isNumber(middle) && !(middle instanceof BigNumber) && !(middle instanceof Unit)) {
throw new math.error.UnsupportedTypeError('median', math['typeof'](middle));
}
return middle;
}
}
};