UNPKG

3.47 kBJavaScriptView Raw
1'use strict'
2
3const flatten = require('../../utils/array').flatten
4const containsCollections = require('../../utils/collection/containsCollections')
5
6function factory (type, config, load, typed) {
7 const add = load(require('../arithmetic/addScalar'))
8 const divide = load(require('../arithmetic/divideScalar'))
9 const compare = load(require('../relational/compare'))
10 const partitionSelect = load(require('../matrix/partitionSelect'))
11 const improveErrorMessage = load(require('./utils/improveErrorMessage'))
12
13 /**
14 * Compute the median of a matrix or a list with values. The values are
15 * sorted and the middle value is returned. In case of an even number of
16 * values, the average of the two middle values is returned.
17 * Supported types of values are: Number, BigNumber, Unit
18 *
19 * In case of a (multi dimensional) array or matrix, the median of all
20 * elements will be calculated.
21 *
22 * Syntax:
23 *
24 * math.median(a, b, c, ...)
25 * math.median(A)
26 *
27 * Examples:
28 *
29 * math.median(5, 2, 7) // returns 5
30 * math.median([3, -1, 5, 7]) // returns 4
31 *
32 * See also:
33 *
34 * mean, min, max, sum, prod, std, var, quantileSeq
35 *
36 * @param {... *} args A single matrix or or multiple scalar values
37 * @return {*} The median
38 */
39 const median = typed('median', {
40 // median([a, b, c, d, ...])
41 'Array | Matrix': _median,
42
43 // median([a, b, c, d, ...], dim)
44 'Array | Matrix, number | BigNumber': function (array, dim) {
45 // TODO: implement median(A, dim)
46 throw new Error('median(A, dim) is not yet supported')
47 // return reduce(arguments[0], arguments[1], ...)
48 },
49
50 // median(a, b, c, d, ...)
51 '...': function (args) {
52 if (containsCollections(args)) {
53 throw new TypeError('Scalar values expected in function median')
54 }
55
56 return _median(args)
57 }
58 })
59
60 /**
61 * Recursively calculate the median of an n-dimensional array
62 * @param {Array} array
63 * @return {Number} median
64 * @private
65 */
66 function _median (array) {
67 try {
68 array = flatten(array.valueOf())
69
70 const num = array.length
71 if (num === 0) {
72 throw new Error('Cannot calculate median of an empty array')
73 }
74
75 if (num % 2 === 0) {
76 // even: return the average of the two middle values
77 const mid = num / 2 - 1
78 const right = partitionSelect(array, mid + 1)
79
80 // array now partitioned at mid + 1, take max of left part
81 let left = array[mid]
82 for (let i = 0; i < mid; ++i) {
83 if (compare(array[i], left) > 0) {
84 left = array[i]
85 }
86 }
87
88 return middle2(left, right)
89 } else {
90 // odd: return the middle value
91 const m = partitionSelect(array, (num - 1) / 2)
92
93 return middle(m)
94 }
95 } catch (err) {
96 throw improveErrorMessage(err, 'median')
97 }
98 }
99
100 // helper function to type check the middle value of the array
101 const middle = typed({
102 'number | BigNumber | Complex | Unit': function (value) {
103 return value
104 }
105 })
106
107 // helper function to type check the two middle value of the array
108 const middle2 = typed({
109 'number | BigNumber | Complex | Unit, number | BigNumber | Complex | Unit': function (left, right) {
110 return divide(add(left, right), 2)
111 }
112 })
113
114 median.toTex = undefined // use default template
115
116 return median
117}
118
119exports.name = 'median'
120exports.factory = factory