Используйте шкалы, чтобы переназначить число
У меня есть домен чисел, например 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));
}
Смотрите логарифмическое преобразование базы.
Очевидный вариант использования логарифмического масштабирования заключается в том, что он хорошо работает со структурами данных кучи.