UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

404 lines 11.1 kB
import { clamp } from "./math"; import { assert, assert_debug } from "./assert"; export { min, max, minmax } from "./iterator"; const { floor } = Math; export function is_empty(array) { return array.length == 0; } export function is_sorted(array) { const n = array.length; if (n == 0) { return true; } let prev = array[0]; for (let i = 1; i < n; i++) { const curr = array[i]; if (prev <= curr) { prev = curr; } else { return false; } } return true; } export function copy(array) { if (Array.isArray(array)) { return array.slice(); } else { return new array.constructor(array); } } export function splice(array, start, k, ...items) { if (Array.isArray(array)) { const result = copy(array); if (k === undefined) { result.splice(start); } else { result.splice(start, k, ...items); } return result; } const len = array.length; if (start < 0) { start += len; } if (start < 0) { start = 0; } else if (start > len) { start = len; } if (k == null || k > len - start) { k = len - start; } else if (k < 0) { k = 0; } const n = len - k + items.length; const result = new array.constructor(n); let i = 0; for (; i < start; i++) { result[i] = array[i]; } for (const item of items) { result[i++] = item; } for (let j = start + k; j < len; j++) { result[i++] = array[j]; } return result; } export function head(array, n) { return splice(array, n, array.length - n); } export function insert(array, item, i) { return splice(array, i, 0, item); } export function append(array, item) { return splice(array, array.length, 0, item); } export function prepend(array, item) { return splice(array, 0, 0, item); } export function index_of(array, item) { return array.indexOf(item); } export function includes(array, value) { return array.indexOf(value) !== -1; } export const contains = includes; export function subselect(array, indices) { const n = indices.length; const result = new array.constructor(n); for (let i = 0; i < n; i++) { result[i] = array[indices[i]]; } return result; } export function mul(array, coeff, output) { const n = array.length; const result = output ?? new array.constructor(n); for (let i = 0; i < n; i++) { result[i] = array[i] * coeff; } return result; } export function map(array, fn) { const n = array.length; const result = new array.constructor(n); for (let i = 0; i < n; i++) { result[i] = fn(array[i], i, array); } return result; } export function inplace_map(array, fn, output) { const n = array.length; const result = output ?? array; for (let i = 0; i < n; i++) { result[i] = fn(array[i], i); } } export function filter(array, pred) { const n = array.length; const result = new array.constructor(n); let k = 0; for (let i = 0; i < n; i++) { const value = array[i]; if (pred(value, i, array)) { result[k++] = value; } } return head(result, k); } export function reduce(array, fn, initial) { const n = array.length; if (initial === undefined && n == 0) { throw new Error("can't reduce an empty array without an initial value"); } let value; let i; if (initial === undefined) { value = array[0]; i = 1; } else { value = initial; i = 0; } for (; i < n; i++) { value = fn(value, array[i], i, array); } return value; } export function sort_by(array, key) { const tmp = Array.from(array, (value, index) => { return { index, key: key(value) }; }); tmp.sort((left, right) => { const a = left.key; const b = right.key; if (a !== b) { if (a > b) { return 1; } if (a < b) { return -1; } } return left.index - right.index; }); return map(array, (_, i) => array[tmp[i].index]); } export function minmax2(arr, brr) { let a; let b; let a_min = +Infinity; let a_max = -Infinity; let b_min = +Infinity; let b_max = -Infinity; const n = Math.min(arr.length, brr.length); for (let i = 0; i < n; i++) { a = arr[i]; b = brr[i]; if (!isNaN(a) && !isNaN(b)) { if (a < a_min) { a_min = a; } if (a > a_max) { a_max = a; } if (b < b_min) { b_min = b; } if (b > b_max) { b_max = b; } } } return [a_min, a_max, b_min, b_max]; } export function min_by(array, key) { if (array.length == 0) { throw new Error("min_by() called with an empty array"); } let result = array[0]; let result_computed = key(result, 0); for (let i = 1, length = array.length; i < length; i++) { const value = array[i]; const computed = key(value, i); if (computed < result_computed) { result = value; result_computed = computed; } } return result; } export function max_by(array, key) { if (array.length == 0) { throw new Error("max_by() called with an empty array"); } let result = array[0]; let result_computed = key(result, 0); for (let i = 1, length = array.length; i < length; i++) { const value = array[i]; const computed = key(value, i); if (computed > result_computed) { result = value; result_computed = computed; } } return result; } export function sum(array) { let result = 0; for (let i = 0, n = array.length; i < n; i++) { result += array[i]; } return result; } export function cumsum(array) { const result = new array.constructor(array.length); reduce(array, (a, b, i) => result[i] = a + b, 0); return result; } export function every(iter, predicate) { for (const item of iter) { if (!predicate(item)) { return false; } } return true; } export function some(iter, predicate) { for (const item of iter) { if (predicate(item)) { return true; } } return false; } function _find_index(dir) { return function (array, predicate) { const length = array.length; let index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { if (predicate(array[index])) { return index; } } return -1; }; } export const find_index = _find_index(1); export const find_last_index = _find_index(-1); export function find(array, predicate) { const index = find_index(array, predicate); return index == -1 ? undefined : array[index]; } export function find_last(array, predicate) { const index = find_last_index(array, predicate); return index == -1 ? undefined : array[index]; } export function bisect_left_by(array, value, fn, low = 0, high = array.length) { assert_debug(() => is_sorted(array)); assert(0 <= low && high <= array.length); while (low < high) { const mid = floor((low + high) / 2); if (fn(array[mid]) < value) { low = mid + 1; } else { high = mid; } } return low; } export function bisect_right_by(array, value, fn, low = 0, high = array.length) { assert_debug(() => is_sorted(array)); assert(0 <= low && high <= array.length); while (low < high) { const mid = floor((low + high) / 2); if (fn(array[mid]) <= value) { low = mid + 1; } else { high = mid; } } return low; } export function bisect_left(array, value, low = 0, high) { return bisect_left_by(array, value, (item) => item, low, high); } export function bisect_right(array, value, low = 0, high) { return bisect_right_by(array, value, (item) => item, low, high); } export function binary_search(array, value) { const i = bisect_left(array, value); return i != array.length && array[i] == value ? i : null; } export const sorted_index = bisect_left; export function bin_counts(data, bin_edges) { const nbins = bin_edges.length - 1; const counts = Array(nbins).fill(0); for (let i = 0; i < data.length; i++) { const sample = data[i]; const index = sorted_index(bin_edges, sample); const bin = clamp(index - 1, 0, nbins - 1); counts[bin] += 1; } return counts; } export function interpolate(points, x_values, y_values) { // Implementation ported from np.interp const n = points.length; const results = new Array(n); for (let i = 0; i < n; i++) { const point = points[i]; if (isNaN(point) || x_values.length == 0) { results[i] = NaN; continue; } const index = left_edge_index(point, x_values); if (index == -1) { results[i] = y_values[0]; } else if (index == x_values.length) { results[i] = y_values[y_values.length - 1]; } else if (index == x_values.length - 1 || x_values[index] == point) { results[i] = y_values[index]; } else { const x0 = x_values[index]; const y0 = y_values[index]; const x1 = x_values[index + 1]; const y1 = y_values[index + 1]; results[i] = lerp(point, x0, y0, x1, y1); } } return results; } function lerp(x, x0, y0, x1, y1) { const slope = (y1 - y0) / (x1 - x0); let res = slope * (x - x0) + y0; if (!isFinite(res)) { res = slope * (x - x1) + y1; if (!isFinite(res) && (y0 == y1)) { res = y0; } } return res; } export function left_edge_index(point, intervals) { if (point < intervals[0]) { return -1; } if (point > intervals[intervals.length - 1]) { return intervals.length; } if (intervals.length == 1) { // Implies point == intervals[0] return 0; } let leftEdgeIndex = 0; let rightEdgeIndex = intervals.length - 1; while (rightEdgeIndex - leftEdgeIndex != 1) { const indexOfNumberToCompare = leftEdgeIndex + Math.floor((rightEdgeIndex - leftEdgeIndex) / 2); if (point >= intervals[indexOfNumberToCompare]) { leftEdgeIndex = indexOfNumberToCompare; } else { rightEdgeIndex = indexOfNumberToCompare; } } return leftEdgeIndex; } export function norm(array, start, end) { const span = end - start; return map(array, (x) => (x - start) / span); } //# sourceMappingURL=arrayable.js.map