【问题标题】:How to display sum of value in stacked bar chart如何在堆积条形图中显示值的总和
【发布时间】:2016-04-27 09:29:25
【问题描述】:

我有一个堆叠条形图,每个堆叠都有标签。每个标签在数据中有 2 行。我能够在图表上显示第一次出现的值,但图表应该显示这两个值的总和。

下面是我的代码:

 var outerWidth = 960;
  var outerHeight = 500;
  var margin = { left: 130, top: 44, right: 30, bottom: 47 };
  var barPadding = 0.2;

  var xColumn = "country";
  var yColumn = "population";
  var colorColumn = "religion";
  var layerColumn = colorColumn;

  var hoveredColorValue;
  var hoveredStrokeColor = "black";

  var innerWidth  = outerWidth  - margin.left - margin.right;
  var innerHeight = outerHeight - margin.top  - margin.bottom;

  var svg = d3.select("body").append("svg")
    .attr("width",  outerWidth)
    .attr("height", outerHeight);
  var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // This is the layer where the bars are drawn.
  var baseBarLayer = g.append("g");

  // This layer contains a semi-transparent overlay
  // that fades out the base bars.
  var overlayRect = g.append("g")
    .append("rect")
    .attr("width", innerWidth)
    .attr("height", innerHeight)
    .style("pointer-events", "none");

  // This contains the subset of bars rendered on top
  // when you hover over the entries in the color legend.
  var foregroundBarLayer = g.append("g");

  var xAxisG = g.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + innerHeight + ")");
  var yAxisG = g.append("g")
    .attr("class", "y axis");
  var colorLegendG = g.append("g")
    .attr("class", "color-legend")
    .attr("transform", "translate(596, 0)");

  var xScale = d3.scale.ordinal().rangeBands([0, innerWidth], barPadding);
  var yScale = d3.scale.linear().range([innerHeight, 0]);
  var colorScale = d3.scale.category10();

  var tipNumberFormat = d3.format(",");
  var tip = d3.tip()
    .attr("class", "d3-tip")
    .offset([-10, 0])
    .html(function(d) {
      return [
        d[colorColumn],
        " in ",
        d[xColumn],
        ": ",
        tipNumberFormat(d[yColumn])
      ].join("");
    });
  g.call(tip);

  // Use a modified SI formatter that uses "B" for Billion.
  var siFormat = d3.format("s");
  var customTickFormat = function (d){
    return siFormat(d).replace("G", "B");
  };

  var xAxis = d3.svg.axis().scale(xScale).orient("bottom")
    .outerTickSize(0);
  var yAxis = d3.svg.axis().scale(yScale).orient("left")
    .ticks(5)
    .tickFormat(customTickFormat)
    .outerTickSize(0);

  var colorLegend = d3.legend.color()
    .scale(colorScale)
    .shapePadding(6.24)
    .shapeWidth(25)
    .shapeHeight(25)
    .labelOffset(5);

  function render(data){

    var nested = d3.nest()
      .key(function (d){ return d[layerColumn]; })
      .entries(data);

    var stack = d3.layout.stack()
      .y(function (d){ return d[yColumn]; })
      .values(function (d){ return d.values; });

    var layers = stack(nested.reverse()).reverse();

    xScale.domain(layers[0].values.map(function (d){
      return d[xColumn];
    }));

    yScale.domain([
      0,
      d3.max(layers, function (layer){
        return d3.max(layer.values, function (d){
          return d.y0 + d.y;
        });
      })
    ]);

    colorScale.domain(layers.map(function (layer){
      return layer.key;
    }));

    xAxisG.call(xAxis); 
    yAxisG.call(yAxis);

    renderBars(baseBarLayer, layers);

    if(hoveredColorValue){
      setOverlayTransparency(0.7);
      renderBars(foregroundBarLayer, layers.filter(function (layer){
        return layer.key === hoveredColorValue;
      }));
    } else {
      setOverlayTransparency(0.0);
      renderBars(foregroundBarLayer, []);
    }

    colorLegendG.call(colorLegend);

    // Move the text down a bit.
    colorLegendG.selectAll("text").attr("y", 4);

    listenForHover(colorLegendG.selectAll("rect"), data);
    listenForHover(colorLegendG.selectAll("text"), data);
  }

  function setOverlayTransparency(alpha){
    overlayRect
      .transition().duration(400)
      .attr("fill", "rgba(255, 255, 255, " + alpha + ")");
  }

  function renderBars(g, layers){
    var layerGs = g.selectAll(".layer").data(layers);
    layerGs.enter().append("g").attr("class", "layer");
    layerGs.exit().remove();
    layerGs.style("fill", function (d){
      return colorScale(d.key);
    });

    var bars = layerGs.selectAll("rect").data(function (d){
      return d.values;
    });
    bars.enter().append("rect")
      .on("mouseover", tip.show)
      .on("mouseout", tip.hide);
    bars.exit().remove();
    bars
      .attr("x", function (d){ return xScale(d[xColumn]); })
      .attr("y", function (d){ return yScale(d.y0 + d.y); })
      .attr("width", xScale.rangeBand())
      .attr("height", function (d){ return innerHeight - yScale(d.y); });
  }

  function listenForHover(selection, data){
    selection
      .on("mouseover", function (d){
        hoveredColorValue = d;
        render(data);
      })
      .on("mouseout", function (d){
        hoveredColorValue = null;
        render(data);
      })
      .style("cursor", "pointer");
  }

  function type(d){
    d.population = +d.population;
    return d;
  }

  d3.csv("data.csv", type, render);

以下是样本数据:

Continent   country religion    gender  population
Asia    China   Christian   male    68410000
Asia    China   Unaffiliated    male    700680000
North America   USA Christian   male    243060000
North America   USA Unaffiliated    male    50980000
South America   Brazil  Christian   male    173300000
South America   Brazil  Unaffiliated    male    15410000
Asia    China   Christian   female  24363526.41
Asia    China   Unaffiliated    female  52308051.93
North America   USA Christian   female  12829311.53
North America   USA Unaffiliated    female  17756518.63
South America   Brazil  Christian   female  85172307.14
South America   Brazil  Unaffiliated    female  12802705.11

【问题讨论】:

  • 你能用你的代码提供一个工作小提琴/jsbin/plnkr吗?
  • @iulian 这里是小提琴链接:jsfiddle.net/GM71819/bfz6k3m6。我不知道如何将我的 csv 链接到这个小提琴所以要求您查看问题中嵌入的示例数据。

标签: javascript d3.js


【解决方案1】:

您需要在获得数据后立即对其进行预处理,以便 d3 轻松获取并绘制它。

为此,您需要一个类似于以下的函数:

function combineMaleAndFemale (data) {
    var temp = {};
    var result = [];

    // Add up population from the same continent and country
    data.forEach(function (value) {
        var combinedKey = value.Continent + '_' + value.country;
        if (!temp.hasOwnProperty(combinedKey)) {
        temp[combinedKey] = value;
      } else {
        temp[combinedKey].population += value.population;
      }
    });

    // Generate an array with combined population values.
    for (prop in temp) {
        if (temp.hasOwnProperty(prop)) {
        result.push(temp[prop]);
      }
    }

    return result;
  }

然后,在您的 render 函数中,预处理您收到的数据。

function render(data){

    var nested = d3.nest()
      .key(function (d){ return d[layerColumn]; })
      .entries(data);

    // For each religion type, pre-process the data to add up populations.
    nested.forEach(function(religion) {
        religion.values = combineMaleAndFemale(religion.values);
    });
    ...

这是你的小提琴的working fork

【讨论】:

  • 非常感谢。这看起来非常好。我可以添加单选按钮和下拉菜单来进一步过滤数据吗?例如,我希望有一个性别单选按钮和一个大陆下拉菜单。我本可以使用 jquery 轻松完成,但在 d3 中是否有一种方法可以用来过滤数据?
  • 您可以使用d3 中的.on('click') 事件处理程序,这与jQuery 的处理程序类似,或者您可以将jquery 和d3 结合使用,如果对您没有问题的话。查看this d3 中处理鼠标点击事件的示例。
  • @Glenn,如果您认为我的回答解决了您最初的问题,您介意将其标记为已接受的答案吗?以便其他面临相同问题的人知道此解决方案有效。最好的问候!
  • 确定。但是我该怎么做呢?
  • 答案的选票计数器下方有一个复选标志。点击它,它会变成绿色。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
  • 2021-09-07
  • 2021-02-06
  • 2014-03-08
  • 1970-01-01
相关资源
最近更新 更多