Reputation: 1173
In this D3.js version 6.7 bar chart I am trying to align the x axis to show the categories and show the y axis to start at 0. Extending the height of the svg and changing the transforms does not appear to be working. How can I make the x axis categories appear under the bars and make the y axis start at 0? Thank you.
async function barChart() {
const dataset = await d3.csv("https://assets.codepen.io/417105/bodypart-injury-clean.csv");
console.log(dataset);
const width = 400;
const height = 400;
const margin = {top:20, right:30, bottom:30, left:40};
const canvas = d3.select("#viz")
.append("svg")
.attr("width", width)
.attr("height", height);
const wrapper = canvas.append("g").style("transform",`translate(${margin.left}px,${margin.top}px)`);
const xScale = d3.scaleBand()
.domain(["Arm","Eye","Head","Hand","Leg","Other"])
.range([0,width - margin.left])
.padding(0.2);
const yScale = d3.scaleLinear()
.domain(d3.extent(dataset, d => +d.Total))
.range([height,0]);
console.log(xScale("Leg"));
console.log(yScale(1700));
const barRect = wrapper.append("g")
.selectAll('rect')
.data(dataset)
.join('rect')
.attr('x', d => xScale(d.BodyRegion))
.attr('y', d => yScale(+d.Total))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(+d.Total))
.attr('fill', 'teal');
const yAxis = d3.axisLeft().scale(yScale);
wrapper.append("g").call(yAxis);
const xAxis = d3.axisBottom().scale(xScale);
wrapper.append("g").attr('transform', `translate(0,${height-margin.bottom})`).call(xAxis);
}
barChart();
Upvotes: 1
Views: 342
Reputation: 38201
The Y scale
The scale's domain sets the extent of the scale in your data's units, the scale's range sets the scale's extent in scaled units (pixels here). The first value in the domain is mapped to the first value in the range.
Your domain is set to:
.domain(d3.extent(dataset, d => +d.Total))
d3.extent returns the minimum and maximum matching values, as your minimum value is not zero, your scale's domain does not start at 0. If you want to set the scale's domain's lower bounds to zero, you need to set that, like so:
.domain([0,d3.max(dataset,d=> +d.Total)])
.domain/.range take arrays, these arrays for a linear scale must have the same number of elements
But you also don't want your scale's range to be [height,0] because of margins:
.range([height-margin.bottom,margin.top])
You want the data to be scaled from between within the two margins, height-margin.bottom
is the furthest down the page you want to plot data, and margin.top
is the furthest to the top of the SVG you want to plot data.
Now your bars are a bit off, that's because you aren't accounting for the margin in the height attribute:
.attr('height', d => height - yScale(+d.Total))
you need:
.attr('height', d => height - margin.bottom - yScale(+d.Total))
Note, a common approach to avoid having to constantly reference the margin is to apply the margin to a parent g
and have width
height
reflect the size of the plot area within that g
(not the entire SVG).
The X Axis
Now that the y scale is configured, let's look at the x axis. All you need to do here is boost the bottom margin: the text is appended (you can inspect the page to see it is there, just not visible). Try margin.bottom = 50.
Upvotes: 3