I am trying to create small multiple bar charts that have different y-axis scales using d3 v6. There are a few examples out there (https://flowingdata.com/2014/10/15/linked-small-multiples/) of small multiples for previous versions of d3, but not v6, which seems to have a good number of changes implemented.
I don’t have much experience with d3, so I am probably missing something obvious, but I can’t get the bars to properly generate, the axes are generating (though I think I am generating them too many times on top of each other).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Small multiple bar charts</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<div id='vis'></div>
<script type="text/javascript">
// Set the sizing metrics
var width = 150;
var height = 120;
var margin = {top: 15, right: 10, bottom: 40, left: 35};
// Create the axes
var xScale = d3.scaleBand()
.range([0, width])
.padding(0.1);
var yScale = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(xScale);
// Load the data
d3.csv('data.csv').then(function(data) {
data.forEach(function(d) {
d.segment = d.segment;
d.parameter = d.parameter;
d.the_value = +d.the_value;
});
// set the x domain
xScale.domain(data.map(function(d) { return d.segment; }));
// group the data
metrics = Array.from(
d3.group(data, d => d.parameter), ([key, value]) => ({key, value})
);
// create a separate SVG object for each group
var svg = d3.select('#vis').selectAll('svg')
.data(metrics)
.enter()
.append('svg');
// loop over the data and create the bars
metrics.forEach(function(d, i) {
console.log(d);
console.log(metrics);
yScale.domain([0, d3.max(metrics, function(c) { return c.the_value; })]);
svg.selectAll('.bar')
.data(d)
.enter().append('rect')
.attr('class', 'bar')
.attr('x', function(c) { return xScale(c.segment); })
.attr('width', xScale.bandwidth())
.attr('y', function(c) { return yScale(c.the_value); })
.attr('height', function(c) { return height - yScale(c.the_value); })
.attr('fill', 'teal');
svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
});
});
</script>
</body>
</html>
Here is the data file:
segment,parameter,the_value
A,one,33
A,two,537723
A,three,14
A,four,5
A,five,0.093430759
B,one,76
B,two,137110
B,three,16
B,four,20
B,five,0.893868331
C,one,74
C,two,62020
C,three,25
C,four,14
C,five,0.862952872
Eventually I would also like to get the charts linked so that when series A is hovered on the first graph the value will display for each series on all of the graphs, but the first step is to get the visuals properly working.
There’s a few small changes to get it working:
When you set the
domain
on thex
scale, you just need the unique segments e.g. A, B, C and not the full list of segments from the data.When you create the 5 SVGs you can class them so that you can refer to each separately when you loop through the values of the metrics. So the first small multiple has a class of
one
, the second small multiple has a class oftwo
etcReset the
y
domain using the set ofthe_value
s from the metrics you’re charting – i.e. used
notmetrics
When you loop
metrics
firstselect
the small multiple for that metric and thenselectAll('.bar')
Pass
d.value
todata
as this makes the references toc.the_value
etc work properlyTo prevent adding the x axis multiple times, again
select
the SVG for the specific small multiple beforecall(xAxis)
otherwise you add as many axes as there are parameters to each small multiple.I faked up your data to include random data.
See the example below – maybe there’s a smarter way to do it: