条形值较小的堆叠条形图 [英] Stacked bar chart with smaller bar value
问题描述
在下面的代码片段中,我尝试使用对数刻度使我的较小条形可见,但这导致上部条形变小.我是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.
我不知道您要使用layer1
和layer2
的什么.
您必须添加mouseover
和mouseout
.
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屋!