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

时间:2016-08-10 08:26:07

标签: javascript d3.js

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

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

0 -> 0-99
1 -> 100-199
2 -> 200-299
3 -> 300-399

反过来

0-99 -> 0
100-199 -> 1
200-299 -> 2
300-399 -> 3

当映射0-4000到400像素时,我希望

0-9 -> 0
10-19 -> 1
etc.

d3中使用的最佳比例是什么?

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

另一方面,线性比例如

d3.scaleLinear()
    .domain([0,399])   // 400 pixels
    .rangeRound([0,3])  // 4 elements

给了我

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

fiddle

因此内插器返回不等间隔(末尾较短)。

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

function coordinateFromSeqIndex(index, seqlength, canvasSize) {
     return Math.floor(index * (canvasSize / seqlength));
}
function seqIndexFromCoordinate(px, seqlength, canvasSize) {
    return Math.floor((seqlength/canvasSize) * px);
}

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

1 个答案:

答案 0 :(得分:5)

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

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

<html>
    <head>
    </head>
    <body>
        <div>
        </div>
        <p>
            <a>
            </a>
        </p>
    </body>
</html>

您还可以枚举离散值:

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)

这些是您已经给出的很好的值,并且由于您想要将整数映射到整数的间隔,我们将需要在数字不可分割时进行舍入。我们可以使用Math.round。

var interval = s.invertExtent(0);
d3.range(interval[0], interval[1]); // [0, 1, ... , 399]

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

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

我还怀疑你用其他东西从线性刻度切换了rangeRound的输出。我得到了

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

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

[0,1,2,3].map(srr); // [0, 133, 266, 399]

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

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

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]

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

一旦我开始实现您实现的功能,似乎要求更简单。真的没有涉及任何间隔。问题是没有一对一的映射。最好的解决方案是你所做的或者只是使用两个线性刻度和自定义插值器(找到地板,而不是四舍五入。

 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