如何使用D3创建一个堆叠的Barchart



我有一个对象数组:

var data = [
  {"ORDER": 1, "apples": 3840, "bananas": 1920, "cherries": 960},
  {"ORDER": 2, "apples": 1600, "bananas": 1440, "cherries": 960},
  {"ORDER": 3, "apples":  640, "bananas":  960, "cherries": 640},
  {"ORDER": 4, "apples":  320, "bananas":  480, "cherries": 640}
];

我想创建一个堆叠的条形图,以便订单在X轴上,苹果,香蕉和樱桃的总和是Y轴。到目前为止,这是我的代码:

x = d3.scaleOrdinal().range([0, w-50])
y = d3.scaleLinear().range([0, h-50])
var stack = d3.stack()
    .keys(["apples", "bananas", "cherries", "dates"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);
var series = stack(data);
 x.domain(series.map(function(d) { return d.Order; }));
 y.domain([0, d3.max(data, function(d) { return d; })]).nice();
g.append("g")
    .selectAll("g")
    .data(d3.stack()(series))
    .enter().append("g")
        .attr("fill", function(d) { return color(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
        .attr("x", function(d) { return x(d.Order); })
        .attr("y", function(d) { return y(d[1]); })
        .attr("height", function(d) { return y(d[0]) - y(d[1]); })
        .attr("width", x);  

我也将其附加到SVG上,但我知道问题在上面的代码中。关于我做错了什么的想法?

存在一些问题。

一个,您的图表数据已经准备好堆叠在series中:

var series = stack(data);

此:d3.stack()(series);将产生一个空数组:

var data = [
  {"ORDER": 1, "apples": 3840, "bananas": 1920, "cherries": 960},
  {"ORDER": 2, "apples": 1600, "bananas": 1440, "cherries": 960},
  {"ORDER": 3, "apples":  640, "bananas":  960, "cherries": 640},
  {"ORDER": 4, "apples":  320, "bananas":  480, "cherries": 640}
];
var stack = d3.stack()
    .keys(["apples", "bananas", "cherries"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);
var series = stack(data);
console.log(d3.stack()(series));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.js"></script>


进一步向下:

 .attr("x", function(d) { return x(d.Order); })

这将返回未定义。首先,您的属性是ORDER不是Order,其次,如果您在返回之前console.log(d),您会注意到属性订单位于:d.data.ORDER

这也是您的量表的问题:

 x.domain(series.map(function(d) { return d.Order; }));

对于x刻度域,您应该使用 data变量来映射x域: x.domain(data.map(function(d) { return d.ORDER; }));


说到秤,最好用scaleBand()(文档)而不是scaleOrdinal()定义宽度,这将使您可以使用x.bandwidth()设置宽度。

最后,仍然谈到量表:对于您的Y量表,如果以此为例,您的域将无法正常工作,您将看到total属性的创建将定义域的上端。因此,您将需要找到一种方法,以获取每个ORDER所需图形的所有属性。我使用任意的10 000作为上限来产生一个工作示例(我也不知道您的颜色功能,所以我只是使用了一个数组):

var data = [
  {"ORDER": 1, "apples": 3840, "bananas": 1920, "cherries": 960},
  {"ORDER": 2, "apples": 1600, "bananas": 1440, "cherries": 960},
  {"ORDER": 3, "apples":  640, "bananas":  960, "cherries": 640},
  {"ORDER": 4, "apples":  320, "bananas":  480, "cherries": 640}
];
var h = 200;
var w = 200;
var svg = d3.select('body').append('svg').attr('width',w).attr('height',h);
var g = svg.append('g');
var x = d3.scaleBand().rangeRound([0, w-50]);
var y = d3.scaleLinear().range([h-50, 0]).domain([0,10000]);
var color = ['#bae4bc','#7bccc4','#43a2ca'];
var stack = d3.stack()
    .keys(["apples", "bananas", "cherries"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);
    
var series = stack(data);
x.domain(data.map(function(d) { return d.ORDER; }));
g.append("g")
    .selectAll("g")
    .data(series)
    .enter().append("g")
        .attr("fill", function(d,i) { return color[i]; })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
        .attr("x", function(d) { return x(d.data.ORDER); })
        .attr("y", function(d) { return y(d[1]); })
        .attr("height", function(d) { return y(d[0]) - y(d[1]); })
        .attr("width", x.bandwidth());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.js"></script>

最新更新