2016-04-27 59 views
0

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

你能用你的代码提供一个工作小提琴/ jsbin/plnkr吗? – iulian

+0

@iulian这里是小提琴链接:https://jsfiddle.net/GM71819/bfz6k3m6/。我不知道如何将我的csv链接到这个fiddleso,请求您查看问题中嵌入的示例数据。 – Glenn

回答

1

你需要预先过程中,你已经得到它使得d会简单的数据之后采取和绘制它。

对于这一点,你需要类似下面的一个功能:

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

+0

非常感谢。这看起来非常好。我可以添加单选按钮和下拉菜单以进一步过滤数据吗?例如,我希望有一个性别单选按钮和一个大陆下拉菜单。我可以使用jquery轻松完成,但在D3中是否有一种方法可以用来过滤数据? – Glenn

+0

您可以在'd3'中使用'.on('click')'事件处理程序,它类似于'jQuery',或者您可以将jquery和d3组合起来使用,如果这对您没有问题。查看[this](http://bl.ocks.org/d3noob/5d621a60e2d1d02086bf)在d3中处理鼠标点击事件的示例。 – iulian

+0

@Glenn,如果您认为我的回答是针对您的原始问题,您是否将其标记为接受的答案?所以其他面临同样问题的人都知道这个解决方案有效。最好的祝福! – iulian