javascript - 使用比例重新映射数字
问题描述
例如,我有一个数字域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 变成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) 如果比例是线性的并且 domain = [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) 如果比例是线性的并且 domain = [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) 如果 scale 是对数且 domain = [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 = ?
解决方案
在我之前的回答中,我展示了计算某个范围内数字的波段索引的正确函数:
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
上述函数使用线性刻度。但是,很容易将其推广到使用另一个尺度:
const index = (scale, min, max, bands, n) =>
Math.floor(bands * scale(n - min) / scale(max - min + 1));
const log = x => Math.log(x + 1);
const logBand = n => index(log, 0, 100, 5, n);
console.log(logBand(0), logBand(1)); // 0 0
console.log(logBand(2), logBand(5)); // 1 1
console.log(logBand(6), logBand(15)); // 2 2
console.log(logBand(16), logBand(39)); // 3 3
console.log(logBand(40), logBand(100)); // 4 4
这里我们使用了对数刻度。请注意,我们在计算其对数之前增加了索引,因为零的对数是未定义的,尽管 JavaScript 很高兴地返回 as 的自然对数的极限x
趋于x
零(即-Infinity
)。但是,-Infinity
它不是一个有效的索引。
无论如何,我们的范围如下:
i: 0 --> [0 - 1] --> 0.2
i: 1 --> [2 - 5] --> 0.4
i: 2 --> [6 - 15] --> 0.6
i: 3 --> [16 - 39] --> 0.8
i: 4 --> [40 - 100] --> 1
请注意,尽管我们的规模是对数的,但我们的范围呈指数增长。这是有道理的,因为当我们以对数方式缩放范围时,我们将数字压缩在一起。因此,当我们将压缩范围划分为带时,每个带中的元素数量呈指数增长。下图可以最好地解释它:
在 x 轴之一,我们有我们的线性刻度,其值从1
到101
。在 y 轴上,我们有我们的对数刻度,其值从log(1)
到log(101)
(表示为 5/5 用于教育目的)。如您所见,我们将我们的对数范围划分为均匀大小的波段。然而,在我们的线性尺度上,这些波段会呈指数级增长。