【问题标题】:d3js - Stacked Bar Chart in Horizontal Axis - How do I set x so the bars are seperated?d3js - 水平轴上的堆积条形图 - 如何设置 x 使条形分开?
【发布时间】:2020-08-19 12:25:41
【问题描述】:

我目前正在编写一些代码,用于水平轴上的堆叠条形图。但我不知道我们如何设置 .attr(x, ...) 以确保输入的每个矩形都位于条的末尾,从而填满空间。你能帮忙吗?

const bars = d3.select('#bars').append('svg').attr('width', 800).attr('height', 200);

d3.json('HHPro.json').then(data => { 
var bar1Data = [{
    position: 1,
    label: '70k',
    value: data.lemon.HH_Income.seventyK //1000
  },
  {
    position: 2,
    label: '71K - 149K',
    value: data.lemon.HH_Income.seventyoneK_onefortynineK // 800
  },
];

//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
  return d3.descending(a.value, b.value);
})

console.log(d3.sum(bar1Data, d => d.value))

var colorScale = d3.scaleOrdinal()
  .range(["#E8A82B", "#000000"]);

const x = d3.scaleLinear()
  .domain([0, d3.sum(bar1Data, d => d.value)])
  .range([0, 800]);

y = d3.scaleLinear()
  .domain([0, d3.max(bar1Data).value])
  .rangeRound([200])


//Join data to rects

const rects = bars.selectAll('rect')
  .data(bar1Data)

rects
  .attr('width', (bar1Data) => {
    return x(bar1Data.value)
  })
  .attr('height', 100)
  .attr('x', 20)
  .attr('y', 20)

rects.enter()
  .append('rect')
  .attr('width', (bar1Data) => {
    return x(bar1Data.value)
  })
  .attr('height', 200)
  .attr('x', 0) //BUT WHAT IS THIS??
  .attr('y', 100)
  .style("fill", (d, i) => colorScale(i));
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="bars"></div>

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    如果我正确理解您的问题,您想堆叠条形图。有两种方法可以做到这一点。第一种是手工完成,正如我在your other question 中展示的那样:

    const bars = d3.select('#bars').append('svg').attr('width', 800).attr('height', 200);
    
    var bar1Data = [{
        position: 1,
        label: '70k',
        value: 1000
      },
      {
        position: 2,
        label: '71K - 149K',
        value: 800
      },
    ];
    
    //sort bars based on value
    bar1Data = bar1Data.sort(function(a, b) {
      return d3.descending(a.value, b.value);
    })
    
    console.log(d3.sum(bar1Data, d => d.value))
    
    var colorScale = d3.scaleOrdinal()
      .range(["#E8A82B", "#000000"]);
    
    const x = d3.scaleLinear()
      .domain([0, d3.sum(bar1Data, d => d.value)])
      .range([0, 800]);
    
    y = d3.scaleLinear()
      .domain([0, d3.max(bar1Data).value])
      .rangeRound([200])
    
    //Join data to rects
    
    const rects = bars.selectAll('rect')
      .data(bar1Data)
    
    rects
      .attr('width', (d) => {
        return x(d.value)
      })
      .attr('height', 100)
      .attr('x', 20)
      .attr('y', 20)
    
    rects.enter()
      .append('rect')
      .attr('width', (d) => {
        return x(d.value)
      })
      .attr('height', 200)
      .attr('x', function(d, i) {
        // Note that I use `.map` here to take only the `.value` attribute
        // from the data and create an array of numbers. Then I pass it to `x`
        // to be transformed (could also be the other way around if you'd like,
        // so sum(bar1Data.map((e) => x(e.value)), 0, i)
        return x(sum(bar1Data.map((e) => e.value), 0, i));
      })
      .attr('y', 100)
      .style("fill", (d, i) => colorScale(i));
    
    function sum(array, start, end) {
      var total = 0;
      for (var i = start; i < end; i++) total += array[i];
      return total;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <div id="bars"></div>

    第二种方式是使用d3.stack

    const bars = d3.select('#bars').append('svg').attr('width', 800).attr('height', 200);
    const stack = d3.stack()
      // Each label is a group we want to stack on top of each other
      .keys(['70k', '71K - 149K']);
    
    var bar1Data = [{
      '70k': 1000,
      '71K - 149K': 800,
    }, ];
    
    //sort bars based on value
    bar1Data = bar1Data.sort(function(a, b) {
      return d3.descending(a.value, b.value);
    })
    
    var stackedData = stack(bar1Data);
    
    var colorScale = d3.scaleOrdinal()
      .range(["#E8A82B", "#000000"]);
    
    const x = d3.scaleLinear()
      .domain([0, 1800])
      .range([0, 800]);
    
    // Join data to rects
    // In this example I take `0` because you get nested arrays for groups
    const rects = bars.selectAll('rect')
      .data(stackedData.map((group) => group[0]))
    
    rects
      .enter()
      .append('rect')
      .attr('width', ([min, max]) => x(max - min))
      .attr('height', 200)
      .attr('x', ([min, _]) => x(min))
      .attr('y', 100)
      .style("fill", (d, i) => colorScale(i));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <div id="bars"></div>

    【讨论】:

    • 感谢鲁本,我能够解决它。我知道这可能是题外话,但你从​​哪里学习你的 d3 技能?非常感谢您的回复。
    • 没问题,乐于助人!我主要是通过小项目(绘制城市的叶绿素地图、创建堆积条形图等)来学习它们的,以 bl.ocks.org 和 observablehq.com 为例。最好的入门方法是从小处着手,慢慢增加复杂性,并使用大量 console.log。希望这会有所帮助:)
    猜你喜欢
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-20
    • 2020-01-21
    • 2013-05-15
    相关资源
    最近更新 更多