我有两个系列的比例,一个是线性的,另一个是波段,如果数据中有一些上限,我怎么能让它们匹配。
如有必要,请查看示例。 将鼠标移到上方,您会看到这些框与行中断不匹配。
答案 0 :(得分:3)
嗯,对你来说是坏消息:他们从不匹配(在你的情况下)。让我们看看为什么。
这是您的数据:
let testData1 = [
[1, 10],
[2, 30],
[8, 34],
[9, 26],
[13, 37],
[14, 12],
[20, 23],
[22, 16],
];
正如你所看到的,关于x坐标,线从1跳到2,然后从2跳到8,从8跳到9,然后从9跳到13 ......也就是说,x范围间隔是不规则,均匀分布。到目前为止,非常好。
然而,当你将相同的数据传递给波段时,它就是这样做的:它将范围([0, width]
,基本上是宽度)除以testData1.length
,即它将范围除以8,并创建8个相等的区间。那些是你的乐队,这是乐队规模的预期行为。来自documentation:
通过将连续范围除以均匀波段,自动计算离散输出值。 (强调我的)
这里的一个解决方案就是使用另一个线性刻度:
let xBand = d3.scaleLinear()
.domain(xDomain)
.rangeRound([0, width]);
这个数学到矩形的宽度:
.attr('width', (d,i) => testData1[i+1] ? xBand(testData1[i+1][0]) - xBand(d[0]) : 0)
以下是您更新的Codepen:http://codepen.io/anon/pen/MJdGyY?editors=0010
答案 1 :(得分:3)
如果您希望缩放(扩展)scaleBand
数据丢失,我不认为scaleBand
是正确的方法,但目前还不清楚是否是你想要的东西。带尺度旨在为每个数据值提供相等的间距,并且所有值都存在 - 它是一个有序的尺度。
假设您只希望将频段比例与存在的数据对齐:
如果您记录每个x标度(scaleBand
和scaleLinear
)的域名,我们会发现scaleBand
的域名为:
[ "1", "2", "8", "9", "13", "14", "20", "22" ] // 8 elements
scaleLinear
的域名为:
[ 1, 22 ] // a span of 22 'elements'
scaleBand
需要与scaleLinear
等效的域名。你可以静态地做这个(我主要是为了演示d3.range如何工作):
let xBand = d3.scaleBand()
.domain(d3.range(1,23))
.rangeRound([0, width]);
这实际上会生成一个包含22到22个元素的域。
或动态:
let xBand = d3.scaleBand()
.domain(d3.range(d3.min(testData1, d => d[0],
d3.max(testData1, d => d[0]+1)))
您可以通过其他方式执行此操作,但d3.range()
function is nice and easy。
然而,仍然存在一个问题,即这是两个尺度之间的对齐。对于线性刻度,第一个值(1)的刻度位于y轴上,但带隙刻度在y轴上开始(并且不居中)并填充1和换句话说,带的中心点不与线图的顶点垂直对齐。
这可以通过在线性标度域的下限和上限上加0.5来解决:
let xDomain = [
d3.min(testData1, d => d[0]-0.5),
d3.max(testData1, d => d[0]+0.5)
];
我已使用相关更改来编写您的codepen:codepen。
如果消失的话,这里有一个片段(鼠标悬停在片段中由于某种原因对我不起作用,它在codepen中有效)
let width = 1000;
let height = 300;
let svg = d3.select(".wrapper-area-simple").append("svg")
.attr("width", width + 80)
.attr("height", height + 80)
.append('svg:g')
.attr('transform', 'translate(40, 30)');
let testData1 = [
[ 1, 10],
[ 2, 30],
[ 8, 34],
[ 9, 26],
[13, 37],
[14, 12],
[20, 23],
[22, 16],
];
let xDomain = [
d3.min(testData1, d => d[0]-0.5),
d3.max(testData1, d => d[0]+0.5)
];
let x = d3.scaleLinear()
.rangeRound([0, width])
.domain(xDomain);
let y = d3.scaleLinear()
.range([height, 0])
.domain(d3.extent(testData1, d => d[1]));
let line = d3.line()
.x(d => x(d[0]))
.y(d => y(d[1]));
svg.append('svg:g')
.datum(testData1)
.append('svg:path')
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', '#000');
let xAxis = d3.axisBottom(x)
.ticks(testData1.length);
svg.append('svg:g')
.call(xAxis)
.attr('transform', `translate(0, 300)`);
let xBand = d3.scaleBand()
.domain(d3.range(d3.min(testData1, d => d[0]),
d3.max(testData1, d => d[0]+1)
))
.rangeRound([0, width]);
svg.append('svg:g')
.selectAll('rect')
.data(testData1)
.enter()
.append('svg:rect')
.attr('x', d => xBand(d[0]))
.attr('width', xBand.bandwidth())
.attr('height', height)
.attr('fill', '#000')
.on('mouseover', function() {
d3.select(this).classed('over', true);
})
.on('mouseout', function() {
d3.select(this).classed('over', false);
});

svg {
border: 1px solid red;
}
rect {
opacity: .1;
}
rect.over {
opacity: .2;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"> </script>
<div class="wrapper-area-simple"></div>
&#13;