条形值较小的堆叠条形图 [英] Stacked bar chart with smaller bar value

查看:103
本文介绍了条形值较小的堆叠条形图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码片段中,我尝试使用对数刻度使我的较小条形可见,但这导致上部条形变小.我是D3的新手,真的很难使它工作.我想让条形图可见,以便标签在条形图区域内可见,现在它与其他较小的条形图标签重叠.

In my below code snippet i tried to make my smaller bar visible using log scale but this cause upper bars to render small. I'm novice to D3, really struggling to make it work. i want to make my bar visible so that labels will be visible inside bar area, now it's over lapping with other smaller bar labels.

var data = [{"month":"JAN","Insolvency":"1","Operating Company":"1","SPV / Asset Backed":"0","Operational Company":"0","TBD":"0"},{"month":"FEB","Insolvency":"1","Operating Company":"11","SPV / Asset Backed":"9","TBD":"1","Operational Company":"0"},{"month":"MAR","Insolvency":"3","Operating Company":"44","SPV / Asset Backed":"33","TBD":"8","Operational Company":"0"},{"month":"APR","Insolvency":"3","Operating Company":"27","SPV / Asset Backed":"31","TBD":"3","Operational Company":"0"},{"month":"MAY","Operating Company":"2","SPV / Asset Backed":"5","TBD":"1","Operational Company":"0","Insolvency":"0"},{"month":"JUL","Insolvency":"44","Operating Company":"9","TBD":"1","SPV / Asset Backed":"0","Operational Company":"0"},{"month":"AUG","Operating Company":"24","SPV / Asset Backed":"30","TBD":"2","Operational Company":"0","Insolvency":"0"},{"month":"SEP","Insolvency":"189","Operating Company":"74","SPV / Asset Backed":"20","Operational Company":"0","TBD":"0"},{"month":"OCT","Insolvency":"3","Operating Company":"16","SPV / Asset Backed":"54","Operational Company":"0","TBD":"0"},{"month":"NOV","Insolvency":"1","Operating Company":"3","SPV / Asset Backed":"25","TBD":"3","Operational Company":"0"},{"month":"DEC","Insolvency":"6","Operating Company":"0","SPV / Asset Backed":"0","Operational Company":"0","TBD":"0"}];
    	
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div").attr("class", "tooltip")
		.style("opacity", 0);

var margin = {
	top : 40,
	right : 50,
	bottom : 30,
	left : 50
}, width = 500, height = 300, padding = 100;

var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .05);

var y = d3.scale.log().range([ height, 0.5 ]);

var color = d3.scale.category10();

var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(0, ".1s")
		.innerTickSize(-width).tickPadding(10);

var svg = d3
		.select("#ashu")
		.append("svg")
		.attr("width", "100%")
		.attr("height", "100%")
		.append("g")
		.attr("transform",
				"translate(" + margin.left + "," + margin.top + ")");

var dataIntermediate = xData.map(function(c) {
	return data.map(function(d, yData) {
		return {
			x : d[Object.keys(d)[0]],
			y : +d[c]
		};
	});
});

var totals = d3.nest().key(function(d) {
	return d.health;
}).rollup(function(d) {
	return d3.sum(d, function(g) {

		return d3.sum(d3.values(g));
	});
}).entries(data);


var dataStackLayout = d3.layout.stack()(dataIntermediate);

x.domain(dataStackLayout[0].map(function(d) {
	return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1],
		function(d) {
			return (d.y + d.y0);
		});
y.domain([ 0.5, maximumY ]).base(2).nice().clamp(true);

var layer1 = svg.selectAll(".stack1").data(dataStackLayout).enter()
		.append("g").attr("class", "stack1").style("fill",
				function(d, i) {
					return color(i);
				}).attr("transform",
						"translate(5,0)");

layer1.selectAll("rect").data(function(d) {
	return d;
}).enter().append("rect").attr("x", function(d) {
	return x(d.x);
}).attr("y", function(d) {
	return y(d.y + d.y0);
}).attr("height", function(d) {
	return y(d.y0) - y(d.y + d.y0);
}).attr("width", x.rangeBand()-2);

layer1.selectAll("text").data(totals).enter().append("text").text(
		function(d) {
			return d.values;
		}).attr("x", function(d, i) {
	return x(d.key) + (x.rangeBand()) / 2;
}).attr("y", function(d) {
	return y(d.values + 10);
}).attr("dy", "0.35em").attr("text-anchor", "middle").style("fill",
		"#1a8cff");


var layer2 = svg.selectAll(".stack2").data(dataStackLayout).enter()
		.append("g").attr("class", "stack2").style("fill", "black");

layer2.selectAll(".shadow").data(function(d) {
	return d;
}).enter().append("text")
.attr("class", "shadow")
.text(function(d) {
	return (d.y === 0 ? '':d.y);
}).attr("x", function(d, i) {
	return x(d.x) + (x.rangeBand()) / 2;
}).attr("y", function(d) {
	return y(d.y0 + (d.y / 2));
}).attr("dy", "0.35em").attr("text-anchor", "middle").style("fill",
		"black");


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

svg.append("g").attr("class", "axis").attr("transform",
		"translate(15px,0)").call(y_axis);

var legend = svg.selectAll(".legend").data(color.domain().slice())
		.enter().append("g").attr("class", "legend").attr(
				"transform",
				function(d, i) {
					return "translate(0," + Math.abs((i - 8) * 20)
							+ ")";
				});

legend.append("rect").attr("x", width + 10).attr("width", 18).attr(
		"height", 18).style("fill", color);

legend.append("text").attr("class", "legendText").attr("x",
		width + 32).attr("y", 10).attr("dy", ".35em").style(
		"text-anchor", "start").text(function(d, i) {
	return xData[i];
});

.textRect {
	fill: grey;
	stroke: none;
}

text {
	font-family: 'Raleway', sans-serif;
	font-weight: bold;
	font-size: 13px;
}

path {
	color: green;
}

div.tooltip {
	position: absolute;
	text-align: center;
	padding: 7px;
	font: 12px sans-serif;
	background: #333333;
	border: 0px;
	border-radius: 8px;
	pointer-events: none;
	color: white;
}

.axis path, .axis line {
	fill: none;
	stroke: grey;
	stroke-width: 1;
	shape-rendering: crispEdges;
}

.grid .tick {
	stroke: lightgrey;
	stroke-opacity: 0.7;
	shape-rendering: crispEdges;
}

.grid path {
	stroke-width: 0;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<div id="ashu" style="width: 700px; height:400px;"></div>

推荐答案

我已将代码转换为D3v5版本.

I have converted the code to a D3v5 version.

无需转换数据,您可以使用对象.

No need to transform the data, you can work with objects.

我不知道您要使用layer1layer2的什么. 您必须添加mouseovermouseout.

I have no clue what you want with the layer1 and layer2. You have to add the mouseover and mouseout.

具有对数Y轴.使用2对数并不常见,因此我使用10对数. 但是现在破产情况看起来非常大.

With logarithmic Y-axis. Using a 2-log is not common so I used 10-log. But now the Insolvencies look very big.

用于JavaScript(health重命名为month)

for the JavaScript (health renamed to month)

var data = [{
    "month": "JAN",
    "INSOLVENCIES": 1,
    "SPV/ASSETBACKED": 67,
    "OPERATINGCOMPANIES": 13,
    "Bank": 15
  }, {
    "month": "FEB",
    "INSOLVENCIES": 60,
    "SPV/ASSETBACKED": 9,
    "OPERATINGCOMPANIES": 20,
    "Bank": 5
  }, {
    "month": "MAR",
    "INSOLVENCIES": 40,
    "SPV/ASSETBACKED": 22,
    "OPERATINGCOMPANIES": 21,
    "Bank": 99
  }, {
    "month": "APR",
    "INSOLVENCIES": 60,
    "SPV/ASSETBACKED": 1,
    "OPERATINGCOMPANIES": 99,
    "Bank": 90
  }, {
    "month": "MAY",
    "INSOLVENCIES": 2,
    "SPV/ASSETBACKED": 27,
    "OPERATINGCOMPANIES": 43,
    "Bank": 82
  }, {
    "month": "JUN",
    "INSOLVENCIES": 17,
    "SPV/ASSETBACKED": 52,
    "OPERATINGCOMPANIES": 79,
    "Bank": 9
  }, {
    "month": "JUL",
    "INSOLVENCIES": 37,
    "SPV/ASSETBACKED": 24,
    "OPERATINGCOMPANIES": 35,
    "Bank": 51
  }, {
    "month": "AUG",
    "INSOLVENCIES": 16,
    "SPV/ASSETBACKED": 17,
    "OPERATINGCOMPANIES": 53,
    "Bank": 38
  }, {
    "month": "SEP",
    "INSOLVENCIES": 15,
    "SPV/ASSETBACKED": 32,
    "OPERATINGCOMPANIES": 5,
    "Bank": 31
  }];
  let xKey = "month";
  let keys = d3.keys(data[0]).filter( e => e != xKey );
  var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

  var margin = {
      top: 20,
      right: 50,
      bottom: 30,
      left: 50
    },
    width = 400,
    height = 300,
    padding = 100;

  var x = d3.scaleBand()
    .rangeRound([0, width])
    .paddingInner(.05);

  var y = d3.scaleLog().range([height, 0]);
  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var xAxis = d3.axisBottom().scale(x);
  var y_axis = d3.axisLeft().scale(y).ticks(6); //.innerTickSize(-width).tickPadding(10);

  var svg = d3.select("#ashu")
    .append("svg")
    .attr("width", "100%")
    .attr("height", "100%");
  var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var dataStackLayout = d3.stack().keys(keys)(data);
  dataStackLayout[0].forEach(e => { e[0]=0.1; }); // do not start at 0

  x.domain(dataStackLayout[0].map(function(d) { return d.data[xKey]; }));
  var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) { return d[1]; });
  y.domain([0.1, maximumY]).base(10).nice();

  g.append("g")
    .selectAll("g")
    .data(dataStackLayout)
    .enter().append("g")
      .attr("fill", function(d) { return color(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d) { return x(d.data[xKey]); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth());

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

  g.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(0,0)")
    .call(y_axis);

  var legend = g.selectAll(".legend")
    .data(color.domain().slice())
    .enter().append("g")
    .attr("class", "legend")
    .attr("transform", function(d, i) { return "translate(0," + Math.abs((i - 8) * 20) + ")"; });

  legend.append("rect")
    .attr("x", width + 10)
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", color);

  legend.append("text")
    .attr("x", width + 32)
    .attr("y", 10)
    .attr("dy", ".35em")
    .style("text-anchor", "start")
    .text(function(d, i) { return keys[i]; });

<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>

修改

我没有使用对数刻度,而是添加了缩放功能.翻译是在range中完成的,这令我感到困惑.我找到了所需的方程式,但需要提示在哪里应用 https://stackoverflow.com/a/44359905/上的转换限制9938317 .

Instead of using a log-scale I have added a zoom functionality. The translation is done in the range and that confused me. I found the needed equation but needed a tip on where to apply the limitation on translate from https://stackoverflow.com/a/44359905/9938317.

当栏的高度足够高时,必须有条件地放置标签;或者当不放置标签时,必须使用工具提示.

You have to place the labels conditionally when the height of the bar is tall enough or use tooltips when you do not place a label.

添加工具提示有点困惑.我不知道一种获取附加到父节点的数据的方法,因此我将组的键复制到了各个条形区域中. (也许 https://stackoverflow.com/a/17459746/9938317 是一种解决方案)

Adding the tooltips was a little puzzle. I don't know a way to get the data that is attached to a parent node so I copied the key of the group over to the individual bar rects. (Maybe https://stackoverflow.com/a/17459746/9938317 is a solution)

var data = [{
    "month": "JAN",
    "INSOLVENCIES": 1,
    "SPV/ASSETBACKED": 67,
    "OPERATINGCOMPANIES": 13,
    "Bank": 15
  }, {
    "month": "FEB",
    "INSOLVENCIES": 60,
    "SPV/ASSETBACKED": 9,
    "OPERATINGCOMPANIES": 20,
    "Bank": 5
  }, {
    "month": "MAR",
    "INSOLVENCIES": 40,
    "SPV/ASSETBACKED": 22,
    "OPERATINGCOMPANIES": 21,
    "Bank": 99
  }, {
    "month": "APR",
    "INSOLVENCIES": 60,
    "SPV/ASSETBACKED": 1,
    "OPERATINGCOMPANIES": 99,
    "Bank": 90
  }, {
    "month": "MAY",
    "INSOLVENCIES": 2,
    "SPV/ASSETBACKED": 27,
    "OPERATINGCOMPANIES": 43,
    "Bank": 82
  }, {
    "month": "JUN",
    "INSOLVENCIES": 17,
    "SPV/ASSETBACKED": 52,
    "OPERATINGCOMPANIES": 79,
    "Bank": 9
  }, {
    "month": "JUL",
    "INSOLVENCIES": 37,
    "SPV/ASSETBACKED": 24,
    "OPERATINGCOMPANIES": 35,
    "Bank": 51
  }, {
    "month": "AUG",
    "INSOLVENCIES": 16,
    "SPV/ASSETBACKED": 17,
    "OPERATINGCOMPANIES": 53,
    "Bank": 38
  }, {
    "month": "SEP",
    "INSOLVENCIES": 15,
    "SPV/ASSETBACKED": 32,
    "OPERATINGCOMPANIES": 5,
    "Bank": 31
  }];
  let xKey = "month";
  let keys = d3.keys(data[0]).filter( e => e != xKey );
  var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

  var margin = {
      top: 20,
      right: 50,
      bottom: 30,
      left: 50
    },
    width = 400,
    height = 300,
    padding = 100;

  var x = d3.scaleBand()
    .rangeRound([0, width])
    .paddingInner(.05);

  var y = d3.scaleLinear().range([height, 0]);

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var xAxis = d3.axisBottom().scale(x);
  var yAxis = d3.axisLeft().scale(y).ticks(6); //.innerTickSize(-width).tickPadding(10);

  var svg = d3.select("#ashu")
    .append("svg")
    .attr("width", "100%")
    .attr("height", "100%");
  const clipPath = svg.append('defs')
    .append('clipPath')
    .attr('id', 'clip')
    .append('rect')
    .attr('width', width)
    .attr('height', height);

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

  var dataStackLayout = d3.stack().keys(keys)(data);
  // add the key to the bar elements
  dataStackLayout.forEach(keyElem => {
      var key = keyElem.key;
      keyElem.forEach(d => { d.key = key; });
  });

  x.domain(dataStackLayout[0].map(function(d) { return d.data[xKey]; }));
  var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) { return d[1]; });
  y.domain([0.1, maximumY]);

  var gBars = g.append("g")
    .attr("class", "bars")
    .attr('clip-path', 'url(#clip)');

  gBars.selectAll("g")
    .data(dataStackLayout)
    .enter().append("g")
      .attr("fill", function(d) { return color(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d) { return x(d.data[xKey]); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth())
      .append("title").text(function(d) { return d.key; });

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

  var gY = g.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(0,0)")
    .call(yAxis);

  var legend = g.selectAll(".legend")
    .data(color.domain().slice())
    .enter().append("g")
    .attr("class", "legend")
    .attr("transform", function(d, i) { return "translate(0," + Math.abs((i - 8) * 20) + ")"; });

  legend.append("rect")
    .attr("x", width + 10)
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", color);

  legend.append("text")
    .attr("x", width + 32)
    .attr("y", 10)
    .attr("dy", ".35em")
    .style("text-anchor", "start")
    .text(function(d, i) { return keys[i]; });

  var zoom = d3.zoom()
    .scaleExtent([1, 40])
    .on("zoom", zoomed);

  svg.call(zoom);

  d3.select("button")
    .on("click", resetted);

  function zoomed() {
    // https://stackoverflow.com/a/44359905/9938317
    var t = d3.event.transform;
    t.y = d3.min([t.y, 0]);
    t.y = d3.max([t.y, (1-t.k) * height]);
    var yTransform = t.rescaleY(y);
    gY.call(yAxis.scale(yTransform));
    gBars.selectAll("rect")
      .attr("y", function(d) { return yTransform(d[1]); })
      .attr("height", function(d) { return yTransform(d[0]) - yTransform(d[1]); });
  }

  function resetted() {
    svg.transition()
       .duration(750)
       .call(zoom.transform, d3.zoomIdentity);
  }

<script src="https://d3js.org/d3.v5.min.js"></script>
<button>Reset</button>
<div id="ashu" style="width: 700px; height:400px;"></div>

这篇关于条形值较小的堆叠条形图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆