Public
Edited
Feb 3, 2023
Insert cell
# choose-ticks translation
Insert cell
function first_power10(min, max) {
let value = max < 0 ? - (10 ** Math.ceil(Math.log(-max)/ Math.log(10))) : 10 ** (Math.floor(Math.log(max) / Math.log(10)))
return value <= min ? false : value
}
Insert cell
first_power10(27, 1037) // should be 1000 (first power below max)
Insert cell
first_power10(0, Number.MAX_VALUE)
Insert cell
function clamp(x, lo, hi) {
return Math.min(hi, Math.max(x, lo))
}
Insert cell
clamp(1, 3, 4)
Insert cell
math = require('mathjs')
Insert cell
function to_signed_int (float64) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setFloat64(0, float64)
return view.getBigInt64(0)
}
Insert cell
function real_from_signed_int (signed) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setBigInt64(0, signed)
return view.getFloat64(0)
}
Insert cell
function mbn(x) {
return math.bignumber(to_signed_int(x).toString())
}
Insert cell
mbn_neg_0 = mbn(-0.0)
Insert cell
function real_to_ordinal(real) {
let signed = to_signed_int(real)
let mbn = math.bignumber(signed.toString())
return signed >= 0 ? mbn : math.subtract(mbn_neg_0, mbn)
}
Insert cell
function ordinal_to_real(ordinal) {
return ordinal >=0 ? real_from_signed_int(BigInt(ordinal)) : real_from_signed_int(BigInt(math.subtract(mbn_neg_0, math.bignumber(ordinal))))
}
Insert cell
function float_to_approximate_ordinal(float) {
return real_to_ordinal(float).toNumber()
}
Insert cell
float_to_approximate_ordinal(Number.MAX_VALUE)
Insert cell
Number.MAX_VALUE
Insert cell
Number.MIN_VALUE
Insert cell
ordinal_to_real(real_to_ordinal(Number.MIN_VALUE))
Insert cell
function choose_between(min, max, number) {
// ; Returns a given number of ticks, roughly evenly spaced, between min and max
// ; For any tick, n divisions below max, the tick is an ordinal corresponding to:
// ; (a) a power of 10 between n and (n + ε) divisions below max where ε is some tolerance, or
// ; (b) a value, n divisions below max
let sub_range = Math.round((max - min) / (1 + number))
let near = (x, n) => (x <= n) && (Math.abs((x - n) / sub_range) <= .2) // <= tolerance
return [...Array(number)].map((_, i) => i + 1).map(itr => {
let power10 = first_power10(
ordinal_to_real(clamp(max - ((itr + 1) * sub_range), min, max)),
ordinal_to_real(clamp(max - (itr * sub_range), min, max))
)
console.log(power10)
return (power10 && near(real_to_ordinal(power10), max - (itr * sub_range))) ? real_to_ordinal(power10)
: max - (itr * sub_range)
})
}
Insert cell
choose_between(real_to_ordinal(1), real_to_ordinal(1e3), 13)
Insert cell
first_power10(1, 10000)
Insert cell
choose_between(real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v))
Insert cell
ordinal_to_real(real_to_ordinal(1))
Insert cell
ordinal_to_real(real_to_ordinal(1e3))
Insert cell
choose_between(real_to_ordinal(1), real_to_ordinal(1e2), 13).map(v => ordinal_to_real(v))
Insert cell
choose_between(real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 0)
Insert cell
function pick_spaced_ordinals(necessary, min, max, number) {
// NOTE that subtle use of mathjs bignumbers is required in this function...
let sub_range = math.divide(math.bignumber(math.subtract(math.bignumber(max), math.bignumber(min))), math.bignumber(number)) // size of a division on the ordinal range
let necessary_star = (function loop(necessary) {
return necessary.length < 2 ? necessary
: math.smaller(math.subtract(necessary[1], necessary[0]), sub_range) ? loop(necessary.slice(1))
: [necessary[0], ...loop(necessary.slice(1))]
})(necessary) // filter out necessary points that are too close
let all = (function loop(necessary, min_star, start) {
if (start >= number) { return [] }
if (necessary.length == 0) { return choose_between(min_star, max, number - start) }
let idx = false
for (let i=0; i<number; i++) {
if (math.smallerEq(math.subtract(necessary[0], math.add(min, math.bignumber(math.multiply(i, sub_range)))), sub_range)) {
idx = i
break
}
}
return [...choose_between(min_star, necessary[0], idx - start), ...loop(necessary.slice(1), necessary[0], idx + 1)]
})(necessary_star, min, 0)
return [...all, ...necessary_star].sort((a, b) => math.subtract(math.bignumber(a), math.bignumber(b)))
}
Insert cell
pick_spaced_ordinals([ real_to_ordinal(0), real_to_ordinal(1)], real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v))
Insert cell
pick_spaced_ordinals([real_to_ordinal(-1), real_to_ordinal(-1), real_to_ordinal(0), real_to_ordinal(1), real_to_ordinal(Number.MAX_VALUE)], real_to_ordinal(-1), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v).toString())
Insert cell
function choose_ticks (min, max) {
let tick_count = 13
let necessary = [min, -1.0, 0, 1.0, max].filter(v => (min <= v) && (v <= max) && (min <= max)).map(v => real_to_ordinal(v))
let major_ticks = pick_spaced_ordinals(necessary, real_to_ordinal(min), real_to_ordinal(max), tick_count).map(v => ordinal_to_real(v))
return major_ticks
}
Insert cell
test = choose_ticks(-.731, Number.MAX_VALUE)
Insert cell
test.map(formatter)
Insert cell
formatter = v => {
const s = v.toString()
const [base, exponent] = s.split('e')
const [l, r] = base.split('.')
const formattedBase = !r ? base : `${l}.${r.slice(0, 2)}`
return !exponent ? formattedBase : `${formattedBase}e${exponent}`
}
Insert cell
formatter2 = v => {
const s = v.toPrecision(1)
const [base, exponent] = s.split('e')
if (!exponent) {
return v.toPrecision(1) == v ? v.toPrecision(1) : v.toPrecision(6)
}
if (exponent <= 1 && -1 <= exponent) {
const a = v.toString()
const b = v.toPrecision(6)
return a.length < b.length ? a : b
}
return v.toPrecision(1)
}
Insert cell
test2 = choose_ticks(1, 1000)
Insert cell
test2.map(formatter2)
Insert cell
function choose_ticks_clientside (min, max) {
function to_signed_int (float64) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setFloat64(0, float64)
return view.getBigInt64(0)
}
function real_from_signed_int (signed) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setBigInt64(0, signed)
return view.getFloat64(0)
}
function first_power10(min, max) {
let value = max < 0 ? - (10 ** Math.ceil(Math.log(-max)/ Math.log(10))) : 10 ** (Math.floor(Math.log(max) / Math.log(10)))
return value <= min ? false : value
}
function clamp(x, lo, hi) {
return Math.min(hi, Math.max(x, lo))
}
function mbn(x) {
return math.bignumber(to_signed_int(x).toString())
}
let mbn_neg_0 = mbn(-0.0)
function real_to_ordinal(real) {
let signed = to_signed_int(real)
let mbn = math.bignumber(signed.toString())
return signed >= 0 ? mbn : math.subtract(mbn_neg_0, mbn)
}
function ordinal_to_real(ordinal) {
return ordinal >=0 ? real_from_signed_int(BigInt(ordinal)) : real_from_signed_int(BigInt(math.subtract(mbn_neg_0, math.bignumber(ordinal))))
}
function choose_between(min, max, number) {
// ; Returns a given number of ticks, roughly evenly spaced, between min and max
// ; For any tick, n divisions below max, the tick is an ordinal corresponding to:
// ; (a) a power of 10 between n and (n + ε) divisions below max where ε is some tolerance, or
// ; (b) a value, n divisions below max
let sub_range = Math.round((max - min) / (1 + number))
let near = (x, n) => (x <= n) && (Math.abs((x - n) / sub_range) <= .2) // <= tolerance
return [...Array(number)].map((_, i) => i + 1).map(itr => {
let power10 = first_power10(
ordinal_to_real(clamp(max - ((itr + 1) * sub_range), min, max)),
ordinal_to_real(clamp(max - (itr * sub_range), min, max))
)
return power10 && near(real_to_ordinal(power10), max - (itr * sub_range)) ? real_to_ordinal(power10)
: max - (itr * sub_range)
})
}
function pick_spaced_ordinals(necessary, min, max, number) {
// NOTE use of mathjs bignumber arithmetic is required in this function!
let sub_range = math.divide(math.bignumber(math.subtract(math.bignumber(max), math.bignumber(min))), math.bignumber(number)) // size of a division on the ordinal range
let necessary_star = (function loop(necessary) {
return necessary.length < 2 ? necessary
: math.smaller(math.subtract(necessary[1], necessary[0]), sub_range) ? loop(necessary.slice(1))
: [necessary[0], ...loop(necessary.slice(1))]
})(necessary) // filter out necessary points that are too close
let all = (function loop(necessary, min_star, start) {
if (start >= number) { return [] }
if (necessary.length == 0) { return choose_between(min_star, max, number - start) }
let idx = false
for (let i=0; i<number; i++) {
if (math.smallerEq(math.subtract(necessary[0], math.add(min, math.bignumber(math.multiply(i, sub_range)))), sub_range)) {
idx = i
break
}
}
return [...choose_between(min_star, necessary[0], idx - start), ...loop(necessary.slice(1), necessary[0], idx + 1)]
})(necessary_star, min, 0)
return [...all, ...necessary_star].sort((a, b) => math.subtract(math.bignumber(a), math.bignumber(b)))
}
function choose_ticks (min, max) {
let tick_count = 13
let necessary = [min, -1.0, 0, 1.0, max].filter(v => (min <= v) && (v <= max) && (min <= max)).map(v => real_to_ordinal(v))
let major_ticks = pick_spaced_ordinals(necessary, real_to_ordinal(min), real_to_ordinal(max), tick_count).map(v => ordinal_to_real(v))
return major_ticks
}
return choose_ticks(min, max)
}
Insert cell
choose_ticks_clientside(-1.79e308, 1.79e308)
Insert cell
1 < 4 < 3
Insert cell
ordinal_to_real("-4607182418800017408")
Insert cell
real_to_ordinal(0).toString()
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more