简体   繁体   English

D3 分组条形图来自 Arrays

[英]D3 Grouped Bar Chart From Arrays

I'm really having trouble with D3 and need some help changing my existing barchart to be a grouped barchart The barchart is being used within a tooltip and currently looks like:我真的在使用 D3 时遇到了问题,需要一些帮助,将我现有的条形图更改为分组条形图 条形图正在工具提示中使用,目前看起来像: 在此处输入图像描述

Each colour represents a sector of industry (pink = retail, teal = groceries...etc).每种颜色代表一个行业部门(粉红色 = 零售,蓝绿色 = 杂货......等)。 I need to change the bar chart so that it compares the percentage change in each industry with the world average percentage change in this industry.我需要更改条形图,以便将每个行业的百分比变化与该行业的世界平均百分比变化进行比较。

At the moment the bar chart is being created from an array of data.目前正在从一组数据创建条形图。 I also have an array with the world percentage values.我还有一个包含世界百分比值的数组。

So imagine:所以想象一下:

countryData = [10,-20,-30,-63,-23,20], worldData = [23,-40,-23,-42,-23,40] countryData = [10,-20,-30,-63,-23,20],worldData = [23,-40,-23,-42,-23,40]

Where index 0 = retail sector, index 1 = grocery sector, etc.其中指数 0 = 零售业,指数 1 = 杂货业等。

I need to plot a grouped barchart comparing each sector to the world average (show the world average in red).我需要 plot 将每个部门与世界平均水平进行比较的分组条形图(以红色显示世界平均水平)。 This is a bit tricky to explain so I drew it for you (...excuse the shoddy drawing).这有点难以解释,所以我为您画了它(......请原谅伪劣的图)。

在此处输入图像描述

Please can someone help me change my existing tooltip?请问有人可以帮我更改现有的工具提示吗? Here's the current code.这是当前代码。 If you want to simulate the data values changing.如果要模拟数据值的变化。 If you want to scrap my existing code that's fine.如果你想废弃我现有的代码,那很好。

 .on('mouseover', ({ properties }) => {
        // get county data
        const mobilityData = covid.data[properties[key]] || {};


      const {
        retailAverage,
        groceryAverage,
        parksAverage,
        transitAverage,
        workplaceAverage,
        residentialAverage,
      } = getAverage(covid1);



      let avgArray = [retailAverage, groceryAverage, parksAverage, transitAverage, workplaceAverage, retailAverage];
      let categoriesNames = ["Retail", "Grocery", "Parks", "Transit", "Workplaces", "Residential"];




        // create tooltip
        div = d3.select('body')
          .append('div')
          .attr('class', 'tooltip')
          .style('opacity', 0);

        div.html(properties[key]);

        div.transition()
          .duration(200)
          .style('opacity', 0.9);

        // calculate bar graph data for tooltip
        const barData = [];

        Object.keys(mobilityData).forEach((industry) => {
          const stringMinusPercentage = mobilityData[industry].slice(0, -1);
          barData.push(+stringMinusPercentage); // changing it to an integer value, from string
        });

        //combine the two lists for the combined bar graph
        var combinedList = [];
        for(var i = 0; i < barData.length; i++) {
          const stringMinusPercentage2 = +(avgArray[i].slice(0, -1));
          const object = {category: categoriesNames[i], country: barData[i], world: stringMinusPercentage2}
          combinedList.push(object); //Push object into list
        }
        console.log(combinedList);

        // barData = barData.sort(function (a, b) {  return a - b;  });
        // sort into ascending ^ keeping this in case we need it later
        const height2 = 220;
        const width2 = 250;
        const margin = {
          left: 50, right: 10, top: 20, bottom: 15,
        };

        // create bar chart svg
        const svgA = div.append('svg')
          .attr('height', height2)
          .attr('width', width2)
          .style('border', '1px solid')
          .append('g')
        // apply the margins:
          .attr('transform', `translate(${[`${margin.left},${margin.top}`]})`);

        const barWidth = 30; // Width of the bars

        // plot area is height - vertical margins.
        const chartHeight = height2 - margin.top - margin.left;

        // set the scale:
        const yScale = d3.scaleLinear()
          .domain([-100, 100])
          .range([chartHeight, 0]);

        // draw some rectangles:
        svgA
          .selectAll('rect')
          .data(barData)
          .enter()
          .append('rect')
          .attr('x', (d, i) => i * barWidth)
          .attr('y', (d) => {
            if (d < 0) {
              return yScale(0); // if the value is under zero, the top of the bar is at yScale(0);
            }

            return yScale(d); // otherwise the rectangle top is above yScale(0) at yScale(d);
          })
          .attr('height', (d) => Math.abs(yScale(0) - yScale(d))) // the height of the rectangle is the difference between the scale value and yScale(0);
          .attr('width', barWidth)
          .style('fill', (d, i) => colours[i % 6]) // colour the bars depending on index
          .style('stroke', 'black')
          .style('stroke-width', '1px');

        // Labelling the Y axis
        const yAxis = d3.axisLeft(yScale);
        svgA.append('text')
          .attr('class', 'y label')
          .attr('text-anchor', 'end')
          .attr('x', -15)
          .attr('y', -25)
          .attr('dy', '-.75em')
          .attr('transform', 'rotate(-90)')
          .text('Percentage Change (%)');

        svgA.append('g')
          .call(yAxis);
      })
      .on('mouseout', () => {
        div.style('opacity', 0);
        div.remove();
      })
      .on('mousemove', () => div
        .style('top', `${d3.event.pageY - 140}px`)
        .style('left', `${d3.event.pageX + 15}px`));

    svg.append('g')
      .attr('transform', 'translate(25,25)')
      .call(colorLegend, {
        colorScale,
        circleRadius: 10,
        spacing: 30,
        textOffset: 20,
      });

  };

  drawMap(svg1, geoJson1, geoPath1, covid1, key1, 'impact1');
  drawMap(svg2, geoJson2, geoPath2, covid2, key2, 'impact2');
};

In short I would suggest you to use two Band Scales for x axis.简而言之,我建议您对 x 轴使用两个波段刻度。 I've attached a code snippet showing the solution.我附上了一个显示解决方案的代码片段。 Enjoy;)享受;)

 //Assuming the following data final format var finalData = [ { "groupKey": "Retail", "sectorValue": 70, "worldValue": 60 }, { "groupKey": "Grocery", "sectorValue": 90, "worldValue": 90 }, { "groupKey": "other", "sectorValue": -20, "worldValue": 30 } ]; var colorRange = d3.scaleOrdinal().range(["#00BCD4", "#FFC400", "#ECEFF1"]); var subGroupKeys = ["sectorValue", "worldValue"]; var svg = d3.select("svg"); var margin = {top: 20, right: 20, bottom: 30, left: 40}; var width = +svg.attr("width") - margin.left - margin.right; var height = +svg.attr("height") - margin.top - margin.bottom; var container = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // The scale spacing the groups, your "sectors": var x0 = d3.scaleBand().domain(finalData.map(d => d.groupKey)).rangeRound([0, width]).paddingInner(0.1); // The scale for spacing each group's bar, your "sector bar": var x1 = d3.scaleBand().domain(subGroupKeys).rangeRound([0, x0.bandwidth()]).padding(0.05); var yScale = d3.scaleLinear().domain([-100, 100]).rangeRound([height, 0]); //and then you will need to append both, groups and bars var groups = container.append('g').selectAll('g').data(finalData, d => d.groupKey).join("g").attr("transform", (d) => "translate(" + x0(d.groupKey) + ",0)"); //define groups bars, one per sub group var bars = groups.selectAll("rect").data(d => subGroupKeys.map(key => ({ key, value: d[key], groupKey: d.groupKey })), (d) => "" + d.groupKey + "_" + d.key).join("rect").attr("fill", d => colorRange(d.key)).attr("x", d => x1(d.key)).attr("width", (d) => x1.bandwidth()).attr('y', (d) => Math.min(yScale(0), yScale(d.value))).attr('height', (d) => Math.abs(yScale(0) - yScale(d.value))); //append x axis container.append("g").attr("class", "axis").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(x0)); //append y axis container.append("g").attr("class", "y axis").call(d3.axisLeft(yScale)).append("text").attr("x", 2).attr("y", yScale(yScale.ticks().pop()) + 0.5).attr("dy", "0.32em").attr("fill", "#000").attr("font-weight", "bold").attr("text-anchor", "start").text("Values");
 <script src="https://d3js.org/d3.v7.min.js"></script> <svg width="600" height="400"></svg>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM