d3.js堆叠条形图-修改堆叠顺序逻辑

时间:2020-10-27 02:42:06

标签: javascript d3.js

我想创建一个堆叠的条形图,其中rects的顺序由数据值(即,最大到最小,最高到最短,最丰富到最穷等)确定。据我所知,在堆叠数据之后,似乎保留了 initial 顺序。可以在我的代码段中看到,硬编码的数据使我们可以看到d3.stack()之前和之后的情况。请注意,尽管第三个矩形fmc3t1中的第三大矩形变为t3中所有矩形中的最大矩形,尽管其在堆栈中的位置保持不变:

var margins = {top:100, bottom:300, left:100, right:100};

var height = 600;
var width = 900;

var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;

var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);

var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");


  var data = [
{period:'t1', fmc1:2, fmc2:5, fmc3:6, fmc4:9, fmc5:10},
{period:'t2', fmc1:3, fmc2:4, fmc3:9, fmc4:8, fmc5:11},
{period:'t3', fmc1:3, fmc2:5, fmc3:15, fmc4:12, fmc5:10},
  ];

  var groups = d3.map(data, function(d){return(d.period)}).keys();

var subgroups = Object.keys(data[0]).slice(1);

  var stackedData = d3.stack()
   .keys(subgroups)
   (data);

//console.log(stackedData);

  var yScale = d3.scaleLinear()
  .domain([0,80])
  .range([height,0]);

  var xScale = d3.scaleBand()
  .domain(['t1','t2','t3'])
  .range([0,width])
  .padding([.5]);

  var colorScale = d3.scaleOrdinal()
  .domain(subgroups)
  .range(["#003366","#366092","#4f81b9","#95b3d7","#b8cce4","#e7eef8","#a6a6a6","#d9d9d9","#ffffcc","#f6d18b","#e4a733","#b29866","#a6a6a6","#d9d9d9","#e7eef8","#b8cce4","#95b3d7","#4f81b9","#366092","#003366"].reverse());

  graphGroup.append("g")
  .selectAll("g")
  .data(stackedData)
  .enter().append("g")
.attr("fill", function(d) { return colorScale(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
  .attr("x", function(d) { return xScale(d.data.period); })
  .attr("y", function(d) { return yScale(d[1]); })
  .attr("height", function(d) { return yScale(d[0]) - yScale(d[1]); })
  .attr("width",xScale.bandwidth());
<script src="https://d3js.org/d3.v5.min.js"></script>

我怀疑保留初始顺序对于计算堆栈中的相邻矩形可能有些必要。但是,另一方面,在可视化之前对数据进行排序是可视化领域中非常普遍的方法,甚至是首选做法,如果没有人找到解决这个问题的方法,我会感到惊讶。

问题

鉴于没有内置功能指定堆栈中rect的顺序,我应该如何使用排序逻辑来实现从大到小的排序?

1 个答案:

答案 0 :(得分:1)

嗯,有 内置功能可以指定订单,stack.order()。但是,它指定计算整个序列的顺序,而不是堆栈中的每个单个值(我相信这是您想要的...在这种情况下,您必须创建自己的函数)。

例如,使用stack.order(d3.stackOrderDescending)

var margins = {
  top: 0,
  bottom: 0,
  left: 0,
  right: 0
};

var height = 300;
var width = 500;

var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;

var svg = d3.select('body')
  .append('svg')
  .attr('width', totalWidth)
  .attr('height', totalHeight);

var graphGroup = svg.append('g')
  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");


var data = [{
    period: 't1',
    fmc1: 2,
    fmc2: 5,
    fmc3: 6,
    fmc4: 9,
    fmc5: 10
  },
  {
    period: 't2',
    fmc1: 3,
    fmc2: 4,
    fmc3: 9,
    fmc4: 8,
    fmc5: 11
  },
  {
    period: 't3',
    fmc1: 3,
    fmc2: 5,
    fmc3: 15,
    fmc4: 12,
    fmc5: 10
  },
];

var groups = d3.map(data, function(d) {
  return (d.period)
}).keys();

var subgroups = Object.keys(data[0]).slice(1);

var stackedData = d3.stack()
  .keys(subgroups)
  .order(d3.stackOrderDescending)
  (data);

//console.log(stackedData);

var yScale = d3.scaleLinear()
  .domain([0, 60])
  .range([height, 0]);

var xScale = d3.scaleBand()
  .domain(['t1', 't2', 't3'])
  .range([0, width])
  .padding([.5]);

var colorScale = d3.scaleOrdinal()
  .domain(subgroups)
  .range(["#003366", "#366092", "#4f81b9", "#95b3d7", "#b8cce4", "#e7eef8", "#a6a6a6", "#d9d9d9", "#ffffcc", "#f6d18b", "#e4a733", "#b29866", "#a6a6a6", "#d9d9d9", "#e7eef8", "#b8cce4", "#95b3d7", "#4f81b9", "#366092", "#003366"].reverse());

graphGroup.append("g")
  .selectAll("g")
  .data(stackedData)
  .enter().append("g")
  .attr("fill", function(d) {
    return colorScale(d.key);
  })
  .selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter().append("rect")
  .attr("x", function(d) {
    return xScale(d.data.period);
  })
  .attr("y", function(d) {
    return yScale(d[1]);
  })
  .attr("height", function(d) {
    return yScale(d[0]) - yScale(d[1]);
  })
  .attr("width", xScale.bandwidth());
<script src="https://d3js.org/d3.v5.min.js"></script>