I'm using a D3.js histogram layout, and the x axis text labels are not aligning to their proper positions directly underneath their respective bar. They seem to be running on different scales, but I haven't been able to figure out why they have different intervals. The y axis text labels match up to the proper height of the bars, but the x axis text labels are at different positions from the widths of the bars. Why might this be?
Thank you in advance!
<html>
<head>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/d3-random.v0.2.min.js"></script>
<style>
html,body{margin:0;padding:0;}
body {
background-color: white;
font-family: Arial;
width: 100%;
}
.title, .axis-text {
font-weight: bold;
}
.axis {
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<svg id="histogram">
</svg>
<script>
function plotHistogram(id, numArray){
//Initial variables
var height = 600;
var width = 600;
var margin = 60;
//Format our data into bins
var bins = d3.layout.histogram()
.bins(numArray.length/5)(numArray);
//Select our SVG and set its attributes
var svg = d3.select(id)
.attr("height", height)
.attr("width", width);
//Create and format x scale
var xScale = d3.scale.linear()
.domain(d3.extent(numArray))
.range([margin, width-margin]);
//Create and format y scale
var yScale = d3.scale.linear()
.domain([0,d3.max(bins, function (bin) {
return bin.y;
})])
.range([height - margin, margin]);
//Create and format x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0,"+(height-margin)+")")
.call(xAxis);
svg.append("text")
.attr("text-anchor","middle")
.attr("x",width/2)
.attr("y",height-margin/3)
.text("Data");
//Create and format y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("transform", "translate(" + margin + ",0)")
.attr("class", "axis")
.call(yAxis);
svg.append("text")
.attr("text-anchor","middle")
.attr("transform","translate("+(margin/3)+","+(height/2)+")rotate(-90)")
.text("Frequency");
//For every element of our bins variable, create a new rectangle for it
var randColors = ["#5c5e58","#af6c6f","#c56b18","#97aedc","#5e8477","#9d291f","#2d3361","#55775a"];
bins.forEach(function (curBin) {
//console.log(curBin);
svg.append("rect")
.attr("class", "bar")
.attr("fill", function() { return randColors[(Math.floor(Math.random() * randColors.length))]})
.attr("height", yScale(0) - yScale(curBin.y))
.attr("stroke","white")
.attr("width", xScale(curBin.dx)-xScale(0))
.attr("x", xScale(curBin.x))
.attr("y", yScale(curBin.y))
});
}
var randomNumbers = [3, 3, 4, 19, 14, 14, 8, 3, 5, 9,
8, 5, 18, 15, 1, 6, 18, 20, 6, 19,
14, 1, 10, 14, 6, 2, 19, 3, 20, 1,
2, 11, 9, 6, 14, 15, 11, 5, 19, 5,
17, 16, 9, 12, 19, 13, 20, 20, 13, 6];
plotHistogram("#histogram",randomNumbers);
</script>
</body>
</html>
Your data values were in the range from 1 to 20. Your code was creating 10 bins over that range. Each bin had a dx = 1.9 = (20 - 1) / 10. This result in bin thresholds of 1, 2.9, 4.8, 6.7, ..., 18.1, 20. Your code was resulting in tick values of 2, 4, 6, ..., 18, 20. Since bin thresholds were spaced at intervals of 1.9 and the tick values were spaced intervals of 2.0, the bars did not align with the tick marks.
You could use the following code to create tick marks at the right edge of each bar.