I am using D3 to make a stacked bar chart (for more artistic purposes than scientific). I want to design my stacked bar chart to be centered around one group, with half above and half below an invisible line, and have the other two groups be on either side of the line.
Currently, my graph looks like this
But I want it to look more like this
My code is here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = { top: 40, right: 30, bottom: 20, left: 20 };
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
d3.csv("test.csv").then((data) => {
let x = d3
.scaleBand(
data.map((d) => d.Time),
[margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear([0, 500], [height - margin.bottom, margin.top]);
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = data.columns.slice(1);
const color = d3.scaleOrdinal(subgroups, [
"#e41a1c",
"#377eb8",
"#4daf4a",
]);
const stackedData = d3.stack().keys(subgroups)(data);
console.log(stackedData);
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
});
</script>
</body>
</html>
and csv:
Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188
Does anyone know how I could vertically center each line around the middle group? Is this something to do in the data pre-processing or in the graph making itself?