D3 Chart version 4 Normalized Stacked Bar Chart 从垂直到水平

D3 Chart version 4 Normalized Stacked Bar Chart from vertical to horizontal

这个问题与这个问题非常相似 但我使用的是最新的 D3 版本(//d3js.org/d3.v4.min.js).

我正在尝试制作这个标准化堆叠条形图 水平图表。最新版本有没有优化的方法来实现这个?

我已经交换了 x 轴和 y 轴,如下所示

var svg = d3.select("svg"),
    margin = {top: 20, right: 60, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var y = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.1)
    .align(0.1);

var x = d3.scaleLinear()
    .rangeRound([height, 0]);

var z = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

var stack = d3.stack()
    .offset(d3.stackOffsetExpand);

d3.csv("data.csv", type, function(error, data) {
  if (error) throw error;

  data.sort(function(a, b) { return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total; });

  y.domain(data.map(function(d) { return d.State; }));
  z.domain(data.columns.slice(1));

  var serie = g.selectAll(".serie")
    .data(stack.keys(data.columns.slice(1))(data))
    .enter().append("g")
      .attr("class", "serie")
      .attr("fill", function(d) { return z(d.key); });

  serie.selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("y", function(d) { return y(d.data.State); })
      .attr("x", function(d) { return x(d[1]); })
      .attr("height", function(d) { return x(d[0]) - x(d[1]); })
      .attr("width", y.bandwidth());

  g.append("g")
      .attr("class", "axis axis--y")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(y));

  g.append("g")
      .attr("class", "axis axis--x")
      .call(d3.axisLeft(x).ticks(10, "%"));

  var legend = serie.append("g")
      .attr("class", "legend")
      .attr("transform", function(d) { var d = d[d.length - 1]; return "translate(" + (y(d.data.State) + y.bandwidth()) + "," + ((x(d[0]) + x(d[1])) / 2) + ")"; });

  legend.append("line")
      .attr("x1", -6)
      .attr("x2", 6)
      .attr("stroke", "#000");

  legend.append("text")
      .attr("y", 9)
      .attr("dy", "0.35em")
      .attr("fill", "#000")
      .style("font", "10px sans-serif")
      .text(function(d) { return d.key; });
});

function type(d, i, columns) {
  for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
  d.total = t;
  return d;
}

参考 example

您需要反转域:

  var y = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.1)
    .align(0.1);

  var x = d3.scaleLinear()
    .rangeRound([height, 0]);

将 x 交换为 y,因为域是相反的,因此当您创建矩形时,x 将变为 y,y 将变为 x。

serie.selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter().append("rect")
  .attr("y", function(d) {
    return y(d.data.State);
  })
  .attr("x", function(d) {
    return x(d[1]);
  })
  .attr("width", function(d) {
    return x(d[0]) - x(d[1]);
  })
  .attr("height", y.bandwidth());

相应地更改图例位置以将其定位在顶部栏上。

var legend = serie.append("g")
  .attr("class", "legend")
  .attr("transform", function(d) {
    var d = d[0];//get the top data for placing legends on that.
    return "translate(" +  ((x(d[0]) + x(d[1])) / 2) + ", " +(y(d.data.State) - y.bandwidth())+ ")";
  });

最后定位图例线:

legend.append("line")
  .attr("y1", 5)
  .attr("x1", 15)
  .attr("x2", 15)
  .attr("y2", 12)
  .attr("stroke", "#000");

工作代码here

下面的例子也会对你有所帮助

var initStackedBarChart = {
 draw: function(config) {
  me = this,
  domEle = config.element,
  stackKey = config.key,
  data = config.data,
  margin = {top: 20, right: 20, bottom: 30, left: 50},
  parseDate = d3.timeParse("%m/%Y"),
  width = 960 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom,
  xScale = d3.scaleLinear().rangeRound([0, width]),
  yScale = d3.scaleBand().rangeRound([height, 0]).padding(0.1),
  color = d3.scaleOrdinal(d3.schemeCategory20),
  xAxis = d3.axisBottom(xScale),
  yAxis =  d3.axisLeft(yScale).tickFormat(d3.timeFormat("%b")),
  svg = d3.select("#"+domEle).append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var stack = d3.stack()
   .keys(stackKey)
   /*.order(d3.stackOrder)*/
   .offset(d3.stackOffsetNone);
 
  var layers= stack(data);
   data.sort(function(a, b) { return b.total - a.total; });
   yScale.domain(data.map(function(d) { return parseDate(d.date); }));
   xScale.domain([0, d3.max(layers[layers.length - 1], function(d) { return d[0] + d[1]; }) ]).nice();

  var layer = svg.selectAll(".layer")
   .data(layers)
   .enter().append("g")
   .attr("class", "layer")
   .style("fill", function(d, i) { return color(i); });

    layer.selectAll("rect")
     .data(function(d) { return d; })
   .enter().append("rect")
     .attr("y", function(d) { return yScale(parseDate(d.data.date)); })
     .attr("x", function(d) { return xScale(d[0]); })
     .attr("height", yScale.bandwidth())
     .attr("width", function(d) { return xScale(d[1]) - xScale(d[0]) });

   svg.append("g")
   .attr("class", "axis axis--x")
   .attr("transform", "translate(0," + (height+5) + ")")
   .call(xAxis);

   svg.append("g")
   .attr("class", "axis axis--y")
   .attr("transform", "translate(0,0)")
   .call(yAxis);       
 }
}
var data = [{"date":"4/1854","total":8571,"disease":1,"wounds":0,"other":5},{"date":"5/1854","total":23333,"disease":12,"wounds":0,"other":9},{"date":"6/1854","total":28333,"disease":11,"wounds":0,"other":6},{"date":"7/1854","total":28772,"disease":359,"wounds":0,"other":23},{"date":"8/1854","total":30246,"disease":828,"wounds":1,"other":30},{"date":"9/1854","total":30290,"disease":788,"wounds":81,"other":70},{"date":"10/1854","total":30643,"disease":503,"wounds":132,"other":128},{"date":"11/1854","total":29736,"disease":844,"wounds":287,"other":106},{"date":"12/1854","total":32779,"disease":1725,"wounds":114,"other":131},{"date":"1/1855","total":32393,"disease":2761,"wounds":83,"other":324},{"date":"2/1855","total":30919,"disease":2120,"wounds":42,"other":361},{"date":"3/1855","total":30107,"disease":1205,"wounds":32,"other":172},{"date":"4/1855","total":32252,"disease":477,"wounds":48,"other":57},{"date":"5/1855","total":35473,"disease":508,"wounds":49,"other":37},{"date":"6/1855","total":38863,"disease":802,"wounds":209,"other":31},{"date":"7/1855","total":42647,"disease":382,"wounds":134,"other":33},{"date":"8/1855","total":44614,"disease":483,"wounds":164,"other":25},{"date":"9/1855","total":47751,"disease":189,"wounds":276,"other":20},{"date":"10/1855","total":46852,"disease":128,"wounds":53,"other":18},{"date":"11/1855","total":37853,"disease":178,"wounds":33,"other":32},{"date":"12/1855","total":43217,"disease":91,"wounds":18,"other":28},{"date":"1/1856","total":44212,"disease":42,"wounds":2,"other":48},{"date":"2/1856","total":43485,"disease":24,"wounds":0,"other":19},{"date":"3/1856","total":46140,"disease":15,"wounds":0,"other":35}];
var key = ["wounds", "other", "disease"];
initStackedBarChart.draw({
 data: data,
 key: key,
 element: 'stacked-bar'
});
.axis text {
  font: 10px sans-serif;
}
.axis line,
.axis path {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
.path-line {
  fill: none;
  stroke: yellow;
  stroke-width: 1.5px;
}
svg {
  background: #f0f0f0;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id='stacked-bar'></div>