D3将点添加到堆积面积图

时间:2019-07-04 19:39:34

标签: d3.js

我需要在下面的代码中将点添加到堆积面积图。我已经使用.data(layers)在代码中尝试了无数次迭代,但是在我尝试过的所有迭代中查找cx和cy时,它都会出错。主要问题是我不了解如何钻取layers变量以获取累积总和,因此圆与堆叠区域中的线匹配。

这里是fiddle,以下是代码段:

var data = d3.csv.parse(d3.select("#dataset").text());
d3.select("#dataset").remove();

var format = d3.time.format("%m/%d/%y");
    
var margin = {top: 20, right: 30, bottom: 30, left: 40},
    width = 500 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    
var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);
    
var z = d3.scale.category20c();
    
var xAxis = d3.svg.axis()
   .scale(x)
   .orient("bottom")
   .ticks(d3.time.days);
    
var yAxis = d3.svg.axis()
   .scale(y)
   .orient("left");
    
var stack = d3.layout.stack()
   .offset("zero")
   .values(function(d) { return d.values; })
   .x(function(d) { return d.date; })
   .y(function(d) { return d.value; });
    
var nest = d3.nest()
   .key(function(d) { return d.key; });
    
var area = d3.svg.area()
  .interpolate("cardinal")
  .x(function(d) { return x(d.date); })
  .y0(function(d) { return y(d.y0); })
  .y1(function(d) { return y(d.y0 + d.y); });
    
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 mygroups = d3.map(data, function(d){return(d.key)}).keys()
    
    
var color = d3.scale.ordinal()
    .domain(mygroups)
    .range(['#CA999A','#99A3B0','#9FBD9F'])
    
data.forEach(function(d) {
   d.date = format.parse(d.date);
   d.value = +d.value;
});
    
var layers = stack(nest.entries(data));
      
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
    
svg.selectAll(".layer")
   .data(layers)
   .enter()
   .append("path")
   .attr("class", "layer")
   .attr("d", function(d) { return area(d.values); })
   .style("fill", function(d, i) { return color(i); })
   .style("stroke","black");
          
 svg.append("g")
   .attr("class", "x axis")
   .attr("transform", "translate(0," + height + ")")
   .call(xAxis)
   .selectAll("path","line")
   .style({
       fill: "none",
       stroke: "#000",
       "shape-rendering":"crispEdges"
    });
    
svg.append("g")
   .attr("class", "y axis")
   .call(yAxis)
   .selectAll("path","line")
   .style({
      fill: "none",
      stroke: "#000",
      "shape-rendering":"crispEdges"
   });
    
    
     //adds dots where original data would go but without error
    /* svg.selectAll("circle")
        .data(data)
        .enter()
        .append("circle")
        .attr("class", "dot")
        .attr("r", 5)
        .attr("cx", function(d) { return x(d.date); })
        .attr("cy", function(d) { return y(d.value); });
     */
    
    //errors out with <circle> attribute cx: Expected length, "NaN".  
    //Tired various looping functions that would not work. 
    //I want the dots to follow the lines in the stack.
    // it does at least put a dot on the graph
    
svg.selectAll("circle")
   .data(layers)
   .enter()
   .append("circle")
   .attr("class", "dot")
   .attr("r", 5)
   .attr("cx", function(d) { return x(d.date); })
   .attr("cy", function(d) { return y(d.value); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<pre id = "dataset">key,value,date
Group1,37,04/23/12
Group2,12,04/23/12
Group3,46,04/23/12
Group1,32,04/24/12
Group2,19,04/24/12
Group3,42,04/24/12
Group1,45,04/25/12
Group2,16,04/25/12
Group3,44,04/25/12
Group1,24,04/26/12
Group2,52,04/26/12
Group3,64,04/26/12</pre>

1 个答案:

答案 0 :(得分:0)

⚠️此问题和答案均使用d3v3-d3v4 +堆栈创建具有不同结构的数据数组-因此,此答案可能对d3v4 +没用

即使缩放,也无法使用data变量绘制圆,因为返回的值将用于非累积线/面图。另外,在layers变量中绘制值将不起作用,因为它每层仅包含一个值。 layers中的每一层都包含一个数组,该数组包含该层的所有点,我们可以获取所有这些点并将其绘制为圆形。

这是layers结构:

[
  {
    "key": "Group1",
    "values": [
      {
        "key": "Group1",
        "value": 37,
        "date": "2012-04-23T07:00:00.000Z",
        "y0": 0,
        "y": 37
      },
      ...
      {
        "key": "Group1",
        "value": 24,
        "date": "2012-04-26T07:00:00.000Z",
        "y0": 0,
        "y": 24
      }
    ]
  },
  {
    "key": "Group2",
    "values": [
      {
        "key": "Group2",
        "value": 12,
        "date": "2012-04-23T07:00:00.000Z",
        "y0": 37,
        "y": 12
      },
      ...  // an so on.

因此,我们可以循环遍历layers的每个项目,这意味着我们可以遍历每一层并收集所有点。这样的东西就足够了:

// Grab all coordinates of all layers:
var points = []; 
layers.forEach(function(d) {
  return points.push(...d.values);
})

现在,让我们看一下points数组中的每个项目:

  {
    "key": "Group1",
    "value": 37,
    "date": "2012-04-23T07:00:00.000Z",
    "y0": 0,
    "y": 37
  }

在这里,y表示项目的高度,y0表示其基础(该点的图层底部)。由于我们只希望绘制一次每个点,并且我们不想绘制底层y0属性的0值,因此我们只应绘制最高的y值(y+y0):

//adds dots where original data would go but without error
svg.selectAll("circle")
   .data(points)
   .enter()
   .append("circle")
   .attr("class", "dot")
   .attr("r", 5)
   .attr("cx", function(d) { return x(d.date); })
   .attr("cy", function(d) { return y(d.y0+d.y); });

总共看起来像这样:

var data = d3.csv.parse(d3.select("#dataset").text());
d3.select("#dataset").remove();

var format = d3.time.format("%m/%d/%y");
    
var margin = {top: 20, right: 30, bottom: 30, left: 40},
    width = 500 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    
var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);
    
var z = d3.scale.category20c();
    
var xAxis = d3.svg.axis()
   .scale(x)
   .orient("bottom")
   .ticks(d3.time.days);
    
var yAxis = d3.svg.axis()
   .scale(y)
   .orient("left");
    
var stack = d3.layout.stack()
   .offset("zero")
   .values(function(d) { return d.values; })
   .x(function(d) { return d.date; })
   .y(function(d) { return d.value; });
    
var nest = d3.nest()
   .key(function(d) { return d.key; });
    
var area = d3.svg.area()
  .interpolate("cardinal")
  .x(function(d) { return x(d.date); })
  .y0(function(d) { return y(d.y0); })
  .y1(function(d) { return y(d.y0 + d.y); });
    
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 mygroups = d3.map(data, function(d){return(d.key)}).keys()
    
    
var color = d3.scale.ordinal()
    .domain(mygroups)
    .range(['#CA999A','#99A3B0','#9FBD9F'])
    
data.forEach(function(d) {
   d.date = format.parse(d.date);
   d.value = +d.value;
});
    
var layers = stack(nest.entries(data));
      
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
    
svg.selectAll(".layer")
   .data(layers)
   .enter()
   .append("path")
   .attr("class", "layer")
   .attr("d", function(d) { return area(d.values); })
   .style("fill", function(d, i) { return color(i); })
   .style("stroke","black");
          
 svg.append("g")
   .attr("class", "x axis")
   .attr("transform", "translate(0," + height + ")")
   .call(xAxis)
   .selectAll("path","line")
   .style({
       fill: "none",
       stroke: "#000",
       "shape-rendering":"crispEdges"
    });
    
svg.append("g")
   .attr("class", "y axis")
   .call(yAxis)
   .selectAll("path","line")
   .style({
      fill: "none",
      stroke: "#000",
      "shape-rendering":"crispEdges"
   });
    

// Grab all coordinates of all layers:
var points = []; 
layers.forEach(function(d) {
  return points.push(...d.values);
})

//adds dots where original data would go but without error
svg.selectAll("circle")
   .data(points)
   .enter()
   .append("circle")
   .attr("class", "dot")
   .attr("r", 5)
   .attr("cx", function(d) { return x(d.date); })
   .attr("cy", function(d) { return y(d.y0+d.y); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<pre id = "dataset">key,value,date
Group1,37,04/23/12
Group2,12,04/23/12
Group3,46,04/23/12
Group1,32,04/24/12
Group2,19,04/24/12
Group3,42,04/24/12
Group1,45,04/25/12
Group2,16,04/25/12
Group3,44,04/25/12
Group1,24,04/26/12
Group2,52,04/26/12
Group3,64,04/26/12</pre>