@jrc03c/js-math-tools
Version:
some math tools for JS
1,692 lines (1,646 loc) • 145 kB
JavaScript
(() => {
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: () => timeSync,
timeAsync: () => timeAsync,
timeSync: () => timeSync,
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 (!temp[key]) 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 in