如何在 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>