javascript - 如何将现有的 d3 区域转换为堆叠区域?

标签 javascript d3.js

目前,我有一个面积图,显示多个数据路径的面积。但是,我正在寻找一种方法将其转换为堆积面积图。基本上,我希望数据集彼此堆叠,以便它们可见并且不会被其他较大区域隐藏。

我找到了两个例子:http://bl.ocks.org/mbostock/3885211http://bl.ocks.org/mbostock/3020685 。我遇到的问题是我的数据格式非常不同,而且我也不确定 d3 实际上是如何实现这项工作的。从概念上讲,我很难理解 d3 在幕后所做的事情。我的理解是,它正在计算添加的最后一层之间的差异,但似乎可以在不使用堆栈的情况下完成。

我的数据结构如下:

// datasets { data1: Array[10], data2: Array[10], data3: Array[10] }
// data1 [{value: 2340234, year: 1945}...] 

绘图函数是这样的:

function draw(series) {
  var data1 = [];
  var data2 = [];
  var data3 = [];
  var prop = 0;

  for (prop in series) {
    var current = series[prop];

    data1.push({value: current.data1, year: current.year});
    data2.push({value: current.data2, year: current.year});
    data3.push({value: current.data3, year: current.year});
  }

  var datasets = {
    data1: data1,
    data2: data2,
    data3: data3
  };

  // updated
  var layers = [datasets.data1, datasets.data2, datasets.data3];

  var height = 800;
  var width = window.innerWidth - 100;

  var x = d3.time.scale()
      .range([0, width]);

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

  var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

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

  // updated
  var stack = d3.layout.stack()
      .values( function(d) { return d.values; });
      /

  var stacked = stack(layers.map( function(layer) {
    return {
      values: layer.map( function(d) {
        return { year: new Date(d.year, 0), y: +d.value};
      })
    };
  }));

  var area = d3.svg.area()
      .x( function(d) { 
           console.log( x(d.year) + ' is the result of x(d.year)' )
           return x(d.year); 
        })
        // d.year is defined correctly 
      .y0(height)
      .y1( function(d) { 
            console.log( y(d.value) + ' is the result of y(d.value)' )
           return y(d.value); 
         }); 
        // d is Object {year: Sat Jan 01 2005 00:00:00 GMT-0800 (PST), y: 47390331, y0: 0}
        // here y(d.value) returns NaN for some reason

  var svg = d3.select('body').append("svg")
    .append("g")
      .attr("transform", "translate(" + 120 + "," + 30 + ")");

  // Calculate max value
  var maxY = 0;
  var minY = 0;
  series.forEach( function(object) {
    for (var key in object) {
      if (object[key] > maxY) {
        maxY = object[key];
      }
    }
  });

  // Calculate min value
  series.forEach( function(object) {
    for (var key in object) {
      if (object[key] < minY) {
        minY = object[key];
      } else if (minY === 0) {
        continue;
      }
    }
  });

  var minYear = new Date("1945");
  var maxYear = new Date("2015");

  x.domain([minYear, maxYear]);
  y.domain([minY, maxY]);

  svg.append("path").data(stacked)
    .attr('d', function(d) { return area(d.values); });

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

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

}

将其转换为堆积面积图的最佳方法是什么?

编辑

已修复。解决方案包括计算面积,如示例所示:

var area = d3.svg.area()
    .x(function(d) { return x(d.year); })
    .y0(function(d) { return y(d.y0); })
    .y1(function(d) { return y(d.y0 + d.y); });

最佳答案

快速浏览您需要执行的操作:

<小时/>

创建合适的堆栈生成器:

var stack = d3.layout.stack()
    .values(function(d) { return d.values; });
<小时/>

通过堆栈生成器运行您的数据。您应该为生成器提供一个“层”数组。对于下面的示例,我假设发生了类似 layers = [datasets.data1, datasets.data2, datasets.data3] 的情况。

  var stacked = stack(layers.map(function(layer) {
    return {
      values: layer.map(function(d) {
        return {year: new Date(d.year, 0), y: +d.value};
      })
    };
  }));

values 键与之前设置生成器时使用的键相同。如果稍后在绘图过程中需要,您可以向此对象添加更多内容。

<小时/>

创建堆叠数据后,通过区域生成器运行它:

svg.append("path")
  .data(stacked)
  .attr("d", function(d) { return area(d.values); });

在您的原始代码中,您使用了.datum(datasets[data]) - 如果您愿意,您可以继续执行此操作,但 for 循环将需要在堆叠的数据上运行:

for (var data in stacked) { ...

但是,我建议尽可能使用 .data 而不是 .datum。在将原始数据集发送到 d3 生成器之前,您对原始数据集进行了大量操作,但我怀疑其中大部分操作是不必要的或重复的。您应该能够得到如下所示的内容:

  var datasets = ...

  var stacked = stack(datasets.map(function(dataset) { ... }));

  svg.selectAll("path")
      .data(stacked)
      .enter()
      .attr("d", function(d) { return area(d.values); });

设置数据集和生成器应该只不过是映射您关心的特定字段/值的位置。

如果有帮助,我可以尝试制作一个完整的工作示例。不过,我需要一个真实的数据文件。

关于javascript - 如何将现有的 d3 区域转换为堆叠区域?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36667639/

相关文章:

javascript - 强制矩形超出 div[溢出 : hidden] to the right

javascript - d3 v4 geo 绘制边界反转

javascript - D3.js 文本输入(在 svg 内)过渡不透明度 0 到 1 不会以 1 结束

javascript - dc-js 禁止在单击饼图时选择切片

javascript - 从列中获取日期列表并将其格式化为 MM/dd/yyyy

javascript - AngularJS ngDisabled 无法在表单内工作

javascript - 如何固定箭头的位置不依赖于 Jquery 工具提示中的内容?

javascript - d3 在鼠标悬停时过滤数据后从选择中提取值

javascript - D3.js 中的脚本使 Chrome 崩溃

javascript - 我可以在自定义 Angular 元素的 html 标记中传递 args 吗?