blaa blaa - 26 days ago 17
Javascript Question

D3: The data on the axis not mapping

I have tried to implement a bar chart in D3, but I have a problem with the mapping on my axes. I have a dataset with x and y but it seems that my values are not mapped correctly.

This is the code:

var data = [
{x: 2, y: 4},
{x: 5, y: 8},
{x: 8, y: 10},
{x: 2, y: 4},
{x: 5, y: 8},
{x: 8, y: 19},
{x: 2, y: 4},
{x: 5, y: 8},
{x: 8, y: 10},
{x: 2, y: 4},
{x: 5, y: 8},
{x: 8, y: 10},
{x: 2, y: 4},
{x: 5, y: 8},
{x: 8, y: 10},
{x: 2, y: 4},
{x: 3, y: 8},
{x: 8, y: 10},
{x: 24, y: 4},
{x: 15, y: 8},
{x: 18, y: 10}
];


var margin = {top: 20, right: 30, bottom: 30, left: 40},
w = 1000 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;

var x = d3.scale.linear()
.range([0, w]);

var y = d3.scale.linear()
.range([h, 0]);

var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//.tickFormat(d3.format("%Y/%m"));

var yAxis = d3.svg.axis()
.scale(y)
.orient("left");

var zoom = d3.behavior.zoom()
.scaleExtent([1, 1])
.x(x)
.on("zoom", zoomed);

var chart = d3.select("#testChart").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);

var rect = chart.append("rect")
.attr("width", w)
.attr("height", h)
.style("fill", "none")
.style("pointer-events", "all");

chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)



chart.append("g")
.attr("class", "y axis")
.call(yAxis);

//data.forEach(function (d) {
//d.date = new Date("20" + d.yy + "/" + d.mm);
// coerce to number
//d.ppm_value = +d.ppm_value;
// d.date = format.parse(d.date);
//console.log(d.ppm_value);
// console.log(d.date);
//});

x.domain(data.map(function (d) {
console.log("HEelllppp!!!!",d.date);
return d.x;
}));
y.domain([0, d3.max(data, function (d) {
return d.y;
})]);

var bars = chart.append("g")
.attr("class", "chartobjects");

bars.selectAll(".rect")
.data(data)
.enter().append("rect")
.attr("class", "rectBar")
.on("click", hello)
.attr('x', function (d) {
console.log("Blaaannaaaaa!!!!!!",d.x);
return x(d.x);
})
.attr("y", function (d) {
return y(d.y);
})
.attr("height", function(d) { return h - y(d.y); })
.attr("width", 45)
.attr("fill", function(d){
return d.y > 6 ? "blue" : "red"});

function hello(){
alert("Hello world!!")
}

function zoomed() {
var tx = Math.max(0, d3.event.translate[0]);
//ty = Math.min(0, d3.event.translate[1]);
zoom.translate([tx]);
bars.attr("transform", "translate(" + [tx] + ")scale(" + d3.event.scale + ")");
chart.select(".x.axis")
.call(xAxis);
//chart.select(".y.axis")
// .call(yAxis);
}


Myabe you have some ideas why.I will also post a picture:

enter image description here

Answer

The explanation here has to do more with the dataviz principles than with D3 code.

A bar chart is different from an histogram, and the main difference is this: in a bar chart, each bar represents a qualitative variable (or a categorical variable). That being said, you shouldn't use a linear scale for your x axis, but an ordinal one instead:

var x = d3.scale.ordinal()
    .rangeBands([0, w]);

Here is a demo with an ordinal scale:

var data = [{
    x: 2,
    y: 4
}, {
    x: 5,
    y: 8
}, {
    x: 8,
    y: 10
}, {
    x: 2,
    y: 4
}, {
    x: 5,
    y: 8
}, {
    x: 8,
    y: 19
}, {
    x: 2,
    y: 4
}, {
    x: 5,
    y: 8
}, {
    x: 8,
    y: 10
}, {
    x: 2,
    y: 4
}, {
    x: 5,
    y: 8
}, {
    x: 8,
    y: 10
}, {
    x: 2,
    y: 4
}, {
    x: 5,
    y: 8
}, {
    x: 8,
    y: 10
}, {
    x: 2,
    y: 4
}, {
    x: 3,
    y: 8
}, {
    x: 8,
    y: 10
}, {
    x: 24,
    y: 4
}, {
    x: 15,
    y: 8
}, {
    x: 18,
    y: 10
}];


var margin = {
        top: 20,
        right: 30,
        bottom: 30,
        left: 40
    },
    w = 1000 - margin.left - margin.right,
    h = 500 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .rangeBands([0, w], 0.6);

var y = d3.scale.linear()
    .range([h, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");
//.tickFormat(d3.format("%Y/%m"));

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

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

var rect = chart.append("rect")
    .attr("width", w)
    .attr("height", h)
    .style("fill", "none")
    .style("pointer-events", "all");



//data.forEach(function (d) {
//d.date = new Date("20" + d.yy + "/" + d.mm);
// coerce to number
//d.ppm_value = +d.ppm_value;
// d.date = format.parse(d.date);
//console.log(d.ppm_value);
// console.log(d.date);
//});

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

chart.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + h + ")")
    .call(xAxis)

chart.append("g")
    .attr("class", "y axis")
    .call(yAxis);

var bars = chart.append("g")
    .attr("class", "chartobjects");

bars.selectAll(".rect")
    .data(data)
    .enter().append("rect")
    .attr("class", "rectBar")
    .on("click", hello)
    .attr('x', function(d) {
        return x(d.x);
    })
    .attr("y", function(d) {
        return y(d.y);
    })
    .attr("height", function(d) {
        return h - y(d.y);
    })
    .attr("width", x.rangeBand())
    .attr("fill", function(d) {
        return d.y > 6 ? "blue" : "red"
    });

function hello() {
    alert("Hello world!!")
}

function zoomed() {
    var tx = Math.max(0, d3.event.translate[0]);
    //ty = Math.min(0, d3.event.translate[1]);
    zoom.translate([tx]);
    bars.attr("transform", "translate(" + [tx] + ")scale(" + d3.event.scale + ")");
    chart.select(".x.axis")
        .call(xAxis);
    //chart.select(".y.axis")
    //  .call(yAxis);
}
.axis path,.axis line {
			fill: none;
			stroke: black;
			shape-rendering: crispEdges;
		}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

But if you want to keep your linear scale and positioning the bars according to the x value in the dataset, dont use map, which returns an array with all x values: simply set the domain to [minValue, maxValue], whatever those values are.

Comments