simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

quantileSeq.js (8615B)


      1 import { isBigNumber, isCollection, isNumber } from '../../utils/is.js';
      2 import { isInteger } from '../../utils/number.js';
      3 import { flatten } from '../../utils/array.js';
      4 import { factory } from '../../utils/factory.js';
      5 var name = 'quantileSeq';
      6 var dependencies = ['typed', 'add', 'multiply', 'partitionSelect', 'compare'];
      7 export var createQuantileSeq = /* #__PURE__ */factory(name, dependencies, _ref => {
      8   var {
      9     typed,
     10     add,
     11     multiply,
     12     partitionSelect,
     13     compare
     14   } = _ref;
     15 
     16   /**
     17    * Compute the prob order quantile of a matrix or a list with values.
     18    * The sequence is sorted and the middle value is returned.
     19    * Supported types of sequence values are: Number, BigNumber, Unit
     20    * Supported types of probability are: Number, BigNumber
     21    *
     22    * In case of a (multi dimensional) array or matrix, the prob order quantile
     23    * of all elements will be calculated.
     24    *
     25    * Syntax:
     26    *
     27    *     math.quantileSeq(A, prob[, sorted])
     28    *     math.quantileSeq(A, [prob1, prob2, ...][, sorted])
     29    *     math.quantileSeq(A, N[, sorted])
     30    *
     31    * Examples:
     32    *
     33    *     math.quantileSeq([3, -1, 5, 7], 0.5)         // returns 4
     34    *     math.quantileSeq([3, -1, 5, 7], [1/3, 2/3])  // returns [3, 5]
     35    *     math.quantileSeq([3, -1, 5, 7], 2)           // returns [3, 5]
     36    *     math.quantileSeq([-1, 3, 5, 7], 0.5, true)   // returns 4
     37    *
     38    * See also:
     39    *
     40    *     median, mean, min, max, sum, prod, std, variance
     41    *
     42    * @param {Array, Matrix} data                A single matrix or Array
     43    * @param {Number, BigNumber, Array} probOrN  prob is the order of the quantile, while N is
     44    *                                            the amount of evenly distributed steps of
     45    *                                            probabilities; only one of these options can
     46    *                                            be provided
     47    * @param {Boolean} sorted=false              is data sorted in ascending order
     48    * @return {Number, BigNumber, Unit, Array}   Quantile(s)
     49    */
     50   function quantileSeq(data, probOrN, sorted) {
     51     var probArr, dataArr, one;
     52 
     53     if (arguments.length < 2 || arguments.length > 3) {
     54       throw new SyntaxError('Function quantileSeq requires two or three parameters');
     55     }
     56 
     57     if (isCollection(data)) {
     58       sorted = sorted || false;
     59 
     60       if (typeof sorted === 'boolean') {
     61         dataArr = data.valueOf();
     62 
     63         if (isNumber(probOrN)) {
     64           if (probOrN < 0) {
     65             throw new Error('N/prob must be non-negative');
     66           }
     67 
     68           if (probOrN <= 1) {
     69             // quantileSeq([a, b, c, d, ...], prob[,sorted])
     70             return _quantileSeq(dataArr, probOrN, sorted);
     71           }
     72 
     73           if (probOrN > 1) {
     74             // quantileSeq([a, b, c, d, ...], N[,sorted])
     75             if (!isInteger(probOrN)) {
     76               throw new Error('N must be a positive integer');
     77             }
     78 
     79             var nPlusOne = probOrN + 1;
     80             probArr = new Array(probOrN);
     81 
     82             for (var i = 0; i < probOrN;) {
     83               probArr[i] = _quantileSeq(dataArr, ++i / nPlusOne, sorted);
     84             }
     85 
     86             return probArr;
     87           }
     88         }
     89 
     90         if (isBigNumber(probOrN)) {
     91           var BigNumber = probOrN.constructor;
     92 
     93           if (probOrN.isNegative()) {
     94             throw new Error('N/prob must be non-negative');
     95           }
     96 
     97           one = new BigNumber(1);
     98 
     99           if (probOrN.lte(one)) {
    100             // quantileSeq([a, b, c, d, ...], prob[,sorted])
    101             return new BigNumber(_quantileSeq(dataArr, probOrN, sorted));
    102           }
    103 
    104           if (probOrN.gt(one)) {
    105             // quantileSeq([a, b, c, d, ...], N[,sorted])
    106             if (!probOrN.isInteger()) {
    107               throw new Error('N must be a positive integer');
    108             } // largest possible Array length is 2^32-1
    109             // 2^32 < 10^15, thus safe conversion guaranteed
    110 
    111 
    112             var intN = probOrN.toNumber();
    113 
    114             if (intN > 4294967295) {
    115               throw new Error('N must be less than or equal to 2^32-1, as that is the maximum length of an Array');
    116             }
    117 
    118             var _nPlusOne = new BigNumber(intN + 1);
    119 
    120             probArr = new Array(intN);
    121 
    122             for (var _i = 0; _i < intN;) {
    123               probArr[_i] = new BigNumber(_quantileSeq(dataArr, new BigNumber(++_i).div(_nPlusOne), sorted));
    124             }
    125 
    126             return probArr;
    127           }
    128         }
    129 
    130         if (Array.isArray(probOrN)) {
    131           // quantileSeq([a, b, c, d, ...], [prob1, prob2, ...][,sorted])
    132           probArr = new Array(probOrN.length);
    133 
    134           for (var _i2 = 0; _i2 < probArr.length; ++_i2) {
    135             var currProb = probOrN[_i2];
    136 
    137             if (isNumber(currProb)) {
    138               if (currProb < 0 || currProb > 1) {
    139                 throw new Error('Probability must be between 0 and 1, inclusive');
    140               }
    141             } else if (isBigNumber(currProb)) {
    142               one = new currProb.constructor(1);
    143 
    144               if (currProb.isNegative() || currProb.gt(one)) {
    145                 throw new Error('Probability must be between 0 and 1, inclusive');
    146               }
    147             } else {
    148               throw new TypeError('Unexpected type of argument in function quantileSeq'); // FIXME: becomes redundant when converted to typed-function
    149             }
    150 
    151             probArr[_i2] = _quantileSeq(dataArr, currProb, sorted);
    152           }
    153 
    154           return probArr;
    155         }
    156 
    157         throw new TypeError('Unexpected type of argument in function quantileSeq'); // FIXME: becomes redundant when converted to typed-function
    158       }
    159 
    160       throw new TypeError('Unexpected type of argument in function quantileSeq'); // FIXME: becomes redundant when converted to typed-function
    161     }
    162 
    163     throw new TypeError('Unexpected type of argument in function quantileSeq'); // FIXME: becomes redundant when converted to typed-function
    164   }
    165   /**
    166    * Calculate the prob order quantile of an n-dimensional array.
    167    *
    168    * @param {Array} array
    169    * @param {Number, BigNumber} prob
    170    * @param {Boolean} sorted
    171    * @return {Number, BigNumber, Unit} prob order quantile
    172    * @private
    173    */
    174 
    175 
    176   function _quantileSeq(array, prob, sorted) {
    177     var flat = flatten(array);
    178     var len = flat.length;
    179 
    180     if (len === 0) {
    181       throw new Error('Cannot calculate quantile of an empty sequence');
    182     }
    183 
    184     if (isNumber(prob)) {
    185       var _index = prob * (len - 1);
    186 
    187       var _fracPart = _index % 1;
    188 
    189       if (_fracPart === 0) {
    190         var value = sorted ? flat[_index] : partitionSelect(flat, _index);
    191         validate(value);
    192         return value;
    193       }
    194 
    195       var _integerPart = Math.floor(_index);
    196 
    197       var _left;
    198 
    199       var _right;
    200 
    201       if (sorted) {
    202         _left = flat[_integerPart];
    203         _right = flat[_integerPart + 1];
    204       } else {
    205         _right = partitionSelect(flat, _integerPart + 1); // max of partition is kth largest
    206 
    207         _left = flat[_integerPart];
    208 
    209         for (var i = 0; i < _integerPart; ++i) {
    210           if (compare(flat[i], _left) > 0) {
    211             _left = flat[i];
    212           }
    213         }
    214       }
    215 
    216       validate(_left);
    217       validate(_right); // Q(prob) = (1-f)*A[floor(index)] + f*A[floor(index)+1]
    218 
    219       return add(multiply(_left, 1 - _fracPart), multiply(_right, _fracPart));
    220     } // If prob is a BigNumber
    221 
    222 
    223     var index = prob.times(len - 1);
    224 
    225     if (index.isInteger()) {
    226       index = index.toNumber();
    227 
    228       var _value = sorted ? flat[index] : partitionSelect(flat, index);
    229 
    230       validate(_value);
    231       return _value;
    232     }
    233 
    234     var integerPart = index.floor();
    235     var fracPart = index.minus(integerPart);
    236     var integerPartNumber = integerPart.toNumber();
    237     var left;
    238     var right;
    239 
    240     if (sorted) {
    241       left = flat[integerPartNumber];
    242       right = flat[integerPartNumber + 1];
    243     } else {
    244       right = partitionSelect(flat, integerPartNumber + 1); // max of partition is kth largest
    245 
    246       left = flat[integerPartNumber];
    247 
    248       for (var _i3 = 0; _i3 < integerPartNumber; ++_i3) {
    249         if (compare(flat[_i3], left) > 0) {
    250           left = flat[_i3];
    251         }
    252       }
    253     }
    254 
    255     validate(left);
    256     validate(right); // Q(prob) = (1-f)*A[floor(index)] + f*A[floor(index)+1]
    257 
    258     var one = new fracPart.constructor(1);
    259     return add(multiply(left, one.minus(fracPart)), multiply(right, fracPart));
    260   }
    261   /**
    262    * Check if array value types are valid, throw error otherwise.
    263    * @param {number | BigNumber | Unit} x
    264    * @param {number | BigNumber | Unit} x
    265    * @private
    266    */
    267 
    268 
    269   var validate = typed({
    270     'number | BigNumber | Unit': function numberBigNumberUnit(x) {
    271       return x;
    272     }
    273   });
    274   return quantileSeq;
    275 });