diff --git a/src/expression/transform/quantileSeq.transform.js b/src/expression/transform/quantileSeq.transform.js new file mode 100644 index 0000000000..aa83d759fa --- /dev/null +++ b/src/expression/transform/quantileSeq.transform.js @@ -0,0 +1,27 @@ +import { factory } from '../../utils/factory.js' +import { createQuantileSeq } from '../../function/statistics/quantileSeq.js' +import { lastDimToZeroBase } from './utils/lastDimToZeroBase.js' + +const name = 'quantileSeq' +const dependencies = ['typed', 'add', 'multiply', 'partitionSelect', 'compare', 'isInteger'] + +/** + * Attach a transform function to math.quantileSeq + * Adds a property transform containing the transform function. + * + * This transform changed the `dim` parameter of function std + * from one-based to zero based + */ +export const createQuantileSeqTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, add, multiply, partitionSelect, compare, isInteger }) => { + const quantileSeq = createQuantileSeq({ typed, add, multiply, partitionSelect, compare, isInteger }) + + return typed('quantileSeq', { + 'Array|Matrix, number|BigNumber|Array, number': (arr, prob, dim) => quantileSeq(arr, prob, dimToZeroBase(dim)), + 'Array|Matrix, number|BigNumber|Array, boolean, number': (arr, prob, sorted, dim) => quantileSeq(arr, prob, sorted, dimToZeroBase(dim)) + }) + + function dimToZeroBase (dim) { + // TODO: find a better way, maybe lastDimToZeroBase could apply to more cases. + return lastDimToZeroBase([[], dim])[1] + } +}, { isTransformFunction: true }) diff --git a/src/factoriesAny.js b/src/factoriesAny.js index 704c25c904..00b5fe195c 100644 --- a/src/factoriesAny.js +++ b/src/factoriesAny.js @@ -352,5 +352,6 @@ export { createConcatTransform } from './expression/transform/concat.transform.j export { createDiffTransform } from './expression/transform/diff.transform.js' export { createStdTransform } from './expression/transform/std.transform.js' export { createSumTransform } from './expression/transform/sum.transform.js' +export { createQuantileSeqTransform } from './expression/transform/quantileSeq.transform.js' export { createCumSumTransform } from './expression/transform/cumsum.transform.js' export { createVarianceTransform } from './expression/transform/variance.transform.js' diff --git a/src/function/statistics/quantileSeq.js b/src/function/statistics/quantileSeq.js index dbf8a351dd..6d772c8fbc 100644 --- a/src/function/statistics/quantileSeq.js +++ b/src/function/statistics/quantileSeq.js @@ -1,12 +1,12 @@ import { isBigNumber, isCollection, isNumber } from '../../utils/is.js' -import { isInteger } from '../../utils/number.js' import { flatten } from '../../utils/array.js' import { factory } from '../../utils/factory.js' +import { createApply } from '../matrix/apply.js' const name = 'quantileSeq' -const dependencies = ['typed', 'add', 'multiply', 'partitionSelect', 'compare'] +const dependencies = ['typed', 'add', 'multiply', 'partitionSelect', 'compare', 'isInteger'] -export const createQuantileSeq = /* #__PURE__ */ factory(name, dependencies, ({ typed, add, multiply, partitionSelect, compare }) => { +export const createQuantileSeq = /* #__PURE__ */ factory(name, dependencies, ({ typed, add, multiply, partitionSelect, compare, isInteger }) => { /** * Compute the prob order quantile of a matrix or a list with values. * The sequence is sorted and the middle value is returned. @@ -41,6 +41,32 @@ export const createQuantileSeq = /* #__PURE__ */ factory(name, dependencies, ({ * @param {Boolean} sorted=false is data sorted in ascending order * @return {Number, BigNumber, Unit, Array} Quantile(s) */ + + const apply = createApply({ typed, isInteger }) + /** + * Check if array value types are valid, throw error otherwise. + * @param {number | BigNumber | Unit} x + * @param {number | BigNumber | Unit} x + * @private + */ + const validate = typed({ + 'number | BigNumber | Unit': function (x) { + return x + } + }) + + return typed(name, { + 'Array|Matrix, number|BigNumber|Array': (data, prob) => quantileSeq(data, prob, false), + 'Array|Matrix, number|BigNumber|Array, boolean': quantileSeq, + 'Array|Matrix, number|BigNumber|Array, number': (data, prob, dim) => _quantileSeqDim(data, prob, false, dim), + 'Array|Matrix, number|BigNumber|Array, boolean, number': (data, prob, sorted, dim) => _quantileSeqDim(data, prob, sorted, dim) + }) + + function _quantileSeqDim (data, prob, sorted, dim) { + // return [1.3, 1.2] + return apply(data, dim, x => quantileSeq(x, prob, sorted)) + } + function quantileSeq (data, probOrN, sorted) { let probArr, dataArr, one @@ -238,18 +264,4 @@ export const createQuantileSeq = /* #__PURE__ */ factory(name, dependencies, ({ const one = new fracPart.constructor(1) return add(multiply(left, one.minus(fracPart)), multiply(right, fracPart)) } - - /** - * Check if array value types are valid, throw error otherwise. - * @param {number | BigNumber | Unit} x - * @param {number | BigNumber | Unit} x - * @private - */ - const validate = typed({ - 'number | BigNumber | Unit': function (x) { - return x - } - }) - - return quantileSeq }) diff --git a/test/unit-tests/function/statistics/quantileSeq.test.js b/test/unit-tests/function/statistics/quantileSeq.test.js index 65872cbd5e..f64680e6e0 100644 --- a/test/unit-tests/function/statistics/quantileSeq.test.js +++ b/test/unit-tests/function/statistics/quantileSeq.test.js @@ -23,6 +23,40 @@ describe('quantileSeq', function () { assert.strictEqual(quantileSeq(lst, 1), 3.7) }) + it('should return the quantileSeq from a multidimensional array in the specified dimension', function () { + const arr = [ + [3.7, 2.7, 3.3, 1.3, 2.2, 3.1], + [3.8, 2.5, 3.2, 1.2, 3.2, 4.1] + ] + assert.deepStrictEqual(quantileSeq(arr, 0, 1), [1.3, 1.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.25, 1), [2.325, 2.675]) + assert.deepStrictEqual(quantileSeq(arr, 0.5, 1), [2.9000000000000004, 3.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.75, 1), [3.2499999999999996, 3.6499999999999995]) + assert.deepStrictEqual(quantileSeq(arr, 1, 1), [3.7, 4.1]) + assert.deepStrictEqual(quantileSeq(arr, 0, false, 1), [1.3, 1.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.25, false, 1), [2.325, 2.675]) + assert.deepStrictEqual(quantileSeq(arr, 0.5, false, 1), [2.9000000000000004, 3.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.75, false, 1), [3.2499999999999996, 3.6499999999999995]) + assert.deepStrictEqual(quantileSeq(arr, 1, false, 1), [3.7, 4.1]) + }) + + it('should return the quantileSeq from a multidimensional array in the specified dimension in the parser', function () { + const arr = [ + [3.7, 2.7, 3.3, 1.3, 2.2, 3.1], + [3.8, 2.5, 3.2, 1.2, 3.2, 4.1] + ] + assert.deepStrictEqual(math.evaluate('quantileSeq(arr, 0, 2)', { arr }), [1.3, 1.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.25, 1), [2.325, 2.675]) + assert.deepStrictEqual(quantileSeq(arr, 0.5, 1), [2.9000000000000004, 3.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.75, 1), [3.2499999999999996, 3.6499999999999995]) + assert.deepStrictEqual(quantileSeq(arr, 1, 1), [3.7, 4.1]) + assert.deepStrictEqual(quantileSeq(arr, 0, false, 1), [1.3, 1.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.25, false, 1), [2.325, 2.675]) + assert.deepStrictEqual(quantileSeq(arr, 0.5, false, 1), [2.9000000000000004, 3.2]) + assert.deepStrictEqual(quantileSeq(arr, 0.75, false, 1), [3.2499999999999996, 3.6499999999999995]) + assert.deepStrictEqual(quantileSeq(arr, 1, false, 1), [3.7, 4.1]) + }) + it('should return the quantileSeq from an ascending array with number probability', function () { const lst = [1.3, 2.2, 2.7, 3.1, 3.3, 3.7] assert.strictEqual(quantileSeq(lst, 0, true), 1.3) @@ -78,7 +112,7 @@ describe('quantileSeq', function () { assert.deepStrictEqual(quantileSeq([math.unit('5mm'), math.unit('15mm'), math.unit('10mm')], 0.5), math.unit('10mm')) }) - it('should return the quantileSeq from an 1d matrix', function () { + it('should return the quantileSeq from a 1d matrix', function () { assert.strictEqual(quantileSeq(math.matrix([2, 4, 6, 8, 10, 12, 14]), 0.25), 5) }) @@ -151,9 +185,9 @@ describe('quantileSeq', function () { }) it('should throw an error if called with invalid number of arguments', function () { - assert.throws(function () { quantileSeq() }, SyntaxError) - assert.throws(function () { quantileSeq(2) }, SyntaxError) - assert.throws(function () { quantileSeq([], 2, 3, 1) }, SyntaxError) + assert.throws(function () { quantileSeq() }, TypeError) + assert.throws(function () { quantileSeq(2) }, TypeError) + assert.throws(function () { quantileSeq([], 2, 3, 1) }, TypeError) }) it('should throw an error if called with unsupported type of arguments', function () {