如何在 D3.js 版本 5 的 xScale 的不同列中显示 X 轴上的不同列元素和组
How to display different column elements on X-Axis and Group in different column for xScale in D3.js Version 5
我们使用的数据有 2 个主要类别列,即 ID 和 Desc。例如Region_ID、Region_Desc.
在代码的前面,我使用 Region_Desc 将其分组在 xScale 中并在 X 轴中显示。
但是当下划线数据具有重复的描述但具有不同的 ID(无论出于何种原因)时会遇到问题。
因此,为了解决这个问题,我想使用 Region_ID 在 xScale 中对数据进行分组,但在 X- 上显示 Region_Desc轴.
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
并在 X 轴上显示:
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_Desc) + xScale.bandwidth() / 2}, 0)`);
我无法达到同样的效果。我该怎么做?
已编辑
以下是所提供数据中所需图表的示例屏幕截图:
代码如下:
const barData = [{
"Region_ID":1,
"Region_Desc": "Region1",
"Value": 538865
},
{
"Region_ID":2,
"Region_Desc": "Region2",
"Value": 645375
},
{
"Region_ID":3,
"Region_Desc": "Region3",
"Value": 434535
},
{
"Region_ID":4,
"Region_Desc": "Region4",
"Value": 734595
},
{
"Region_ID": 5,
"Region_Desc": "Region5",
"Value": 834545
},
{
"Region_ID": 6,
"Region_Desc": "Region6",
"Value": 534555
},
{
"Region_ID": 7,
"Region_Desc": "Region1",
"Value": 634565
},
{
"Region_ID":8,
"Region_Desc": "Region2",
"Value": 33455
}
];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
const defs = svg.append("defs");
const marker = defs.append("marker")
.attr("id","arrowhead")
.attr("markerWidth","10")
.attr("markerHeight","7")
.attr("refX","0")
.attr("refY","3.5")
.attr("orient","auto")
const polygon = marker.append("polygon")
.attr("fill","gray")
.attr("points","0 0, 10 3.5, 0 7")
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale);
//Adding g attribute to svg for x axis
svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_Desc) + xScale.bandwidth() / 2}, 0)`);
/*
const staticColor = "steelblue",
highlightColor = "orange";
var sheet = document.createElement('style')
sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";
document.body.appendChild(sheet);
*/
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
//.attr("class", "bar")
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Region_Desc) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
//d3.select(this).attr('class', 'bar');
//this.setState({ fillColour: 'blue' });
}
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5)
;
bars.filter((d, i) => i < barData.length - 1)
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i + 1].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
bars.filter((d, i) => i < barData.length - 1)
.append('rect')
.attr('x', 15)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70)
.attr('width', xScale.bandwidth() - 30)
.attr('height', 20)
.attr('rx', 10)
.style('fill', 'white')
.style('stroke', 'gray');
bars.filter((d, i) => i < barData.length - 1)
.append('text')
.text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
.attr('x', xScale.bandwidth() / 2)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56)
.attr('text-anchor', 'middle')
.style('fill', 'black');
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>
经过多次google搜索和试用。最后,我找到了解决我所面临问题的方法。
下面是我使用的代码:
var xAxisData = [];
for(i=0;i<barData.length;i++)
{
xAxisData.push(barData[i].Region_Desc);
//console.log(barData[i].Region_Desc);
}
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale)
.tickFormat((d,i) => xAxisData[i]);
下面是完整的工作代码:
const barData = [{
"Region_ID":1,
"Region_Desc": "Region1",
"Value": 538869
},
{
"Region_ID":2,
"Region_Desc": "Region2",
"Value": 645375
},
{
"Region_ID":3,
"Region_Desc": "Region3",
"Value": 434535
},
{
"Region_ID":4,
"Region_Desc": "Region4",
"Value": 734595
},
{
"Region_ID": 5,
"Region_Desc": "Region5",
"Value": 834545
},
{
"Region_ID": 6,
"Region_Desc": "Region6",
"Value": 534555
},
{
"Region_ID": 7,
"Region_Desc": "Region1",
"Value": 634565
},
{
"Region_ID":8,
"Region_Desc": "Region2",
"Value": 33459
}
];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
var xAxisData = [];
for(i=0;i<barData.length;i++)
{
xAxisData.push(barData[i].Region_Desc);
//console.log(barData[i].Region_Desc);
}
//console.log(xAxisData);
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale)
.tickFormat((d,i) => xAxisData[i]);
//Adding g attribute to svg for x axis
const xAxisG = svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
/* xAxisG.selectAll('.tick').each(function() {
const tick = d3.select(this);
const text = tick.select('text').text();
//const data = barData.find(d => d.Region_ID === text);
tick.select('text').style('fill', 'black');
tick.select('text').attr('value',d => d.Region_Desc)
//tick.select('text').attr('y',-15)
//tick.select('line').style('stroke', 'white');
//tick.select('text').style('dy',height/2 -10);
})
*/
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_ID) + xScale.bandwidth() / 2}, 0)`);
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
//.attr("class", "bar")
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Region_Desc) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
//d3.select(this).attr('class', 'bar');
//this.setState({ fillColour: 'blue' });
}
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>
我们使用的数据有 2 个主要类别列,即 ID 和 Desc。例如Region_ID、Region_Desc.
在代码的前面,我使用 Region_Desc 将其分组在 xScale 中并在 X 轴中显示。
但是当下划线数据具有重复的描述但具有不同的 ID(无论出于何种原因)时会遇到问题。 因此,为了解决这个问题,我想使用 Region_ID 在 xScale 中对数据进行分组,但在 X- 上显示 Region_Desc轴.
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
并在 X 轴上显示:
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_Desc) + xScale.bandwidth() / 2}, 0)`);
我无法达到同样的效果。我该怎么做?
已编辑
以下是所提供数据中所需图表的示例屏幕截图:
代码如下:
const barData = [{
"Region_ID":1,
"Region_Desc": "Region1",
"Value": 538865
},
{
"Region_ID":2,
"Region_Desc": "Region2",
"Value": 645375
},
{
"Region_ID":3,
"Region_Desc": "Region3",
"Value": 434535
},
{
"Region_ID":4,
"Region_Desc": "Region4",
"Value": 734595
},
{
"Region_ID": 5,
"Region_Desc": "Region5",
"Value": 834545
},
{
"Region_ID": 6,
"Region_Desc": "Region6",
"Value": 534555
},
{
"Region_ID": 7,
"Region_Desc": "Region1",
"Value": 634565
},
{
"Region_ID":8,
"Region_Desc": "Region2",
"Value": 33455
}
];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
const defs = svg.append("defs");
const marker = defs.append("marker")
.attr("id","arrowhead")
.attr("markerWidth","10")
.attr("markerHeight","7")
.attr("refX","0")
.attr("refY","3.5")
.attr("orient","auto")
const polygon = marker.append("polygon")
.attr("fill","gray")
.attr("points","0 0, 10 3.5, 0 7")
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale);
//Adding g attribute to svg for x axis
svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_Desc) + xScale.bandwidth() / 2}, 0)`);
/*
const staticColor = "steelblue",
highlightColor = "orange";
var sheet = document.createElement('style')
sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";
document.body.appendChild(sheet);
*/
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
//.attr("class", "bar")
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Region_Desc) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
//d3.select(this).attr('class', 'bar');
//this.setState({ fillColour: 'blue' });
}
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5)
;
bars.filter((d, i) => i < barData.length - 1)
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i + 1].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
bars.filter((d, i) => i < barData.length - 1)
.append('rect')
.attr('x', 15)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70)
.attr('width', xScale.bandwidth() - 30)
.attr('height', 20)
.attr('rx', 10)
.style('fill', 'white')
.style('stroke', 'gray');
bars.filter((d, i) => i < barData.length - 1)
.append('text')
.text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
.attr('x', xScale.bandwidth() / 2)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56)
.attr('text-anchor', 'middle')
.style('fill', 'black');
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>
经过多次google搜索和试用。最后,我找到了解决我所面临问题的方法。 下面是我使用的代码:
var xAxisData = [];
for(i=0;i<barData.length;i++)
{
xAxisData.push(barData[i].Region_Desc);
//console.log(barData[i].Region_Desc);
}
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale)
.tickFormat((d,i) => xAxisData[i]);
下面是完整的工作代码:
const barData = [{
"Region_ID":1,
"Region_Desc": "Region1",
"Value": 538869
},
{
"Region_ID":2,
"Region_Desc": "Region2",
"Value": 645375
},
{
"Region_ID":3,
"Region_Desc": "Region3",
"Value": 434535
},
{
"Region_ID":4,
"Region_Desc": "Region4",
"Value": 734595
},
{
"Region_ID": 5,
"Region_Desc": "Region5",
"Value": 834545
},
{
"Region_ID": 6,
"Region_Desc": "Region6",
"Value": 534555
},
{
"Region_ID": 7,
"Region_Desc": "Region1",
"Value": 634565
},
{
"Region_ID":8,
"Region_Desc": "Region2",
"Value": 33459
}
];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
var xAxisData = [];
for(i=0;i<barData.length;i++)
{
xAxisData.push(barData[i].Region_Desc);
//console.log(barData[i].Region_Desc);
}
//console.log(xAxisData);
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Region_ID))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale)
.tickFormat((d,i) => xAxisData[i]);
//Adding g attribute to svg for x axis
const xAxisG = svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
/* xAxisG.selectAll('.tick').each(function() {
const tick = d3.select(this);
const text = tick.select('text').text();
//const data = barData.find(d => d.Region_ID === text);
tick.select('text').style('fill', 'black');
tick.select('text').attr('value',d => d.Region_Desc)
//tick.select('text').attr('y',-15)
//tick.select('line').style('stroke', 'white');
//tick.select('text').style('dy',height/2 -10);
})
*/
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Region_ID) + xScale.bandwidth() / 2}, 0)`);
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
//.attr("class", "bar")
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Region_Desc) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
//d3.select(this).attr('class', 'bar');
//this.setState({ fillColour: 'blue' });
}
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>