UNPKG

@jrc03c/js-math-tools

Version:
1,692 lines (1,646 loc) 145 kB
(() => { var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/index.mjs var index_exports = {}; __export(index_exports, { DataFrame: () => DataFrame, IndexMatcher: () => IndexMatcher, MathError: () => MathError, Series: () => Series, abs: () => vabs, add: () => vadd, apply: () => vapply, arccos: () => varccos, arcsin: () => varcsin, arctan: () => varctan, argmax: () => argmax, argmin: () => argmin, assert: () => assert, cast: () => cast, ceil: () => vceil, chop: () => vchop, clamp: () => vclamp, combinations: () => combinations, combinationsIterator: () => combinationsIterator, copy: () => copy, correl: () => correl, cos: () => vcos, count: () => count, covariance: () => covariance, dataTypes: () => dataTypes, decycle: () => decycle, diff: () => diff, distance: () => distance, divide: () => divide, dot: () => dot, dropMissing: () => dropMissing, dropMissingPairwise: () => dropMissingPairwise, dropNaN: () => dropNaN, dropNaNPairwise: () => dropNaNPairwise, dropUndefined: () => dropUndefined, every: () => every, exp: () => vexp, factorial: () => vfactorial, filter: () => filter, find: () => find, findAll: () => findAll, flatten: () => flatten, float: () => vfloat, floor: () => vfloor, forEach: () => forEach, identity: () => identity, indexOf: () => indexOf, inferType: () => inferType, int: () => vint, intersect: () => intersect, inverse: () => inverse, isArray: () => isArray, isBoolean: () => isBoolean, isBrowser: () => isBrowser, isDataFrame: () => isDataFrame, isDate: () => isDate, isEqual: () => isEqual, isFunction: () => isFunction, isJagged: () => isJagged, isNested: () => isNested, isNumber: () => isNumber, isObject: () => isObject, isSeries: () => isSeries, isString: () => isString, isUndefined: () => isUndefined, lerp: () => vlerp, log: () => vlog, map: () => map, max: () => max, mean: () => mean, median: () => median, merge: () => merge, min: () => min, mod: () => vmod, mode: () => mode, multiply: () => vmultiply, ndarray: () => ndarray, normal: () => normal, ones: () => ones, permutations: () => permutations, permutationsIterator: () => permutationsIterator, pow: () => vpow, print: () => print, product: () => product, random: () => random, range: () => range, reduce: () => reduce, remap: () => remap, reshape: () => reshape, reverse: () => reverse, round: () => vround, scale: () => scale, seed: () => seed, set: () => set, shape: () => shape, shuffle: () => shuffle, sign: () => vsign, sin: () => vsin, some: () => some, sort: () => sort, sqrt: () => vsqrt, stats: () => stats, std: () => std, stdev: () => stdev, subtract: () => subtract, sum: () => sum, tan: () => vtan, time: () => time, transpose: () => transpose, union: () => union, variance: () => variance, vectorize: () => vectorize, zeros: () => zeros, zip: () => zip }); // src/is-number.mjs function isNumber(x) { return typeof x === "number" && !isNaN(x) || typeof x === "bigint"; } // src/is-browser.mjs var isBrowser = new Function( ` try { return this === window } catch(e) {} try { return !!importScripts } catch(e){} return false ` ); // src/math-error.mjs var MathError = class extends Error { constructor(message) { if (isBrowser()) { super(message); } else { super("\n\n\x1B[31m" + message + "\n\x1B[0m"); } } }; // src/assert.mjs function assert(isTrue, message) { if (!isTrue) throw new MathError(message); } // src/for-each.mjs function forEach(x, fn) { for (let i = 0; i < x.length; i++) { fn(x[i], i, x); } } // src/helpers/array-types.mjs var arrayTypes = [ Array, ArrayBuffer, BigInt64Array, BigUint64Array, Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray ]; // src/is-undefined.mjs function isUndefined(x) { return x === null || typeof x === "undefined"; } // src/map.mjs function map(x, fn) { const out = new Array(x.length); for (let i = 0; i < x.length; i++) { out[i] = fn(x[i], i, x); } return out; } // src/is-array.mjs var typeStrings = map(arrayTypes, (s2) => s2.name); function isArray(obj) { try { if (obj instanceof Array) { return true; } if (!isUndefined(obj.constructor)) { return arrayTypes.indexOf(obj.constructor) > -1 || typeStrings.indexOf(obj.constructor.name) > -1; } return false; } catch (e) { return false; } } // src/is-dataframe.mjs function isDataFrame(x) { try { return !!x._symbol && x._symbol === Symbol.for("@jrc03c/js-math-tools/dataframe"); } catch (e) { return false; } } // src/is-function.mjs function isFunction(fn) { return typeof fn === "function"; } // src/is-object.mjs function isObject(x) { return typeof x === "object" && !isUndefined(x) && !isArray(x); } // src/is-series.mjs function isSeries(x) { try { return !!x._symbol && x._symbol === Symbol.for("@jrc03c/js-math-tools/series"); } catch (e) { return false; } } // src/index-of.mjs function indexOf(x, fn) { if (isDataFrame(x)) { const index = indexOf(x.values, fn); if (index.length > 0 && isNumber(index[0]) && index[0] >= 0 && index[0] < x.index.length) { index[0] = x.index[index[0]]; } if (index.length > 1 && isNumber(index[1]) && index[1] >= 0 && index[1] < x.columns.length) { index[1] = x.columns[index[1]]; } return index; } if (isSeries(x)) { const index = indexOf(x.values, fn); if (index.length > 0 && isNumber(index[0]) && index[0] >= 0 && index[0] < x.index.length) { index[0] = x.index[index[0]]; } return index; } assert( isObject(x) || isArray(x), "You must pass (1) an object, array, Series, or DataFrame and (2) a function or value into the `indexOf` function!" ); if (!isFunction(fn)) { const value = fn; fn = (v) => v === value; } function helper5(x2, fn2, checked) { checked = checked || []; if (checked.indexOf(x2) > -1) { return null; } if (isObject(x2)) { checked.push(x2); const keys = Object.keys(x2).concat(Object.getOwnPropertySymbols(x2)); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = x2[key]; if (fn2(value)) { return [key]; } const results = helper5(value, fn2, checked); if (results && results.length > 0) { return [key].concat(results); } } } else if (isArray(x2)) { checked.push(x2); for (let i = 0; i < x2.length; i++) { const value = x2[i]; if (fn2(value)) { return [i]; } const results = helper5(value, fn2, checked); if (results && results.length > 0) { return [i].concat(results); } } } else { if (fn2(x2)) { return []; } } return null; } function safeFn(v) { try { return fn(v); } catch (e) { return false; } } const paths = helper5(x, safeFn); if (paths && paths.length > 0) { return paths; } else { return null; } } // src/copy.mjs function copy(x) { function helper5(x2) { if (typeof x2 === "object") { if (x2 === null) { return null; } if (isArray(x2)) { if (!(x2 instanceof Array)) { return x2.slice(); } return map(x2, (v) => copy(v)); } if (isSeries(x2)) { const out2 = x2.copy(); out2.values = copy(out2.values); return out2; } if (isDataFrame(x2)) { const out2 = x2.copy(); out2.values = copy(x2.values); return out2; } if (x2 instanceof Date) { return new Date(x2.getTime()); } x2 = decycle(x2); const out = {}; forEach( Object.keys(x2).concat(Object.getOwnPropertySymbols(x2)), (key) => { out[key] = copy(x2[key]); } ); return out; } else { return x2; } } return helper5(decycle(x)); } function decycle(x) { function helper5(x2, checked, currentPath) { checked = checked || []; currentPath = currentPath || ""; if (checked.indexOf(x2) > -1) { const parts = currentPath.split("/").slice(currentPath.startsWith("/") ? 1 : 0); const isANestedCopy = parts.some((v, i) => { const subParts = parts.slice(0, parts.length - i - 1); let temp = orig; forEach(subParts, (part) => { temp = temp[part]; }); return temp === x2; }); if (isANestedCopy) { const pathToCopy = orig === x2 ? "/" : "/" + indexOf(orig, x2).join("/"); return `<reference to "${pathToCopy}">`; } } if (typeof x2 === "object") { if (x2 === null) return null; checked.push(x2); if (isArray(x2)) { if (typeof x2.constructor !== "undefined" && x2.constructor.name !== "Array") { return x2.slice(); } return map(x2, (v, i) => helper5(v, checked, currentPath + "/" + i)); } else { forEach( Object.keys(x2).concat(Object.getOwnPropertySymbols(x2)), (key) => { x2[key] = helper5(x2[key], checked, currentPath + "/" + key.toString()); } ); return x2; } } else { return x2; } } const orig = x; let out = helper5(orig); if (isDataFrame(x)) { const temp = x.copy(); temp._values = out.values; temp._columns = out.columns; temp._index = out.index; out = temp; } if (isSeries(x)) { const temp = x.copy(); temp.name = out.name; temp._values = out.values; temp._index = out.index; out = temp; } return out; } // src/is-date.mjs function isDate(x) { return x instanceof Date && x.toString() !== "Invalid Date"; } // src/is-equal.mjs var numberTypes = ["number", "int", "float", "bigint"]; function isEqual(a, b) { function helper5(a2, b2) { const aType = typeof a2; const bType = typeof b2; if (aType !== bType && !numberTypes.includes(aType) && !numberTypes.includes(bType)) return false; if (aType === "undefined" && bType === "undefined") return true; if (aType === "boolean") return a2 === b2; if (aType === "symbol") return a2 === b2; if (aType === "number" || aType === "bigint") { try { const aString = a2.toString(); const bString = b2.toString(); return aString === bString; } catch (e) { return false; } } if (aType === "string") return a2 === b2; if (aType === "function") return a2 === b2; if (aType === "object") { if (a2 === null || b2 === null) { return a2 === null && b2 === null; } else { if (isDate(a2)) { if (isDate(b2)) { return a2.getTime() === b2.getTime(); } else { return false; } } else if (isDate(b2)) { return false; } if (a2 instanceof RegExp && b2 instanceof RegExp) { return a2.toString() === b2.toString(); } if (isArray(a2) !== isArray(b2)) { return false; } const aKeys = Object.keys(a2).concat(Object.getOwnPropertySymbols(a2)); const bKeys = Object.keys(b2).concat(Object.getOwnPropertySymbols(b2)); if (aKeys.length !== bKeys.length) return false; for (let i = 0; i < aKeys.length; i++) { const key = aKeys[i]; if (!helper5(a2[key], b2[key])) return false; } return true; } } } try { return helper5(a, b); } catch (e) { return helper5(decycle(a), decycle(b)); } } // src/helpers/counter.mjs function makeKey(n) { const alpha = "abcdefg1234567890"; let out = ""; while (out.length < n) out += alpha[Math.floor(Math.random() * alpha.length)]; return out; } var NULL_KEY = makeKey(16); var UNDEFINED_KEY = makeKey(16); var INFINITY_KEY = makeKey(16); var MINUS_INFINITY_KEY = makeKey(16); var SYMBOL_KEY = makeKey(16); var Counter = class { constructor() { this.clear(); } get counts() { return map(this.values, (v) => this.get(v)); } get values() { return Object.values(this.valuesDict); } clear() { this.countsDict = {}; this.valuesDict = {}; return this; } count(x) { for (const v of x) { if (isArray(v)) { this.count(v); } else { this.increment(v); } } return this; } delete(value) { const key = this.getStandardizedKey(value); delete this.countsDict[key]; delete this.valuesDict[key]; return this; } get(value) { return this.countsDict[this.getStandardizedKey(value)] || 0; } getStandardizedKey(value) { return typeof value === "object" && value === null ? NULL_KEY : isUndefined(value) ? UNDEFINED_KEY : isFunction(value) ? value.toString() : typeof value === "symbol" ? value.toString() + " - " + SYMBOL_KEY : value === Infinity ? INFINITY_KEY : value === -Infinity ? MINUS_INFINITY_KEY : typeof value === "bigint" ? value.toString() : isDataFrame(value) ? value.toJSONString() : isSeries(value) ? JSON.stringify(value.toObject()) : JSON.stringify(value); } has(value) { return !isUndefined(this.countsDict[this.getStandardizedKey(value)]); } increment(value) { return this.set(value, this.get(value) + 1); } set(value, count2) { const key = this.getStandardizedKey(value); this.countsDict[key] = count2; this.valuesDict[key] = value; return this; } toArray() { return map(this.values, (v) => ({ value: v, count: this.get(v) })); } toObject() { const out = {}; forEach(this.values, (value) => { out[value] = this.get(value); }); return out; } }; // src/flatten.mjs function flatten(arr) { if (isDataFrame(arr) || isSeries(arr)) { return flatten(arr.values); } assert( isArray(arr), "The `flatten` function only works on arrays, Series, and DataFrames!" ); function helper5(arr2) { let out = []; forEach(arr2, (child) => { if (isArray(child)) { out = out.concat(helper5(child)); } else { out.push(child); } }); return out; } return helper5(arr); } // src/stats.mjs function stats(x, options) { options = options || {}; const counts = new Counter(); const out = {}; const xflat = flatten(x); const xnums = []; let max2 = -Infinity; let min2 = Infinity; let resultsShouldIncludeBigInts = false; let sum2 = 0; for (const v of xflat) { if (typeof v === "bigint") { resultsShouldIncludeBigInts = true; } if (!options.shouldDropNaNs || isNumber(v)) { try { if (v > max2) { max2 = v; } if (v < min2) { min2 = v; } sum2 += Number(v); xnums.push(v); } catch (e) { max2 = NaN; min2 = NaN; sum2 = NaN; } } counts.increment(v); } const mean2 = sum2 / xnums.length; out.counts = counts; out.max = max2; out.mean = mean2; out.min = min2; out.n = xflat.length; out.sum = sum2; if (isNaN(out.mean)) { out.max = NaN; out.min = NaN; } if (options.shouldDropNaNs) { out.nWithoutNaNs = xnums.length; } if (options.mode) { const sortedCountPairs = Array.from( map(counts.values, (v) => [v, counts.get(v)]) ).toSorted((a, b) => b[1] - a[1]); const highestCount = sortedCountPairs[0][1]; const mode2 = []; for (const pair of sortedCountPairs) { if (pair[1] == highestCount) { mode2.push(pair[0]); } else { break; } } out.mode = mode2.toSorted(); } if (options.median) { if (isNaN(mean2)) { out.median = NaN; } else { const xnumsSorted = xnums.toSorted((a, b) => Number(a) - Number(b)); const middle = Math.floor(xnumsSorted.length / 2); if (xnumsSorted.length % 2 === 0) { const left = xnumsSorted[middle - 1]; const right = xnumsSorted[middle]; out.median = (Number(left) + Number(right)) / 2; if (resultsShouldIncludeBigInts && typeof left === "bigint" && typeof right === "bigint") { try { out.median = BigInt(out.median); } catch (e) { } } } else { out.median = xnumsSorted[middle]; } } } if (options.stdev || options.variance) { let variance2 = 0; for (const v of xnums) { variance2 += Math.pow(Number(v) - mean2, 2); } variance2 /= xnums.length; const stdev2 = Math.sqrt(variance2); out.stdev = stdev2; out.variance = variance2; } if (resultsShouldIncludeBigInts) { try { out.sum = BigInt(out.sum); } catch (e) { } try { out.mean = BigInt(out.mean); } catch (e) { } if (options.mode) { out.mode = map(out.mode, (v) => { try { return BigInt(v); } catch (e) { return v; } }); } } return out; } // src/count.mjs function count(arr, matcher) { const { counts } = stats(arr); if (!isUndefined(matcher)) { if (isFunction(matcher)) { forEach(counts.values, (v) => { if (!matcher(v)) { counts.delete(v); } }); } else { forEach(counts.values, (v) => { if (!isEqual(v, matcher)) { counts.delete(v); } }); } } return counts; } // src/filter.mjs function filter(x, fn) { const out = []; for (let i = 0; i < x.length; i++) { if (fn(x[i], i, x)) { out.push(x[i]); } } return out; } // src/is-jagged.mjs function helper(x) { if (isDataFrame(x) || isSeries(x)) { return helper(x.values); } if (isArray(x)) { let hasArrayValues = false; let hasNonArrayValues = false; let arrayLength = null; for (const v of x) { if (helper(v)) { return true; } if (isArray(v)) { if (arrayLength === null) { arrayLength = v.length; } else if (v.length !== arrayLength) { return true; } hasArrayValues = true; } else { hasNonArrayValues = true; } if (hasArrayValues && hasNonArrayValues) { return true; } } } return false; } function isJagged(x) { return helper(decycle(x)); } // src/is-nested.mjs function isNested(x) { if (isDataFrame(x) || isSeries(x)) { return isNested(x.values); } assert( isArray(x), "The `isNested` function only works on arrays, Series, and DataFrames!" ); for (let i = 0; i < x.length; i++) { if (isArray(x[i])) { return true; } } return false; } // src/ndarray.mjs var error = "You must pass a natural number or a one-dimensional array of natural numbers into the `ndarray` function!"; function ndarray(shape2) { assert(!isUndefined(shape2), error); if (!isArray(shape2)) shape2 = [shape2]; assert(!isNested(shape2), error); assert(shape2.length > 0, error); let s2 = shape2[0]; if (typeof s2 === "bigint") s2 = Number(s2); assert(isNumber(s2), error); assert(s2 >= 0, error); assert(Math.floor(s2) === s2, error); assert( s2 !== Infinity, "We can't create an array containing an infinite number of values!" ); if (shape2.length === 1) { const out = []; for (let i = 0; i < s2; i++) out.push(void 0); return out; } else { const out = []; for (let i = 0; i < s2; i++) { out.push(ndarray(shape2.slice(1))); } return out; } } // src/range.mjs var RangeIterator = class _RangeIterator { static from(data) { return new _RangeIterator(data.a, data.b); } a = 0; b = 0; step = 0; constructor(a, b, step) { this.a = a; this.b = b; this.step = step ?? 1; } get length() { return Math.abs( (Math.max(this.a, this.b) - Math.min(this.a, this.b)) / this.step ); } get pairIterator() { const iterator = this[Symbol.iterator](); function* helper5() { let i = 0; for (const v of iterator) { yield [v, i]; i++; } } return helper5(); } [Symbol.iterator]() { const shouldIncludeBigInts = typeof this.a === "bigint" || typeof this.b === "bigint" || typeof this.step === "bigint"; const a = shouldIncludeBigInts ? BigInt(this.a) : this.a; const b = shouldIncludeBigInts ? BigInt(this.b) : this.b; let step = shouldIncludeBigInts ? BigInt(this.step) : this.step; if (a <= b && step < 0 || a > b && step > 0) { step *= shouldIncludeBigInts ? BigInt(-1) : -1; } function* helper5() { if (a <= b) { for (let i = a; i < b; i += step) { yield i; } } else { for (let i = a; i > b; i += step) { yield i; } } } return helper5(); } drop(limit) { return new _RangeIterator(this.a + limit * this.step, this.b); } every(fn) { for (const pair of this.pairIterator) { if (!fn(...pair)) { return false; } } return true; } filter(fn) { const out = []; for (const pair of this.pairIterator) { if (fn(...pair)) { out.push(pair[0]); } } return out; } find(fn) { for (const pair of this.pairIterator) { if (fn(...pair)) { return pair[0]; } } } flatMap() { throw new Error("The `RangeIterator.flatMap` method has no implementation!"); } forEach(fn) { for (const pair of this.pairIterator) { fn(...pair); } } map(fn) { const out = []; for (const pair of this.pairIterator) { out.push(fn(...pair)); } return out; } reduce(fn, out) { for (const pair of this.pairIterator) { out = fn(pair[0], out, pair[1]); } return out; } some(fn) { for (const pair of this.pairIterator) { if (fn(...pair)) { return true; } } return false; } take(limit) { return new _RangeIterator(this.a, this.a + limit * this.step); } toArray() { const out = []; for (const i of this) { out.push(i); } return out; } }; function range(a, b, step = 1) { assert( !isUndefined(a) && !isUndefined(b) && !isUndefined(step), "You must pass two numbers and optionally a step value to the `range` function!" ); assert( isNumber(a) && isNumber(b) && isNumber(step), "You must pass two numbers and optionally a step value to the `range` function!" ); assert( step !== 0, "The step value must be greater than 0! (NOTE: The step value is a magnitude; it does not indicate direction.)" ); return new RangeIterator(a, b, step); } // src/set.mjs function makeKey2(n) { const alpha = "abcdefg1234567890"; let out = ""; while (out.length < n) out += alpha[Math.floor(Math.random() * alpha.length)]; return out; } var NULL_KEY2 = makeKey2(256); var UNDEFINED_KEY2 = makeKey2(256); var INFINITY_KEY2 = makeKey2(256); var MINUS_INFINITY_KEY2 = makeKey2(256); var SYMBOL_KEY2 = makeKey2(256); function set(arr) { if (isDataFrame(arr) || isSeries(arr)) { return set(arr.values); } assert( isArray(arr), "The `set` function only works on arrays, Series, and DataFrames!" ); const out = []; const temp = {}; forEach(flatten(arr), (item) => { const key = typeof item === "object" && item === null ? NULL_KEY2 : isUndefined(item) ? UNDEFINED_KEY2 : isFunction(item) ? item.toString() : typeof item === "symbol" ? item.toString() + " - " + SYMBOL_KEY2 : item === Infinity ? INFINITY_KEY2 : item === -Infinity ? MINUS_INFINITY_KEY2 : typeof item === "bigint" ? item.toString() : isDataFrame(item) ? item.toJSONString() : isSeries(item) ? JSON.stringify(item.toObject()) : JSON.stringify(item); if (typeof temp[key] === "undefined") { out.push(item); } temp[key] = true; }); return out; } // src/shape.mjs function helper2(x) { if (isArray(x)) { const childShapes = helper2(x[0]); return [x.length].concat(childShapes || []); } else { return void 0; } } function shape(x) { if (isDataFrame(x) || isSeries(x)) { return shape(x.values); } assert( isArray(x), "The `shape` function only works on arrays, Series, and DataFrames!" ); return helper2(x); } // src/dataframe/df-append.mjs function dfAppend(df, x, axis) { if (isUndefined(axis)) { axis = 0; } assert( axis === 0 || axis === 1 || axis === "vertical" || axis === "horizontal", 'The only valid axis values for use when appending data to a DataFrame are 0, 1, "vertical", and "horizontal". Note that 0 == "horizontal" and 1 == "vertical".' ); if (isArray(x)) { assert( !isJagged(x), "The array of data you're trying to append to this DataFrame is jagged!" ); const xShape = shape(x); if (xShape.length === 1) { if (axis === 0) { const out = df.copy(); out._values.push(x); const maxRowLength = Math.max(df.shape[1], xShape[0]); forEach(out._values, (row) => { while (row.length < maxRowLength) { row.push(void 0); } }); while (out._index.length < out._values.length) { out._index.push("row" + out._index.length); } while (out._columns.length < maxRowLength) { out._columns.push("col" + out._columns.length); } return out; } else { const maxColLength = Math.max(df.shape[0], xShape[0]); const out = df.copy(); range(0, maxColLength).forEach((i) => { if (i >= out._values.length) { out._values.push(ndarray(df.shape[1])); } out._values[i].push(x[i]); }); while (out._index.length < out._values.length) { out._index.push("row" + out._index.length); } while (out._columns.length < out._values[0].length) { out._columns.push("col" + out._columns.length); } return out; } } else if (xShape.length === 2) { if (axis === 0) { const maxRowLength = Math.max( ...map(x, (row) => row.length).concat([df.shape[1]]) ); const out = df.copy(); out._values = map(out._values.concat(x), (row) => { while (row.length < maxRowLength) { row.push(void 0); } return row; }); while (out._index.length < out._values.length) { out._index.push("row" + out._index.length); } while (out._columns.length < maxRowLength) { out._columns.push("col" + out._columns.length); } return out; } else { const maxRowLength = Math.max(...map(x, (row) => row.length)) + df.shape[1]; const maxColLength = Math.max(df.shape[0], xShape[0]); const out = df.copy(); range(0, maxColLength).forEach((i) => { if (i >= out._values.length) { out._values.push(ndarray(df.shape[1])); } out._values[i] = out._values[i].concat(x[i]); while (out._values[i].length < maxRowLength) { out._values[i].push(void 0); } }); while (out._index.length < out._values.length) { out._index.push("row" + out._index.length); } while (out._columns.length < maxRowLength) { out._columns.push("col" + out._columns.length); } return out; } } else { throw new MathError( "Only 1- and 2-dimensional arrays can be appended to a DataFrame!" ); } } else if (isSeries(x)) { const out = dfAppend(df, x.values, axis); if (axis === 0) { out.index[out.index.length - 1] = out.index.indexOf(x.name) > -1 ? x.name + " (2)" : x.name; } else { out.columns[out.columns.length - 1] = out.columns.indexOf(x.name) > -1 ? x.name + " (2)" : x.name; } return out; } else if (isDataFrame(x)) { if (axis === 0) { const out = df.copy(); const maxRowLength = set(out._columns.concat(x._columns)).length; forEach(out._values, (row) => { while (row.length < maxRowLength) { row.push(void 0); } }); x.apply((row) => { const rowCopy = row.copy(); const temp = []; forEach(out._columns, (col) => { const index = rowCopy._index.indexOf(col); if (index > -1) { temp.push(rowCopy._values[index]); rowCopy._values.splice(index, 1); rowCopy._index.splice(index, 1); } else { temp.push(void 0); } }); out._values.push(temp.concat(rowCopy._values)); }, 1); out._columns = out._columns.concat( filter(x._columns, (c) => out._columns.indexOf(c) < 0) ); while (out._index.length < out._values.length) { const newRowName = "row" + out._index.length; out._index.push( newRowName + (df._index.indexOf(newRowName) > -1 ? " (2)" : "") ); } return out; } else { const out = df.copy(); forEach(out._index, (rowName, i) => { const xIndex = x._index.indexOf(rowName); if (xIndex > -1) { out._values[i] = out._values[i].concat(x._values[xIndex]); } else { out._values[i] = out._values[i].concat(ndarray(x.shape[1])); } }); forEach(x._index, (rowName, i) => { const outIndex = out._index.indexOf(rowName); if (outIndex < 0) { out._index.push(rowName); out._values.push(ndarray(out._columns.length).concat(x._values[i])); } }); out._columns = out._columns.concat( map(x._columns, (c) => c + (out._columns.indexOf(c) > -1 ? " (2)" : "")) ); return out; } } else { throw new MathError( "Only 1- or 2-dimensional arrays, Series, and DataFrames can be appended to a DataFrame!" ); } } // src/dataframe/df-apply.mjs function dfApply(DataFrame2, Series2, df, fn, axis) { axis = axis || 0; assert( isFunction(fn), "The first parameter to the `apply` method must be a function." ); assert( axis === 0 || axis === 1, "The second parameter to the `apply` method (the `axis`) must be 0 or 1." ); if (axis === 0) { const temp = {}; let shouldReturnADataFrame; forEach(df.columns, (colName, i) => { const series = new Series2(map(df.values, (row) => row[i])); series.name = colName; series.index = df.index; const value = fn(series, i, df); if (value instanceof Series2) { temp[colName] = value.values; } else { temp[colName] = value; } if (isUndefined(shouldReturnADataFrame)) { shouldReturnADataFrame = value instanceof Series2 || isArray(value); } }); if (shouldReturnADataFrame) { const out = new DataFrame2(temp); out.index = df.index; return out; } else { const out = new Series2(map(df.columns, (colName) => temp[colName])); out.index = df.columns; return out; } } else if (axis === 1) { let shouldReturnADataFrame; const temp = map(df.values, (row, i) => { const series = new Series2(row); series.name = df.index[i]; series.index = df.columns; const value = fn(series, i, df); if (isUndefined(shouldReturnADataFrame)) { shouldReturnADataFrame = value instanceof Series2 || isArray(value); } if (value instanceof Series2) { return value.values; } else { return value; } }); if (shouldReturnADataFrame) { const out = new DataFrame2(temp); out.index = df.index; out.columns = df.columns; return out; } else { const out = new Series2(temp); out.index = df.index; return out; } } } // src/is-string.mjs function isString(s2) { return typeof s2 === "string"; } // src/dataframe/df-assign.mjs function dfAssign(DataFrame2, Series2, df, p1, p2) { const isDataFrame2 = (x) => x instanceof DataFrame2; const isSeries2 = (x) => x instanceof Series2; if (!isUndefined(p2)) { assert( isString(p1), "If passing two arguments into the `assign` method, then the first argument must be a string name!" ); assert( isArray(p2) && !isJagged(p2) && shape(p2).length === 1, "If passing two arguments into the `assign` method, then the second argument must be a 1-dimensional array!" ); const out = df.copy(); if (out.columns.includes(p1)) { const index = out.columns.indexOf(p1); out.columns[index] = p1; forEach(out.values, (v, i) => v[index] = p2[i]); return out; } else { out._columns.push(p1); forEach(out._values, (v, i) => v.push(p2[i])); return out; } } else { if (isDataFrame2(p1)) { const out = df.copy(); const outShape = out.shape; const p1Shape = p1.shape; for (let j = 0; j < p1Shape[1]; j++) { const col = p1.columns[j]; const colNewIndex = out.columns.includes(col) ? out.columns.indexOf(col) : out.columns.length; if (!out.columns.includes(col)) { out._columns.push(col); } for (let i = 0; i < outShape[0]; i++) { out._values[i][colNewIndex] = p1._values[i][j]; } } return out; } else if (isSeries2(p1)) { return df.assign(p1.name, p1.values); } else if (isObject(p1)) { return df.assign(new DataFrame2(p1)); } else { throw new MathError( "You must pass a DataFrame, Series, or object into the `assign` method!" ); } } } // src/dataframe/df-copy.mjs function dfCopy(DataFrame2, df) { if (df.isEmpty) return new DataFrame2(); const out = new DataFrame2(copy(df.values)); out.columns = df.columns.slice(); out.index = df.index.slice(); return out; } // src/dataframe/df-drop.mjs function dfDrop(DataFrame2, Series2, df, rows, cols) { if (isUndefined(rows)) rows = []; if (isUndefined(cols)) cols = []; if (isString(rows) || isNumber(rows)) rows = [rows]; if (isString(cols) || isNumber(cols)) cols = [cols]; assert( isArray(rows), "The `drop` method only works on 1-dimensional arrays of numerical indices and/or strings." ); assert( isArray(cols), "The `drop` method only works on 1-dimensional arrays of numerical indices and/or strings." ); assert( shape(rows).length === 1, "The `drop` method only works on 1-dimensional arrays of numerical indices and/or strings." ); assert( shape(cols).length === 1, "The `drop` method only works on 1-dimensional arrays of numerical indices and/or strings." ); let outIndex, outColumns; forEach(df.index, (row, i) => { if (rows.indexOf(row) < 0 && rows.indexOf(i) < 0) { if (!outIndex) outIndex = []; outIndex.push(row); } }); forEach(df.columns, (col, i) => { if (cols.indexOf(col) < 0 && cols.indexOf(i) < 0) { if (!outColumns) outColumns = []; outColumns.push(col); } }); let out = df.get(outIndex, outColumns); if (out instanceof Series2) { let temp = new DataFrame2(); temp = temp.assign(out); if (df.index.indexOf(out.name) > -1) temp = temp.transpose(); out = temp; } return out; } // src/helpers/is-integer.mjs function isInteger(x) { return isNumber(x) && (x >= 0 ? Math.floor(x) === x : Math.ceil(x) === x); } // src/helpers/is-whole-number.mjs function isWholeNumber(x) { return isInteger(x) && x >= 0; } // src/dataframe/df-drop-missing.mjs function dfDropMissing(DataFrame2, Series2, df, axis, condition, threshold) { axis = axis || 0; assert( axis === 0 || axis === 1, "The first parameter of the `dropMissing` method (the `axis`) must be 0 or 1." ); threshold = threshold || 0; assert( isWholeNumber(threshold), "The third parameter of the `dropMissing` method (the `threshold`) should be a whole number (meaning that data should be dropped if it contains more than `threshold` null values)." ); condition = threshold > 0 ? "none" : condition || "any"; assert( condition === "any" || condition === "all" || condition === "none", "The second parameter of the `dropMissing` method (the `condition` parameter, which indicates the condition under which data should be dropped) should be 'any' or 'all' (meaning that if 'any' of the data contains null values, then it should be dropped; or that if 'all' of the data contains null values, then it should be dropped)." ); function helper5(values) { if (threshold > 0) { let count2 = 0; for (let i = 0; i < values.length; i++) { const value = values[i]; if (isUndefined(value)) count2++; if (count2 >= threshold) return []; } } else if (condition === "any") { for (let i = 0; i < values.length; i++) { const value = values[i]; if (isUndefined(value)) return []; } } else if (condition === "all") { for (let i = 0; i < values.length; i++) { const value = values[i]; if (!isUndefined(value)) return values; } return []; } return values; } let out = df.copy(); const tempID = Math.random().toString(); if (axis === 0) { out = out.assign(tempID, out.index); const newValues = filter(map(out.values, helper5), (row) => row.length > 0); if (shape(newValues).length < 2) return new DataFrame2(); out.values = newValues; let newIndex = out.get(null, tempID); if (isUndefined(newIndex)) return new DataFrame2(); if (isString(newIndex)) newIndex = [newIndex]; if (newIndex instanceof Series2) newIndex = newIndex.values; out.index = newIndex; out = out.drop(null, tempID); } else if (axis === 1) { const temp = {}; forEach(out.columns, (colName, i) => { const values = map(out.values, (row) => row[i]); const newValues = helper5(values); if (newValues.length > 0) { temp[colName] = newValues; } }); if (Object.keys(temp).length + Object.getOwnPropertySymbols(temp).length === 0) { return new DataFrame2(); } const newOut = new DataFrame2(temp); newOut.index = out.index; return newOut; } return out; } // src/drop-nan.mjs function dropNaN(x) { if (isDataFrame(x) || isSeries(x)) { return x.dropNaN(...Object.values(arguments).slice(1)); } assert( isArray(x), "The `dropNaN` function only works on arrays, Series, and DataFrames!" ); const out = []; forEach(x, (v) => { try { return out.push(dropNaN(v)); } catch (e) { if (isNumber(v)) { return out.push(v); } } }); return out; } // src/dataframe/df-drop-nan.mjs function dfDropNaN(DataFrame2, df, axis, condition, threshold) { axis = axis || 0; assert( axis === 0 || axis === 1, "The first parameter of the `dropNaN` method (the `axis`) must be 0 or 1." ); threshold = threshold || 0; assert( isWholeNumber(threshold), "The third parameter of the `dropNaN` method (the `threshold`) should be a whole number (meaning that data should be dropped if it contains more than `threshold` NaN values)." ); condition = threshold > 0 ? "none" : condition || "any"; assert( condition === "any" || condition === "all" || condition === "none", "The second parameter of the `dropNaN` method (the `condition` parameter, which indicates the condition under which data should be dropped) should be 'any' or 'all' (meaning that if 'any' of the data contains NaN values, then it should be dropped; or that if 'all' of the data contains NaN values, then it should be dropped)." ); function helper5(values) { const numericalValues = dropNaN(values); if (threshold > 0) return values.length - numericalValues.length < threshold; if (condition === "any") return numericalValues.length === values.length; if (condition === "all") return numericalValues.length > 0; return true; } const out = df.copy(); if (axis === 0) { const rowsToKeep = filter(out.index, (row) => { const values = out.get(row, null).values; return helper5(values); }); if (rowsToKeep.length > 0) return out.get(rowsToKeep, null); else return new DataFrame2(); } else if (axis === 1) { const colsToKeep = filter(out.columns, (col) => { const values = out.get(null, col).values; return helper5(values); }); if (colsToKeep.length > 0) return out.get(null, colsToKeep); else return new DataFrame2(); } return out; } // src/dataframe/df-filter.mjs function arrayToObject(x) { const out = {}; forEach(flatten(x), (value, i) => { out[value] = i; }); return out; } function undoArrayToObject(obj) { return Object.keys(obj).concat(Object.getOwnPropertySymbols(obj)).sort((a, b) => obj[a] - obj[b]); } function dfFilter(DataFrame2, Series2, df, fn, axis) { assert( isFunction(fn), "The `filter` method takes a single parameter: a function that is used to filter the values." ); if (isUndefined(axis)) axis = 0; assert( axis === 0 || axis === 1, "The `axis` parameter to the `filter` method must be 0 or 1." ); let out = df.copy(); if (out.isEmpty) return out; const index = arrayToObject(out.index); const columns = arrayToObject(out.columns); if (axis === 0) { let count2 = 0; const newValues = filter(out.values, (row, i) => { const series = new Series2(row); series.name = df.index[i]; series.index = df.columns; const shouldKeep = fn(series, i, df); if (shouldKeep) { count2++; } else { delete index[out.index[i]]; } return shouldKeep; }); if (count2 === 0) { return new DataFrame2(); } if (count2 === 1) { const temp = new Series2(newValues[0]); temp.name = undoArrayToObject(index)[0]; temp.index = undoArrayToObject(columns); return temp; } out.values = newValues; out.index = undoArrayToObject(index); } else if (axis === 1) { out = out.transpose(); let count2 = 0; const newValues = filter(out.values, (row, i) => { const series = new Series2(row); series.name = df.columns[i]; series.index = df.index; const shouldKeep = fn(series, i, df); if (shouldKeep) { count2++; } else { delete columns[out.index[i]]; } return shouldKeep; }); if (count2 === 0) { return new DataFrame2(); } if (count2 === 1) { const temp = new Series2(newValues[0]); temp.name = undoArrayToObject(columns)[0]; temp.index = undoArrayToObject(index); return temp; } out.values = newValues; out.index = undoArrayToObject(columns); out = out.transpose(); } return out; } // src/dataframe/df-get.mjs function dfGet(df, rows, cols) { if (isString(rows) || isNumber(rows)) rows = [rows]; if (isString(cols) || isNumber(cols)) cols = [cols]; for (const i in rows) { if (typeof rows[i] === "bigint") { rows[i] = Number(rows[i]); } } for (const i in cols) { if (typeof cols[i] === "bigint") { cols[i] = Number(cols[i]); } } const types = set(map((rows || []).concat(cols || []), (v) => typeof v)); assert( types.length <= 2, "Only whole numbers and/or strings are allowed in `get` arrays!" ); if (types.length === 1) { assert( types[0] === "string" || types[0] === "number", "Only whole numbers and/or strings are allowed in `get` arrays!" ); } if (types.length === 2) { assert( types.indexOf("string") > -1, "Only whole numbers and/or strings are allowed in `get` arrays!" ); assert( types.indexOf("number") > -1, "Only whole numbers and/or strings are allowed in `get` arrays!" ); } if (!isUndefined(rows)) { rows = map(rows, (r) => { if (isString(r)) { assert(df.index.indexOf(r) > -1, `Row "${r}" does not exist!`); return r; } if (isNumber(r)) { assert(r >= 0, `Index ${r} is out of bounds!`); assert(Math.floor(r) === r, `Row numbers must be integers!`); assert(r < df.index.length, `Index ${r} is out of bounds!`); return df.index[r]; } }); } if (!isUndefined(cols)) { cols = map(cols, (c) => { if (isString(c)) { assert(df.columns.indexOf(c) > -1, `Column "${c}" does not exist!`); return c; } if (isNumber(c)) { assert(c >= 0, `Column ${c} is out of bounds!`); assert(Math.floor(c) === c, `Column numbers must be integers!`); assert(c < df.columns.length, `Column ${c} is out of bounds!`); return df.columns[c]; } }); } return df.getSubsetByNames(rows, cols); } // src/sort.mjs function alphaSort(a, b) { try { if (a < b) return -1; if (a > b) return 1; return 0; } catch (e) { a = typeof a === "object" && a !== null ? JSON.stringify(a) : a.toString(); b = typeof b === "object" && b !== null ? JSON.stringify(b) : b.toString(); if (a < b) return -1; if (a > b) return 1; return 0; } } function sort(arr, fn) { if (isUndefined(fn)) fn = alphaSort; if (isDataFrame(arr) || isSeries(arr)) { return arr.sort(...Object.values(arguments).slice(1)); } assert( isArray(arr), "The `sort` function only works on arrays, Series, and DataFrames!" ); assert( isFunction(fn), "The second parameter of the `sort` function must be a comparison function!" ); const out = arr.slice(); out.sort(fn); return out; } // src/dataframe/df-get-dummies.mjs function camelify(text) { const temp = text.toLowerCase(); let out = ""; for (let i = 0; i < temp.length; i++) { const char = temp[i]; if (char.match(/[a-z0-9]/g)) { out += char; } else { out += " "; } } const words = filter(out.split(" "), (word) => word.length > 0); return words[0] + map( words.slice(1), (word) => word[0].toUpperCase() + word.substring(1) ).join(""); } function dfGetDummies(DataFrame2, df, columns) { if (isUndefined(columns)) { columns = df.columns; } else if (isString(columns)) { columns = [columns]; } const temp = {}; forEach(columns, (col) => { assert( isString(col), "You must pass either a string or a one-dimensional array of strings into the `getDummies` (AKA `