Используйте шкалы, чтобы переназначить число

У меня есть домен чисел, например domain = [100, 200] и количество полос, на которые нужно разделить диапазон, например bands = 5. Я знаю, что каждая полоса соответствует значению:

band #1 --> v = 0.2
band #2 --> v = 0.4
band #3 --> v = 0.6
band #4 --> v = 0.8
band #5 --> v = 1.0

Эти значения являются фиксированными (жестко запрограммированными): если полосы стали bands = 6 тогда разработчик выбирает значение band #6.

Я хочу разделить домен на полосы, размер которых зависит от используемого масштаба. Например, я мог бы хотеть использовать или линейную или логарифмическую или шкалу Pow.

Тогда я хочу функцию, которая на входе принимает номер x ∈ domain и должна возвращать значение v связанное с полосой, которой принадлежит номер inout.

Здесь похожий вопрос, но теперь я хочу использовать разные шкалы (например, я могу использовать d3 scales), но я не знаю, как..

Вот кусок кода:

function getLinearScaledValue(x, min, max, bands) {
  const range = max - min
  if (x === max) {
    return 1
  } else {
    return Math.floor(1 + ((x - min) / range) * bands) / bands
  }
}

где min и max - это минимальное и максимальное значение домена.

Я думаю, что примеры лунатизма были хорошими, поэтому я поместил их здесь:

если полос = 5:

band #1 --> v = 0.2
band #2 --> v = 0.4
band #3 --> v = 0.6
band #4 --> v = 0.8
band #5 --> v = 1.0

(1) если масштаб линейный, а домен = [0, 100] → полосы:

band #1 --> v = 0.2 --> [0, 20]
band #2 --> v = 0.4 --> [21, 40]
band #3 --> v = 0.6 --> [41, 60]
band #4 --> v = 0.8 --> [61, 80]
band #5 --> v = 1.0 --> [81, 100]

например:

if x = 0  --> v = 0.2
if x = 10 --> v = 0.2
if x = 21 --> v = 0.4
if x = 98 --> v = 1.0

(2) если масштаб линейный, а домен = [100, 200] → полосы:

band #1 --> v = 0.2 --> [100, 120]
band #2 --> v = 0.4 --> [121, 140]
band #3 --> v = 0.6 --> [141, 160]
band #4 --> v = 0.8 --> [161, 180]
band #5 --> v = 1.0 --> [181, 200]

например:

if x = 100 --> v = 0.2
if x = 110 --> v = 0.2
if x = 121 --> v = 0.4
if x = 198 --> v = 1.0

(3) если масштаб является логарифмическим, а домен = [0, 100] → полосы:

band #1 --> v = 0.2 --> [?, ?]
band #2 --> v = 0.4 --> [?, ?]
band #3 --> v = 0.6 --> [?, ?]
band #4 --> v = 0.8 --> [?, ?]
band #5 --> v = 1.0 --> [?, ?]

например:

if x = 0  --> v = ?
if x = 10 --> v = ?
if x = 21 --> v = ?
if x = 98 --> v = ?

Ответ 1

В моем предыдущем ответе я показал правильную функцию для вычисления индекса диапазона числа в диапазоне:

const index = (min, max, bands, n) =>
    Math.floor(bands * (n - min) / (max - min + 1));

const band = n => index(0, 100, 5, n);

console.log(band(0),  band(20));  // 0 0
console.log(band(21), band(40));  // 1 1
console.log(band(41), band(60));  // 2 2
console.log(band(61), band(80));  // 3 3
console.log(band(81), band(100)); // 4 4

Ответ 2

Думаю, если я правильно понял ваш вопрос, может помочь следующее решение:

//upper threshold of each band
const bandMap = [0.2, 0.4, 0.6, 0.8, 1];
//map number to band
const mapNumToBand = (num, min, max, bands) => {
  //normalize number to domain min/max
  const normalizedNum = (num - min) / (max - min);
  //get highest matching band threshold
  return bands.reduce((highestBand, band) => highestBand >= normalizedNum ? highestBand : band);
}
//output the result
console.log(mapNumToBand(141, 100, 200, bandMap));

Ответ 3

Не уверен, что я прав, но скажу, но в этом случае просто перечитайте X заранее и затем используйте его в линейной функции - пример списка значений - если вы поместите их в XL, вы получите логарифмическую кривую (из исходных линейных значений) ) и линейный из пересчитанных (с топором), но, пожалуйста, не бейте меня, если я ошибаюсь ;-)

function recalcValue(v, logBase, domainSize) {
  return i,Math.pow(logBase,i*Math.log(domainSize)/Math.log(logBase)/domainSize);
}
var domainSize = 30;
var logBase = 10;
for(var i=1;i<=domainSize;i++) {
  console.log(i,recalcValue(i,logBase,domainSize));
}

Ответ 4

Прежде всего, вам нужны соглашения. Вы должны задать себе правильные вопросы.

Что означает логарифмический? Функция логарифма имеет два входа, основание и значение. Выход - это сила, на которую вы поднимаете базу, чтобы получить число. Предположим, что база равна 2, а область - 0–200. В этом случае вы рассчитываете log(2, 200), что составляет примерно 7.64. Это означает, что у нас есть пределы, [1, 2, 4, 8, 16, 32, 64, 128]. Если мы исключим 1 (то есть 2 0), то у нас будет 6 границ, что означает, что у нас есть 7 регионов.

Что произойдет, если домен 200–400? В этом случае у вас будет только два региона, что не имеет смысла. В отличие от линейного масштабирования, логарифмическое масштабирование даст разные результаты для разных доменов, даже если их размер одинаков.

Я думаю, что имеет смысл считать домены векторными, т.е. Не имеет значения, является ли домен 0–200 или 200–400. Единственным ограничением является то, что домен является положительным и длиной 200. Таким образом, домен 200–400 будет разделен аналогично домену 0–200, единственное отличие состоит в значениях. Для самого подразделения мы используем индексы.

Если мы примем это соглашение, то проблема значительно упростится. Домен 200–400 имеет длину 201. Следовательно, вместо [1, 2, 4, 8, 16, 32, 64, 128] мы имеем [201, 202, 204, 208, 216, 232, 264, 328].

Теперь логарифмическая шкала выглядит как линейная шкала. Количество границ в линейной шкале равно основанию логарифма. Следовательно, в случае логарифмической шкалы мы имели бы это:

function getBaseLog(x, y) {
  return Math.log(y) / Math.log(x);
}

function getLogarithmicScaledValue(x, min, max, base) {
    return parseInt(getBaseLog(base, x - min));
}

Смотрите логарифмическое преобразование базы.

Очевидный вариант использования логарифмического масштабирования заключается в том, что он хорошо работает со структурами данных кучи.