2016-06-07 133 views
1

首先對不起,如果我的英語是很難理解的,我會盡我所能...D3嵌套分組條形圖

我是相當新的D3.js,我試圖創建一個D3使用嵌套數據分組條形圖。我已經看過這裏分享的一些解決方案,但它們只顯示一個級別的分組。在我的情況下,數據將來自具有該數據結構的CSV文件:

groups,categories,value 1,value 2,value 3 
group 1,A,61.0158803,25.903359,13.08076071 
group 1,B,71.27703826,21.0180133,7.70494844 
group 1,C,82.70203982,13.52731445,3.770645737 
group 2,A,58.85721523,28.25939061,12.88339417 
group 2,B,71.39695487,20.66010982,7.942935308 
group 2,C,82.22389321,13.68924542,4.08686137 

圖表意圖具有兩個X軸,一個用於組(0電平),一個用於類別(1級)。值1至3將顯示爲每個目錄的分組條,並且分類將顯示在相應的組內。

圖表的結構應該是:

value 1 | value 2 | value 3 | value 1 | value 2 | value 3 | value 1 | value 2 | value 3 | 
|  category A   |   category B   |   category C   | 
|          group 1           | 

與同爲第2組,置於連續的。

問題出在我正在處理的代碼上,我找到了正確的座標軸,但是顯示了兩個組的數據,一個在另一個上面,在每個組區域中。我無法將類別上的數據鏈接到相應的組中,以便將它們繪製在相應的位置。

這是到目前爲止,我已經得到了代碼:

var x0 = d3.scale.ordinal() 
    .rangeRoundBands([0,width], 0); 

var x1 = d3.scale.ordinal() 
.rangeRoundBands([0,width]); 

var x2 = d3.scale.ordinal(); 

var y = d3.scale.linear() 
    .range([height,0]); 

var color = d3.scale.category10(); 

var x0Axis = d3.svg.axis() 
    .scale(x0) 
    .orient("bottom"); 

var x1Axis = d3.svg.axis() 
    .scale(x1) 
.orient("bottom"); 

var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left"); 

var svg = d3.select(".chart") 
    .append("svg") 
    .attr("class", "svg") 
    .attr("viewBox", "" + margin* -1 + " " + margin* -1 + " " + (width + margin*2) + " " + (height + margin *2) + "") 
    .attr ("preserveAspectRatio", "xMidYMid") 
    .attr("width", "100%") 
    .attr("height", "100%") 



d3.csv("../data/EQ01.csv", function(error, data){ 
    if (error) throw error; 

var seriesNames = d3.keys(data[0]).filter(function(key) { return key !== "categories" && key !== "groups";}); 

    data.forEach(function(d) { 
d.values = seriesNames.map(function(name) { return { 
    xValue: name, 
    yValue: +d[name] 
    }; 
}); 
    }); 

    nested = d3.nest() 
     .key(function(d) { return d.groups}) 
     .key(function(d) { return d.categories}) 
     .entries(data); 

    y.domain([0, d3.max(data, function(d) { return d3.max(d.values, function(d) { return d.yValue; }); })]); 
    x0.domain(nested.map(function(d) {return d.key;})); 
    x1.domain(data.map(function(d) { return d.categories; })).rangeRoundBands([0, x0.rangeBand() ], 0.1); 
    x2.domain(seriesNames).rangeRoundBands([0, x1.rangeBand()], 0); 

    svg.append("g") 
    .attr("class", "x0 axis") 
    .attr("transform", "translate(0," + (height+30) + ")") 
    .call(x0Axis); 

    svg.append("g") 
    .attr("class", "y axis") 
    .call(yAxis) 

    var group = svg.selectAll(".group") 
    .data(nested) 
    .enter().append("g") 
    .attr("class", "group") 
    .attr("transform", function(d) { return "translate(" + x0(d.key) + ",0)"; }); 

    group.append("g") 
    .attr("class", "x1 axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(x1Axis); 

    var category = group.selectAll(".category") 
    .data(data) 
    .enter().append("g") 
    .attr("class", "category") 
    .attr("transform", function(d) { return "translate(" + x1(d.categories) + ",0)"; }); 

    category.selectAll("rect") 
    .data(function(d) { return d.values; }) 
    .enter().append("rect") 
    .attr("width", x2.rangeBand()) 
    .attr("x", function(d) { return x2(d.xValue); }) 
    .attr("y", function(d) { return y(d.yValue); }) 
    .attr("height", function(d) { return height - y(d.yValue); }) 
    .style("fill", function(d){return color(d.xValue)}) 

提前的幫助非常感謝!

+0

我覺得應該是'。數據(函數(d){回報d.values;})'的'」 .category''組。如果這不起作用,你能設置一個jsfiddle或類似的嗎? – tarulen

+0

嗨@tarulen,謝謝你的回答!我已經嘗試過,似乎沒有工作。這是一個JSFiddle [鏈接](https://jsfiddle.net/hLaxmb8p/3/) – Vidimar

回答

3

問題是您沒有正確地將您的數據與您的元素進行連接。

我們需要構建不同的比例以獲得正確的rangeBand值。

var x_groups = d3.scale.ordinal() 
    .rangeRoundBands([0, width], .1); 

var x_categories = d3.scale.ordinal(); 

var x_values = d3.scale.ordinal(); 

我創建了一個嵌套的數據結構,它將包含我們分組條形圖方法所需的所有東西。

var nested = d3.nest() 
    .key(function(d) { 
     return d.groups; 
    }) 
    .key(function(d) { 
     return d.categories; 
    }) 
    .rollup(function(leaves) { 
     return [{ 
     key: 'v-a', 
     value: leaves[0]['value 1'] 
     }, { 
     key: 'v-b', 
     value: leaves[0]['value 2'] 
     }, { 
     key: 'v-c', 
     value: leaves[0]['value 3'] 
     }]; 
    }) 
    .entries(data); 

接下來讓我們用我們剛剛得到的信息配置秤。

x_groups.domain(nested.map(function(d) { 
    return d.key; 
    })); 
    //var categories = ['A', 'B', 'C']; 
    var categories = nested[0].values.map(function(d, i) { 
    return d.key; 
    }); 
    x_categories.domain(categories).rangeRoundBands([0, x_groups.rangeBand()]); 
    //var values = ['value 1', 'value 2', 'value 3']; 
    var values = nested[0].values[0].values.map(function(d, i) { 
    return d.key; 
    }); 
    x_values.domain(values).rangeRoundBands([0, x_categories.rangeBand()]); 

然後我們終於可以開始我們的數據加入。您可以看到,當我們輸入新的信息級別時,我們需要正確設置data函數。

var groups_g = svg.selectAll(".group") 
    .data(nested) 
    .enter().append("g") 
    .attr("class", function(d) { 
    return 'group group-' + d.key; 
    }) 
    .attr("transform", function(d) { 
    return "translate(" + x_groups(d.key) + ",0)"; 
    }); 

var categories_g = groups_g.selectAll(".category") 
    .data(function(d) { 
    return d.values; 
    }) 
    .enter().append("g") 
    .attr("class", function(d) { 
    return 'category category-' + d.key; 
    }) 
    .attr("transform", function(d) { 
    return "translate(" + x_categories(d.key) + ",0)"; 
    }); 

var categories_labels = categories_g.selectAll('.category-label') 
    .data(function(d) { 
    return [d.key]; 
    }) 
    .enter().append("text") 
    .attr("class", function(d) { 
    return 'category-label category-label-' + d; 
    }) 
    .attr("x", function(d) { 
    return x_categories.rangeBand()/2; 
    }) 
    .attr('y', function(d) { 
    return height + 25; 
    }) 
    .attr('text-anchor', 'middle') 
    .text(function(d) { 
    return d; 
    }) 

var values_g = categories_g.selectAll(".value") 
    .data(function(d) { 
    return d.values; 
    }) 
    .enter().append("g") 
    .attr("class", function(d) { 
    return 'value value-' + d.key; 
    }) 
    .attr("transform", function(d) { 
    return "translate(" + x_values(d.key) + ",0)"; 
    }); 

var values_labels = values_g.selectAll('.value-label') 
    .data(function(d) { 
    return [d.key]; 
    }) 
    .enter().append("text") 
    .attr("class", function(d) { 
    return 'value-label value-label-' + d; 
    }) 
    .attr("x", function(d) { 
    return x_values.rangeBand()/2; 
    }) 
    .attr('y', function(d) { 
    return height + 10; 
    }) 
    .attr('text-anchor', 'middle') 
    .text(function(d) { 
    return d; 
    }) 

var rects = values_g.selectAll('.rect') 
    .data(function(d) { 
    return [d]; 
    }) 
    .enter().append("rect") 
    .attr("class", "rect") 
    .attr("width", x_values.rangeBand()) 
    .attr("x", function(d) { 
    return 0; 
    }) 
    .attr("y", function(d) { 
    return y(d.value); 
    }) 
    .attr("height", function(d) { 
    return height - y(d.value); 
    }) 
    .style("fill", function(d) { 
    return color(d.key); 
    }); 

工作plnkr:https://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki?p=preview

+0

哇,非常感謝@torresomar!它工作完美,最重要的是,我從你的答案中學到了很多東西! – Vidimar

+0

工程就像一個魅力!謝謝!!! – xpt