@bokeh/bokehjs
Version:
Interactive, novel data visualization
404 lines • 11.1 kB
JavaScript
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