Srikanta Srikanta - 11 days ago 5
Javascript Question

Incorrect axis domains in a D3 based bar graph

I'm trying to do a simple bar graph with y-axis having the Band scale and x-axis having a linear scale.

I'm facing the following issues -


  1. Unable to get the strings displayed along with the ticks on the
    Yaxis

  2. The domain of X axis is not in sync as per the data

  3. Not sure if the width of the rectangle is correct (might be getting inverted)

  4. Is it appropriate to use Band scale for the Y axis and not the Ordinal scale ?



I would appretiate any help pointing to the error or missing part. Thanks!

I have attached the code along with a JsFiddle link at the bottom.



function addBarGraph() {
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;

var x = d3.scaleLinear()
.rangeRound([0, width]);

var y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);

var xAxis = d3.axisBottom(x);

var yAxis = d3.axisLeft(y);

var svg = d3.select('#barGraph').append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var max = d3.max(data, function(datum) {
return datum.distribution;
});

svg.append("g")
.attr("class", "xAxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

svg.append("g")
.attr("class", "yAxis")
.call(yAxis);

data.forEach(function(d) {
d.distribution = +d.distribution;
});

y.domain(data.map(function(d) {
return d.nv;
}));
x.domain([max, 0]);

svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) {
return y(d.nv);
})
.attr("width", function(d) {
return width - x(d.distribution);
})
.attr("height", y.bandwidth());

}

function getData() {
data = JSON.parse('[{"nv":"Channel1","distribution":60},{"nv":"channel2","distribution":30}]');
}

$(document).ready(function() {
getData();
addBarGraph();
});

.bar {
fill: steelblue;
}

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="barGraph"></span>





Link: https://jsfiddle.net/6esyv0os/

Answer

Let's go step by step.

  1. Unable to get the strings displayed along with the ticks on the Yaxis

That's because you're appending the <g> elements for the axis before setting the y domain.

  1. The domain of X axis is not in sync as per the data

That's because the order in the array is inverted. It should be:

x.domain([0, max]);
  1. Not sure if the width of the rectangle is correct (might be getting inverted)

Yes, it's inverted. This is the correct function:

.attr("width", function (d) {
        return x(d.distribution);
    })
  1. Is it appropriate to use Band scale for the Y axis and not the Ordinal scale ?

A band scale is an ordinal scale. And yes, it's appropriate. Actually, for a bar chart (which is not an histogram), it's properly expected.

Here is your updated fiddle: https://jsfiddle.net/58fo16s9/

Even better than a fiddle, here is a SO snippet:

function addBarGraph() {
    var margin = {
            top: 20,
            right: 20,
            bottom: 30,
            left: 60
        },
        width = 500 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;

    var x = d3.scaleLinear()
        .rangeRound([0, width]);

    var y = d3.scaleBand()
        .rangeRound([height, 0]).padding(0.1);

    var xAxis = d3.axisBottom(x);

    var yAxis = d3.axisLeft(y).ticks();

    var svg = d3.select('body').append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var max = d3.max(data, function(datum) {
        return datum.distribution;
    });



    data.forEach(function(d) {
        d.distribution = +d.distribution;
    });


    y.domain(data.map(function(d) {
        return d.nv;
    }));
    x.domain([0, max]);



    svg.selectAll(".bar")
        .data(data)
        .enter().append("rect")
        .attr("fill", "steelblue")
        .attr("y", function(d) {
            return y(d.nv);
        })
        .attr("width", function(d) {
            return x(d.distribution);
        })
        .attr("height", y.bandwidth());

    svg.append("g")
        .attr("class", "xAxis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "yAxis")
        .call(yAxis);

}

function getData() {
    data = JSON.parse('[{"nv":"Channel1","distribution":60},{"nv":"Channel2","distribution":30}]');
}

getData();
addBarGraph();
<script src="https://d3js.org/d3.v4.min.js"></script>

Comments