Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
229 views
in Technique[技术] by (71.8m points)

javascript - Wrong usage of d3 scale with same values in data?

I'm new to d3 and using it for creating a simple chart using array of numbers where the value '16' appears twice in it.

It generate the chart with one 'missing' 'rect' element for the 2nd '16' value, when I check the html I see that both '16' rect has same 'y' value of 72.

Please tell me what I'm doing wrong, thanks

code:

var data = [4, 8, 15, 16, 23, 16];

var chart = d3.select("body").append("svg")
     .attr("class", "chart")
     .attr("width", 420)
     .attr("height", 20 * data.length);


var x = d3.scale.linear()
     .domain([0, d3.max(data)])
     .range([0, 420])

var y = d3.scale.ordinal()
     .domain(data)
     .rangeBands([0, 120]);

chart.selectAll("rect")
     .data(data)
     .enter().append("rect")
     .attr("y", y)
     .attr("width", x)
     .attr("height", y.rangeBand());
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem with your code is that you are trying to use the values from your data array to create range bands on an ordinal scale. Since the same input value will always be mapped to the same output value that means that both inputs 16 get mapped to the same range band 72.

If you want each input value to be mapped to its own "bar" then you need to use array indices instead of array values.

First you prepare the indices

var indices = d3.range(0, data.length);    // [0, 1, 2, ..., data.length-1]

Then you use them to define the y scale domain

var y = d3.scale.ordinal()
    .domain(indices)
    // use rangeRoundBands instead of rangeBands when your output units
    // are pixels (or integers) if you want to avoid empty line artifacts
    .rangeRoundBands([0, chartHeight]);  

Finally, instead of using array values as inputs use array indices when mapping to y

chart.selectAll("rect")
    .data(data)
    .enter().append("rect")
    .attr("y", function (value, index) { 
        // use the index as input instead of value; y(index) instead of y(value)
        return y(index); 
    })
    .attr("width", x)
    .attr("height", y.rangeBand());

As an added bonus this code will automatically rescale the chart if the amount of data changes or if you decide to change the chart width or height.

Here's a jsFiddle demo: http://jsfiddle.net/q8SBN/1/

Complete code:

var data = [4, 8, 15, 16, 23, 16];
var indices = d3.range(0, data.length);

var chartWidth = 420;
var chartHeight = 120;

var chart = d3.select("body").append("svg")
     .attr("class", "chart")
     .attr("width", chartWidth)
     .attr("height", chartHeight);

var x = d3.scale.linear()
     .domain([0, d3.max(data)])
     .range([0, chartWidth])

var y = d3.scale.ordinal()
     .domain(indices)
     .rangeRoundBands([0, chartHeight]);

chart.selectAll("rect")
    .data(data)
    .enter().append("rect")
    .attr("y", function (value, index) { return y(index); })
    .attr("width", x)
    .attr("height", y.rangeBand());

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...