用于映射整数范围的最佳d3比例

Jul*_*enD 5 javascript d3.js

我想构建一个比例,将一系列连续的整数(字符串中的字符索引)映射到另一个整数范围(像素,比如0-600)的常规间隔.也就是说,我想将字符分配给像素,并且相反地尽可能规则地分配,一个的长度不一定是另一个的倍数.

例如,我希望将[0,1,2,3]映射到400像素

0 -> 0-99
1 -> 100-199
2 -> 200-299
3 -> 300-399
Run Code Online (Sandbox Code Playgroud)

反之亦然

0-99 -> 0
100-199 -> 1
200-299 -> 2
300-399 -> 3
Run Code Online (Sandbox Code Playgroud)

而对于映射0-4000到400像素,我希望

0-9 -> 0
10-19 -> 1
etc.
Run Code Online (Sandbox Code Playgroud)

在d3中使用的最佳比例是多少?

一方面,我担心离散标度不会使用域被均等分离的事实,并且如果元素的数量很大则生成一个巨大的switch语句.由于我将使用每个元素上的比例来绘制图像,我担心性能.

另一方面,线性比例如

d3.scaleLinear()
    .domain([0,399])   // 400 pixels
    .rangeRound([0,3])  // 4 elements
Run Code Online (Sandbox Code Playgroud)

给我

0 0
66 0  // 1st interval has 66 pixels
67 1
199 1 // other intervals have 132 pixels
200 2 
332 2
333 3 // last interval has 66 pixels
400 3
Run Code Online (Sandbox Code Playgroud)

(小提琴)

因此插补器返回不等间隔(末端较短).

编辑:不使用d3,实现起来并不难:

function coordinateFromSeqIndex(index, seqlength, canvasSize) {
     return Math.floor(index * (canvasSize / seqlength));
}
function seqIndexFromCoordinate(px, seqlength, canvasSize) {
    return Math.floor((seqlength/canvasSize) * px);
}
Run Code Online (Sandbox Code Playgroud)

太糟糕了,只有它没有d3音阶,因为它会变得更具可读性.

Ste*_*ton 5

如果要映射到间隔,则d3 量化比例是最佳选项.但是,比例尺在离散值和连续间隔之间进行映射.我不是100%清楚你想做什么,但让我们看看我如何用量化量表做一些你提到的事情.

将整数映射到间隔是很简单的,只要您知道d3使用半开区间[,]来分解连续域.

var s1 = d3.scaleQuantize()
          .domain([0,400])
          .range([0,1,2,3]);

s1.invertExtent(0); // the array [0,100] represents the interval [0,100)
s1.invertExtent(1); // [100,200)
s1.invertExtent(2); // [200,300)
s1.invertExtent(3); // [300,400)
Run Code Online (Sandbox Code Playgroud)

您还可以枚举离散值:

var interval = s.invertExtent(0);
d3.range(interval[0], interval[1]); // [0, 1, ... , 399]
Run Code Online (Sandbox Code Playgroud)

这些是你给出的很好的值,并且因为你想将整数映射到整数的间隔,所以当数字不可分时我们将需要舍入.我们可以使用Math.round.

var s2 = d3.scaleQuantize()
          .domain([0,250])
          .range([0,1,2,3]);

s2.invertExtent(0); // [0, 62.5)
s2.invertExtent(0).map(Math.round); // [0,63) ... have to still interpret as half open
Run Code Online (Sandbox Code Playgroud)

从区间本身到整数没有映射,但是比例将区域中的点映射到域(连续)到其范围内的值.

[0, 99, 99.9999, 100, 199, 250, 399, 400].map(s1); // [0, 0, 0, 1, 1, 2, 3, 3]
Run Code Online (Sandbox Code Playgroud)

我还怀疑你用其他东西从线性刻度切换rangeRound的输出.我明白了

var srr = d3.scaleLinear()
            .domain([0,3])   // 4 elements
            .rangeRound([0,399]);

[0,1,2,3].map(srr); // [0, 133, 266, 399]
Run Code Online (Sandbox Code Playgroud)

var srr2 = d3.scaleLinear()
             .domain([0,4])   // 4 intervals created with 5 endpoints
             .rangeRound([0,400]);
[0,1,2,3,4].map(srr2); // [0, 100, 200, 300, 400]
Run Code Online (Sandbox Code Playgroud)

输出看起来像我们的比例,带有50%填充的条形图(然后每个位置将是132像素的间隔的中点).我猜测原因是rangeRound使用round进行插值,而不是floor.

如果您想要间隔的宽度,也可以使用为条形图设计的函数.

 var sb = d3.scaleBand().padding(0).domain([0,1,2,3]).rangeRound([0,400]);
 [0,1,2,3].map(sb); // [0, 100, 200, 300]
 sb.bandwidth(); // 100
Run Code Online (Sandbox Code Playgroud)

并不是说任何一个都使代码更简单.

一旦我了解了您实现的功能,似乎要求更简单.真的没有任何间隔.问题是没有一对一的映射.最好的解决方案是你已经完成的工作,或者只使用两个线性刻度和自定义插补器(找到地板,而不是舍入.

var interpolateFloor = function (a,b) {
  return function (t) {
     return Math.floor(a * (1 - t) + b * t);
  };
}

var canvasSize = 400;
var sequenceLength = 4000;

var coordinateFromSequenceIndex = d3.scaleLinear()
             .domain([0, sequenceLength])  
             .range([0, canvasSize])
             .interpolate(interpolateFloor);

var seqIndexFromCoordinate = d3.scaleLinear()
             .domain([0, canvasSize ])  
             .range([0, sequenceLength])
             .interpolate(interpolateFloor);
Run Code Online (Sandbox Code Playgroud)