如何在堆积条形图中显示值的总和

时间:2016-04-27 09:29:26

标签: javascript d3.js

我有一个堆积条形图,每个堆栈中都有标签。每个标签在数据中有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

1 个答案:

答案 0 :(得分: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