2016-04-27 116 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