UNPKG

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