javascript - 使用 D3 创建多尺度、嵌套、有序条形图

标签 javascript d3.js

我正在制作 this infographic 的交互式版本

这在 D3 中被证明是困难的。将有多个序数尺度:

  • 每个国家/地区一个 Y 刻度
  • 每个国家/地区的每个时期都有一个 Y 刻度
  • 一个 X 比例

对于每个周期,都会有两个值(可视化为两个不同颜色的条)。主要问题是,并非所有国家都会经历三个时期;数据的形状并不完全均匀。这就是说,第一Y尺度的某些段将具有不同的高度。我不知道如何处理这个问题。

假设我有这些数据:

CountryName Period  ValueA  ValueB
Argentina   2004-2008   12  5
Argentina   2004-2013   10  5
Argentina   2008-2013   8   4
Bolivia 2002-2008   4   2
Bolivia 2002-2013   6   18
Brazil  2003-2008   9   2
Brazil  2003-2013   2   19
Brazil  2008-2013   1   3

我使用 d3.nest() 函数:

d3.nest()
  .key(function(d) { return d.CountryName; })
  .key(function(d) { return d.Period; })
  .map(data);

现在我将以我想要的形式获得数据,但请注意,有一些丢失的数据 - 在本例中,玻利维亚只有两个时期的数据。我创建了一个JSFiddle为此,但正如您所看到的,它存在一些问题。条形的高度应该始终相同。我想使用 yScale.rangeBand(),但问题是有些国家/地区有三个周期,而有些国家/地区只有两个周期。我还需要找到一种方法来在条形左侧显示国家/地区名称和句点

如果有人有更好的方法来解决这个问题,请告诉我。我已经为此奋斗了几天。正如您从 JSFiddle 中看到的,我只有一个 yScale,但我确信鉴于我的情况最好使用两个 - 我不知道如何实现这一点。

非常感谢任何和所有帮助

最佳答案

您可能并不真正需要嵌套。 这是一个特殊情况的条形图,因此您需要自己使轴的条形刻度网格。

我在代码中添加了注释供您关注

    var outerWidth = 1000;
    var outerHeight = 500;
    var margin = {
      left: 100,
      top: 0,
      right: 100,
      bottom: 90
    };
    var barPadding = 0.6;
    var barPaddingOuter = 0.3;
    var innerWidth = outerWidth - margin.left - margin.right;
    var innerHeight = outerHeight - margin.top - margin.bottom;
    //make your svg
    var svg = d3.select("body").append("svg")
      .attr("width", outerWidth)
      .attr("height", outerHeight);
    var g = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + 30 + ")");
    //the axis is on the right
    var xAxisG = g.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + 10 + ")")

    //the y axis is common for all
    var yAxisG = g.append("g")
      .attr("class", "y axis");

    var xScale = d3.scale.linear().range([0, innerWidth]);
    var yScale = d3.scale.ordinal().rangeRoundBands([0, innerHeight], barPadding, barPaddingOuter);

    var yAxis = d3.svg.axis().scale(yScale).orient("left")
      .outerTickSize(0);

    function render(data) {
      var xAxis = d3.svg.axis().scale(xScale).orient("top")
        //.tickValues([-5, -4, -3, 0, 3, 5, 10, 15, 20, 25])
        .outerTickSize(0)
        .innerTickSize(-1 * outerHeight);

      xScale.domain([0, 30]);

      //this will make the y axis labels.
      var country = "";
      data.forEach(function(d) {
        if (d.CountryName == country) {
          d.label = d.Period
        } else {
          d.label = d.Period
          d.heading = d.CountryName;
        }
        country = d.CountryName;
      });
      var labels = data.map(function(d) {
        return d.label
      });
      //set the domain for all y labels,
      yScale.domain(labels);
      //makes the x Axis
      xAxisG.call(xAxis);
      //makes the y Axis
      yAxisG.call(yAxis);
      //make the bar chart for valueB
      var bars = g.selectAll(".ValueA").data(data);
      bars.enter().append("rect")
        .attr("height", yScale.rangeBand() / 2);
      bars
        .attr("x", function(d) {
          return xScale(0)
        })
        .attr("y", function(d) {
          return yScale(d.label);
        })
        .attr("width", function(d) {
          console.log(d.ValueA)
          return xScale(d.ValueA);
        })
        .style("fill", "red")
        .attr("class", "ValueA");
      //make the bar chart for valueB
      var bars = g.selectAll(".ValueB").data(data);
      bars.enter().append("rect")
        .attr("height", yScale.rangeBand() / 2);
      bars
        .attr("x", function(d) {
          return xScale(0)
        })
        .attr("y", function(d) {
          return yScale.rangeBand() / 2 + yScale(d.label);
        })
        .attr("width", function(d) {
          return xScale(d.ValueB);
        })
        .style("fill", "blue")
        .attr("class", "ValueA");
      //make grid lines
      var lines = g.selectAll(".xLine").data(data.filter(function(d){return !(d.heading == undefined) }));
      lines.enter().append("line")
      lines
        .attr("x1", 0)
        .attr("y1", function(d) {
          return (yScale(d.label) - yScale.rangeBand())
        })
        .attr("x2", innerWidth)
        .attr("y2", function(d) {
          return (yScale(d.label) - yScale.rangeBand())
        })
        .style("stroke", "blue")
        .attr("class", "xLine")
        .style("display", function(s) {
          if((yScale(s.label) - yScale.rangeBand()) < 0){
            return "none";//not show grids when y comes negative
          }
        });
      
      //make heading
      var headings = g.selectAll(".heading").data(data.filter(function(d){return !(d.heading == undefined) }));
      headings.enter().append("text")
      .text(function(d){return d.heading})
      .attr("x", -100)
      .attr("y", function(d) {
          return (yScale(d.label) - yScale.rangeBand()) +30
      })

    }

    function type(d) {
      d.ValueA = +d.ValueA;
      d.ValueB = +d.ValueB;
      console.log(d)
      return d;
    }    d3.csv("https://gist.githubusercontent.com/cyrilcherian/e2dff83329af684cde78/raw/f85ce83497553c360d2af5d5dcf269390c44022f/cities.csv", type, render);
.tick line {
      opacity: 0.2;
    }
    
    .xLine {
      opacity: 0.2;
    }
    
    .axis text {
      font-size: 10px;
    }
    
    .axis .label {}
    
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    
    .y.axis path,
    .y.axis line {
      stroke: none;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

希望这有帮助!

关于javascript - 使用 D3 创建多尺度、嵌套、有序条形图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34584864/

相关文章:

javascript - 由 d3 (d3.svg.arc) 创建的 SVG 路径元素在应用蒙版时消失

javascript - Parsley 表单验证 - 事件监听器

javascript - 如何让 jStree 从 HTML 源代码中读取初始树

Typescript - 从接口(interface)实现匿名对象

javascript - 如何使用 d3 .ticks 在条形图中显示原始 y 轴值

javascript - 建议一种每分钟更新一次时间的方法

javascript - 按名称将 channel 添加到类别

JavaScript、使用 CSV 的 D3 条形图错误

javascript - D3 Javascript - 嵌套函数可能存在问题 - 生成控制台错误

javascript - 使用相对的 SVG 宽度和高度在中心节点周围均匀分布节点