javascript - D3 分组条形图不沿 x 比例分布
问题描述
我正在尝试按城市创建一个具有 3 个类别的分组条形图,但是它只绘制一个组,并且所有其他组都处于相同的位置,因此它们彼此重叠。它不会占用整个宽度,也不会沿 x 比例分布。我的 x0 和 x1 秤有问题。如果我理解正确,x0 是图表中矩形的开始位置,x1 刻度是矩形的结束位置?你能解释一下我在这里做错了什么吗?
这是问题的一个片段:
const groupedBarChartData = {
dataset: [
{
City: 'York',
'Sales': 20000,
'Returns': 25000,
'Total': 22000,
},
{
City: 'London',
'Sales': 40000,
'Returns': 43000,
'Total': 45000,
},
{
City: 'Manchester',
'Sales': 40000,
'Returns': 7000,
'Total': 19999,
}]}
// svg dimensions
const width = 1192;
const height = 610;
const colorScale = d3
.scaleOrdinal()
.range(['red', 'blue', 'green', 'orange']);
const svg = d3.select('body').append('svg');
// shape data
const data = groupedBarChartData.dataset;
// get categories
const keys = Object.keys(data[0]).slice(1);
// x axis
const x0Scale = d3.scaleBand().rangeRound([0, width]);
const x1Scale = d3.scaleBand();
// set x0 - cities
x0Scale.domain(data.map((d) => d['City']));
// set x1 - categories
x1Scale.domain(keys).rangeRound([0, x0Scale.bandwidth()]);
// y axis
const yScale = d3.scaleLinear().range([height, 0]);
// equal to Math.round(data.map((x) => Math.max(...keys.map((el) => x[el]))).sort((a, b) => b - a)[0])
yScale.domain([
0,
Math.round(d3.max(data, (d) => d3.max(keys, (key) => d[key]))),
]);
// draw chart
svg
.selectAll('rect')
.data(data)
.enter()
.selectAll('rect')
.data((d) =>
keys.map((key) => {
return { key: key, value: d[key] };
})
)
.enter()
.append('rect')
.attr('x', (d, i) => x1Scale(d.key))
.attr('y', (d) => yScale(d.value))
.attr('width', x1Scale.bandwidth() - 15)
.attr('height', (d) => height - yScale(d.value))
.attr('fill', (d) => colorScale(d.key));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
它给我的结果是:
解决方案
发生这种情况是因为您仅基于 x1Scale 定位矩形,这些位置仅基于类别:
.attr('x', (d, i) => x1Scale(d.key))
你没有根据城市定位。
最常见的解决方案是使用一个父级g
表示和定位父级城市,然后再去定位代表类别的条形:
svg
.selectAll('rect')
.data(data)
.enter()
.append("g")
.attr("transform", d=>`translate(${x0Scale(d.City)},0)`)
.selectAll('rect')
...
这给了我们:
const groupedBarChartData = {
dataset: [
{
City: 'York',
'Sales': 20000,
'Returns': 25000,
'Total': 22000,
},
{
City: 'London',
'Sales': 40000,
'Returns': 43000,
'Total': 45000,
},
{
City: 'Manchester',
'Sales': 40000,
'Returns': 7000,
'Total': 19999,
}]}
// svg dimensions
const width = 1192;
const height = 610;
const colorScale = d3
.scaleOrdinal()
.range(['red', 'blue', 'green', 'orange']);
const svg = d3.select('body').append('svg').attr("width",width).attr("height",height);
// shape data
const data = groupedBarChartData.dataset;
// get categories
const keys = Object.keys(data[0]).slice(1);
// x axis
const x0Scale = d3.scaleBand().rangeRound([0, width]);
const x1Scale = d3.scaleBand();
// set x0 - cities
x0Scale.domain(data.map((d) => d['City']));
// set x1 - categories
x1Scale.domain(keys).rangeRound([0, x0Scale.bandwidth()]);
// y axis
const yScale = d3.scaleLinear().range([height, 0]);
// equal to Math.round(data.map((x) => Math.max(...keys.map((el) => x[el]))).sort((a, b) => b - a)[0])
yScale.domain([
0,
Math.round(d3.max(data, (d) => d3.max(keys, (key) => d[key]))),
]);
// draw chart
svg
.selectAll('rect')
.data(data)
.enter()
.append("g")
.attr("transform", d=>`translate(${x0Scale(d.City)},0)`)
.selectAll('rect')
.data((d) =>
keys.map((key) => {
return { key: key, value: d[key]};
})
)
.enter()
.append('rect')
.attr('x', (d, i) => x1Scale(d.key))
.attr('y', (d) => yScale(d.value))
.attr('width', x1Scale.bandwidth() - 15)
.attr('height', (d) => height - yScale(d.value))
.attr('fill', (d) => colorScale(d.key));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
或者,如果您不想要嵌套元素,您可以将城市信息传递给每个条形图的基准,并使用它来定位条形图和类别:
.selectAll('rect')
.data((d) =>
keys.map((key) => {
return { key: key, value: d[key], city: d.City };
})
)
.enter()
.append('rect')
.attr('x', (d, i) => x1Scale(d.key)+x0Scale(d.city))
...
const groupedBarChartData = {
dataset: [
{
City: 'York',
'Sales': 20000,
'Returns': 25000,
'Total': 22000,
},
{
City: 'London',
'Sales': 40000,
'Returns': 43000,
'Total': 45000,
},
{
City: 'Manchester',
'Sales': 40000,
'Returns': 7000,
'Total': 19999,
}]}
// svg dimensions
const width = 1192;
const height = 610;
const colorScale = d3
.scaleOrdinal()
.range(['red', 'blue', 'green', 'orange']);
const svg = d3.select('body').append('svg').attr("width",width).attr("height",height);
// shape data
const data = groupedBarChartData.dataset;
// get categories
const keys = Object.keys(data[0]).slice(1);
// x axis
const x0Scale = d3.scaleBand().rangeRound([0, width]);
const x1Scale = d3.scaleBand();
// set x0 - cities
x0Scale.domain(data.map((d) => d['City']));
// set x1 - categories
x1Scale.domain(keys).rangeRound([0, x0Scale.bandwidth()]);
// y axis
const yScale = d3.scaleLinear().range([height, 0]);
// equal to Math.round(data.map((x) => Math.max(...keys.map((el) => x[el]))).sort((a, b) => b - a)[0])
yScale.domain([
0,
Math.round(d3.max(data, (d) => d3.max(keys, (key) => d[key]))),
]);
// draw chart
svg
.selectAll('rect')
.data(data)
.enter()
.selectAll('rect')
.data((d) =>
keys.map((key) => {
return { key: key, value: d[key], city: d.City };
})
)
.enter()
.append('rect')
.attr('x', (d, i) => x1Scale(d.key)+x0Scale(d.city))
.attr('y', (d) => yScale(d.value))
.attr('width', x1Scale.bandwidth() - 15)
.attr('height', (d) => height - yScale(d.value))
.attr('fill', (d) => colorScale(d.key));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
推荐阅读
- c# - 网站如何捕获服务引发的异常
- sencha-architect - Sencha Architect 4.2 - 如何无痛改变
in generated index.html</h1> <div id="body"><p>I'm working on a project that has gone through a name change. I need to change the <em><strong><title></strong></em> prope - python - 使用 Pythons Wikipedia 提取多个维基百科页面
- python-3.x - Flappy Bird 的管子不能正常工作
- java - 如何将图标添加到 android 中的日历组件?我想使用用户友好的图形来显示空闲日
- python - PySimpleGUI 将函数应用于数据框输入
- reactjs - Nextjs 嵌套动态路由
- flutter - 无法在初始化程序中访问实例成员“_showSelectImageDialog”
- java - 客户端传输异常,但似乎还可以
- mongodb - 由于无法验证开发者,无法打开《机械人 3T》