D3.js - Gestapeltes Barchart von JSON?
Ich habe versucht, diese Geige nachzubilden: https://jsfiddle.net/1c5eqrp3/3/
Im Kontext eines horizontal gestapelten Balkendiagramms, das ich mit meinen Daten erstelle.
var bar1Data = [{
position: 1,
label: '70k',
value: 1708026
},
{
position: 2,
label: '71K - 149K',
value: 1915059
},
];
//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
return d3.ascending(a.position, b.position);
})
var colors = ['green', 'blue'];
var bars = d3.select('#bars')
.append('svg')
.attr('width', 800)
.attr('height', 200);
bars.selectAll('rect')
.data(bar1Data)
.enter()
.append('rect')
.attr('width', function(d) {
return d;
})
.attr('x', function(d, i) {
return sum(bar1Data.value, 0, i);
})
.attr('fill', function(d, i) {
return colors[i];
})
.attr('y', 0)
.attr('height', 100);
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="bar"></div>
Alles lief gut, bis ich versuchte, die von mir erstellten bar1Data zu lesen.
Jetzt erhalte ich folgende Fehler:
Error: <rect> attribute width: Expected length, "[object Object]".
(anonymous) @ d3.js:1211
selection_each @ d3.js:1176
selection_attr @ d3.js:1233
(anonymous) @ bars3.js:23
Promise.then (async)
(anonymous) @ bars3.js:1
bars3.js:33 Uncaught (in promise) TypeError: Cannot read property '0' of undefined
at sum (bars3.js:33)
at SVGRectElement.<anonymous> (bars3.js:26)
at SVGRectElement.<anonymous> (d3.js:1209)
at Selection.selection_each [as each] (d3.js:1176)
at Selection.selection_attr [as attr] (d3.js:1233)
at bars3.js:25
Ich hatte große Probleme, mit d3.v5 ein gestapeltes horizontales Balkendiagramm zu erstellen. Ich habe verschiedene Beispiele mit d3.v4 ausprobiert, aber ich konnte sie nicht zum Laufen bringen. Dies war das einfachste und leichteste Beispiel, das ich gefunden habe. Aber es enthält noch keine Skalen (die ich hoffentlich etwas später einfügen werde).
Könnten Sie mir bitte helfen, dieses Problem mit einer Erklärung zu lösen?
Antworten
In .attr('width', function(d){ return d;})legen Sie die Breite der Leiste für das gesamte Objekt fest d. Sie müssen verstehen, dass .data(bar1Data)ein Eintrag pro Wert von erstellt bar1Datawird und dass auf diesen Wert wie din all diesen Funktionen zugegriffen werden kann. Das bedeutet, dass Sie .data(bar1Data)dieses Objekt nicht mehr direkt berühren sollten , sobald Sie bestanden haben.
Aus Ihren Daten scheint ich nun zu verstehen, dass Sie die Werte aller vorhergehenden Einträge verwenden möchten, um die kumulative Summe bis zu diesem Punkt zu berechnen. Ich werde das in der Antwort umsetzen, möchte Sie aber auch darauf hinweisen stack = d3.stack(), was es für Sie tut.
Beachten Sie auch, dass Ihre Werte zu groß sind. Sie müssen sie mithilfe einer Domain transformieren, andernfalls erhalten Sie ein Pixel pro Dollar (oder eine andere Geldeinheit). Dies bedeutet, dass die Balken 100-mal so breit wie Ihr Monitor rechts gezeichnet sind.
var bar1Data = [{
position: 1,
label: '70k',
value: 50
},
{
position: 2,
label: '71K - 149K',
value: 40
},
];
//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
return d3.ascending(a.position, b.position);
})
var colors = ['green', 'blue'];
var bars = d3.select('#bars')
.append('svg')
.attr('width', 800)
.attr('height', 200);
bars.selectAll('rect')
.data(bar1Data)
.enter()
.append('rect')
.attr('width', function(d) {
return d.value;
})
.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.
return sum(bar1Data.map((e) => e.value), 0, i);
})
.attr('fill', function(d, i) {
return colors[i];
})
.attr('y', 0)
.attr('height', 100);
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>