0
votes

I'm working on a bit of code at the moment for a stacked bar chart in the horizontal axis. But I can't figure our how to set the .attr(x, ...) to ensure each rect that is input is at the end of the bar, thus fulling up the space. Can you help?

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>
1

1 Answers

1
votes

If I understand your question correctly, you want to stack the bars. There are two ways to do it. The first is to do it by hand, as I showed in 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>

The second way is to use 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>