Я не вижу простого пути. Вы должны поиграть со смещениями по оси x.
вот мой пример. Надеюсь, это поможет ...
const data = [
{
"model_name":"f1",
"field1":19,
"field2":83,
"field3":100,
},
{
"model_name":"f2",
"field1":67,
},
{
"model_name":"f3",
"field1":10,
"field3":100,
},
{
"model_name":"f4",
"field1":98,
"field2":43,
"field3":100,
}
],
all_keys = ["field1", "field2", "field3"]
const container = d3.select('#bar_cont'),
width = 500,
height = 300,
margin = {top: 30, right: 20, bottom: 30, left: 50},
barPadding = .2
const svg = container
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const xScale0 = d3.scaleBand().range([0, width - margin.left - margin.right]).padding(barPadding);
const yScale = d3.scaleLinear().range([height - margin.top - margin.bottom, 0]);
const xAxis = d3.axisBottom(xScale0)
const yAxis = d3.axisLeft(yScale)
xScale0.domain(data.map(d => d.model_name));
yScale.domain([0, d3.max(data, d => d3.max(all_keys, k => d[k]))]);
svg.selectAll(".groups")
.data(data)
.enter().append("g")
.attr("class", "model_name")
.each(function (group_data) {
const bar_data = getBars(group_data),
x_offset = groupOffset(group_data),
group_keys = bar_data.map(d => d.name)
xScale1 = d3.scaleBand()
.domain(group_keys)
.range([0, xScale0.bandwidth() - x_offset*2])
.padding(barPadding);
d3.select(this)
.attr("transform", d => `translate(${xScale0(d.model_name) + x_offset},0)`)
.selectAll(".bar")
.data(bar_data)
.enter()
.append("rect")
.attr("class", "bar")
.style("fill","blue")
.attr("x", d => xScale1(d.name))
.attr("y", d => yScale(d.value))
.attr("width", xScale1.bandwidth())
.attr("height", d => {
return height - margin.top - margin.bottom - yScale(d.value)
});
})
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${height - margin.top - margin.bottom})`)
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function getBars(d) {
const bars = [];
all_keys.forEach(k => {
if (d.hasOwnProperty(k)) bars.push({name:k, value: d[k], data: d})
})
return bars
}
function groupOffset(d) {
const groupElementsCount = all_keys.reduce((acc, k) => (d.hasOwnProperty(k) ? acc+=1 : acc), 0),
allElementsCount = all_keys.length,
groupWidth = xScale0.bandwidth(),
x_offset = (1 - (groupElementsCount / allElementsCount)) * groupWidth / 2
return x_offset
}
<div id="bar_cont"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>