无法创建堆积的条形图?

时间:2019-04-05 10:52:35

标签: javascript angular typescript d3.js

我正在使用d3图表创建堆积的条形图。我在前端有按钮。单击按钮时应显示图表。单击按钮时出现以下错误。

错误:属性y:预期长度为“ NaN”。

错误:属性高度:预期长度“ NaN”。

错误:属性宽度:预期长度为“ 10,900”。

import * as d3 from 'd3';

export class stackedbarchart  {

    sbar(box_id){

      d3.select(box_id)
      .select("svg")
      .remove();

      var margin = { top: 20, right: 160, bottom: 35, left: 30 };

      var width = 1100 - margin.left - margin.right,
          height = 500 - margin.top - margin.bottom;

      var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      var data = [
        { country:"India", sales:100, profit:100, loss:10 },
        { country:"US", sales:100, profit:80, loss:40 },
        { country:"aus", sales:100, profit:70, loss:30 }
      ];

      var stack = d3.stack()
      .keys(["sales","profit","loss"])
      .order(d3.stackOrderNone)
      .offset(d3.stackOffsetNone);

      var series = stack(data);

      var x = d3.scaleOrdinal()
        .domain(series[0].map(function(d) {
          console.log(d)
           return d.x; }))
        .range([10, width-10], 0.02);

      var y = d3.scaleLinear()
        .domain([0, d3.max(series, function(d) {   
          return d3.max(d, function(d) { 
            return d.y0 + d.y; });  })])
        .range([height, 0]);

      var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];

      var yAxis = d3.axisLeft(y)
        .ticks(5)
        .tickSize(-width, 0, 0)
        .tickFormat( function(d) { return d } );

      var xAxis = d3.axisBottom(x)
      console.log(xAxis)

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

      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

      var groups = svg.selectAll("g.cost")
        .data(series)
        .enter().append("g")
        .attr("class", "cost")
        .style("fill", function(d, i) { return colors[i]; });

      var rect = groups.selectAll("rect")    
        .data(function(d) { return d; })
        .enter()
        .append("rect")
        .attr("x", function(d) { return x(d.x); })
        .attr("y", function(d) { return y(d.y0 + d.y); })
        .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
        .attr("width", x.range())
        .on("mouseover", function() { tooltip.style("display", null); })
        .on("mouseout", function() { tooltip.style("display", "none"); })
        .on("mousemove", function(d) {
          var xPosition = d3.mouse(this)[0] - 15;
          var yPosition = d3.mouse(this)[1] - 25;
          tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
          tooltip.select("text").text(d.y);
        });

      var legend = svg.selectAll(".legend")
        .data(colors)
        .enter().append("g")
        .attr("class", "legend")
        .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });

      legend.append("rect")
        .attr("x", width - 18)
        .attr("width", 18)
        .attr("height", 18)
        .style("fill", function(d, i) {return colors.slice().reverse()[i];});

      legend.append("text")
        .attr("x", width + 5)
        .attr("y", 9)
        .attr("dy", ".35em")
        .style("text-anchor", "start")
        .text(function(d, i) { 
          switch (i) {
            case 0: return "sales";
            case 1: return "profit";
            case 2: return "loss"; 
          }
        });

      var tooltip = svg.append("g")
        .attr("class", "tooltip")
        .style("display", "none");

      tooltip.append("rect")
        .attr("width", 30)
        .attr("height", 20)
        .attr("fill", "white")
        .style("opacity", 0.5);

      tooltip.append("text")
        .attr("x", 15)
        .attr("dy", "1.2em")
        .style("text-anchor", "middle")
        .attr("font-size", "12px")
        .attr("font-weight", "bold") ;

    }
}

任何帮助或向正确方向的提示将不胜感激。

1 个答案:

答案 0 :(得分:0)

D3v4中的更改

在d3v4中进行的更改会破坏d3v3图表。 d3v3中的堆栈具有x个访问器,并使用y0y来表示堆栈图的每个段的开始和结束值。结果看起来像这样:

 .append("rect")
  .attr("x", function(d) { return x(d.x); })
  .attr("y", function(d) { return y(d.y0 + d.y); })
  .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })

此方法不适用于d3v4 +,但似乎是您正在使用的方法。乍一看,您的代码可能基于this v3代码。

d3v4 +中的堆栈

让我们看看d3.stack在d3v4 +中产生了什么。

我们有输入数据:

var data = [
  { country:"India", sales:100, profit:100, loss:10 },
  { country:"US", sales:100, profit:80, loss:40 },
  { country:"aus", sales:100, profit:70, loss:30 }
];

还有一个堆栈生成器:

var stack = d3.stack()
  .keys(["sales","profit","loss"])
  .order(d3.stackOrderNone)     // default value, does not need to be specified.
  .offset(d3.stackOffsetNone)   // default value, does not need to be specified

当我们将数据传递给堆栈生成器时,我们得到:

[/*  India     US        AUS   */
 [[0,  100],[0,  100],[0,  100]],  // Sales
 [[100,200],[100,180],[100,170]],  // Profit
 [[200,210],[180,220],[170,200]]   // Loss 
]

如果将数组安排为表格,则列代表原始数据数组中的每个项目(代表国家的系列),行代表每个键。两个元素阵列中的每一个都代表一个堆叠的条形段。

包含给定键的所有值(例如,[[0,100],[0,100],[0,100]]用于销售)的数组,还包含具有键名的属性“ key”。

表示单个段的每个数组(例如:[0,100])具有属性data,该属性是原始数据数组中的一项(例如:{"country": "India","sales": 100,"profit": 100,"loss": 10})。 / p>

堆栈数组中的项目数(行数)不等于序列数(在这种情况下为国家),但等于键的数量。

创建天平

您遇到的第一个问题是如何指定刻度:

var x = d3.scaleOrdinal()
  .domain(series[0].map(function(d) { return d.x; }))
  .range([10, width-10], 0.02);

var y = d3.scaleLinear()
  .domain([0, d3.max(series, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; });  })])
  .range([height, 0]);

因为堆栈没有创建任何y0yx属性,所以这些都不起作用。

x刻度应该是带刻度(条形图通常使用带d3的带刻度),我们可以使用原始数据数组来创建域:

var x = d3.scaleBand()  // band scale
   .domain(data.map(function(d) {
         return d.country; }))  // return the country.
   .range([10, width-10]); 

y标度需要访问d [0]和d [1],而不是d.y0和d.y:

var y = d3.scaleLinear()
   .domain([0, d3.max(series, function(d) {   
       return d3.max(d, function(d) { 
            return d[0] + d[1]; });  })]) // access stack values (not d.y, d.y0)
   .range([height, 0]);  

附加矩形

现在让我们移至附加矩形的位置:

    .append("rect")
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
    .attr("width", x.range())

由于d.x未定义,我们可以使用x(d.data.country)d.y0d.y出现的地方,我们需要替换d[0]d[1]。最后,x.range()几乎是图形的宽度,我们可以改用x.bandwidth(),它是单个条形的宽度。

下面是您的代码(从定义边距开始),其中包含以下更新:

var margin = { top: 20, right: 160, bottom: 35, left: 30 };

var width = 500 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

var svg = d3.select("body")
   .append("svg")
   .attr("width", width + margin.left + margin.right)
   .attr("height", height + margin.top + margin.bottom)
   .append("g")
   .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var data = [
  { country:"India", sales:100, profit:100, loss:10 },
  { country:"US", sales:100, profit:80, loss:40 },
  { country:"Aus", sales:100, profit:70, loss:30 },
  { country:"NZ", sales:100, profit:70, loss:30 }
];

var stack = d3.stack()
   .keys(["sales","profit","loss"])
   .order(d3.stackOrderNone)
   .offset(d3.stackOffsetNone);

var series = stack(data);


var x = d3.scaleBand()  // band scale
   .domain(data.map(function(d) {
         return d.country; }))  // return the country.
   .range([10, width-10])
   .padding(0.1); // space between (value between 0 and 1).

var y = d3.scaleLinear()
   .domain([0, d3.max(series, function(d) {   
       return d3.max(d, function(d) { 
            return d[0] + d[1]; });  })]) // access stack values (not d.y, d.y0)
   .range([height, 0]);  

// color scale:
var colors = d3.scaleOrdinal()
  .range(["b33040", "#d25c4d", "#f2b447", "#d9d574"])
  .domain(series.map(function(d) { return d.key; }));

var yAxis = d3.axisLeft(y)
   .ticks(5)
   .tickSize(-width, 0, 0)
   .tickFormat( function(d) { return d } );

var xAxis = d3.axisBottom(x)
     
svg.append("g")
   .attr("class", "y axis")
   .call(yAxis);

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis);

var groups = svg.selectAll("g.cost")
  .data(series)
  .enter().append("g")
  .attr("class", "cost")
  .style("fill", function(d) { return colors(d); });


// More changes:
var rect = groups.selectAll("rect")    
 .data(function(d) { return d; })
 .enter()
 .append("rect")
 .attr("x", function(d) { return x(d.data.country); })  // Access country.
 .attr("y", function(d) { return y(d[0] + d[1]); })
 .attr("height", function(d) { return y(d[0]) - y(d[0] + d[1]); })
 .attr("width", x.bandwidth())

// Minor changes:
var legend = svg.selectAll(".legend")
 .data(series)
 .enter().append("g")
 .attr("class", "legend")
 .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });

legend.append("rect")
  .attr("x", width - 18)
  .attr("width", 18)
  .attr("height", 18)
  .style("fill", function(d) {
      return colors(d.key);
  });

legend.append("text")
  .attr("x", width + 5)
  .attr("y", 9)
  .attr("dy", ".35em")
  .style("text-anchor", "start")
  .text(function(d) { return d.key; })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

为简化代码段,我删除了鼠标交互

创建图例

这可以简化一点,但这不是问题的一部分,不过,我将其与色标一起包括在这里。