使用D3

时间:2016-01-04 04:53:39

标签: javascript d3.js

我正在制作this infographic

的互动版本

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

  • 每个国家一个Y分数
  • 每个国家/地区每个期间的一个Y比例
  • One X scale

对于每个时期,将有两个值(可视化为两个不同颜色的条)。主要问题是并非所有国家都有三个时期;数据不是完全均匀的形状。这就是说,第一个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,但我确定在我的情况下使用两个更可取 - 我不知道如何实现它。

非常感谢任何和所有帮助

1 个答案:

答案 0 :(得分:1)

您可能不需要嵌套。 这是一个特殊情况条形图,因此您需要自己制作轴'条形标记网格。

我在代码中添加了注释,供您遵循

    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>

希望这有帮助!