x-axis text labels not aligned with bars in D3.js

2019-07-27 06:39发布

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>

1条回答
▲ chillily
2楼-- · 2019-07-27 07:12

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.

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .tickValues(bins.map(function (bin) { return bin.x + bin.dx; }))
    .tickFormat(d3.format(".1f"));
查看更多
登录 后发表回答