D3.js - Stacked Barchart từ JSON?
Tôi đã cố gắng tạo lại trò chơi này: https://jsfiddle.net/1c5eqrp3/3/
Trong bối cảnh biểu đồ thanh xếp chồng theo chiều ngang mà tôi đang xây dựng bằng dữ liệu của mình.
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>
Tất cả đều diễn ra tốt đẹp, cho đến khi tôi cố gắng làm cho nó đọc được dữ liệu bar1 mà tôi đã tạo.
Bây giờ, tôi gặp các lỗi sau:
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
Tôi đã rất vất vả để tạo một biểu đồ thanh ngang xếp chồng lên nhau bằng cách sử dụng d3.v5. Tôi đã thử các ví dụ khác nhau bằng cách sử dụng d3.v4 nhưng tôi không thể làm cho chúng hoạt động. Đây là ví dụ đơn giản nhất, nhẹ nhất mà tôi đã tìm thấy. Nhưng nó vẫn chưa bao gồm các thang âm (hy vọng tôi sẽ đưa vào một chút sau).
Bạn có thể vui lòng giúp tôi giải quyết vấn đề này với một lời giải thích?
Trả lời
Trong đó .attr('width', function(d){ return d;})
, bạn đang thiết lập chiều rộng của thanh cho toàn bộ đối tượng d
. Bạn cần hiểu rằng điều đó .data(bar1Data)
tạo ra một mục nhập cho mỗi giá trị bar1Data
và giá trị đó có thể truy cập được như d
trong tất cả các hàm đó. Điều đó có nghĩa là, ngay sau khi bạn đi qua .data(bar1Data)
, bạn không nên trực tiếp chạm vào đối tượng đó nữa.
Bây giờ, từ dữ liệu của bạn, tôi dường như hiểu rằng bạn muốn sử dụng các giá trị của tất cả các mục trước để tính tổng tích lũy cho đến thời điểm đó. Tôi sẽ triển khai điều đó trong câu trả lời, nhưng cũng muốn chỉ cho bạn stack = d3.stack()
, điều đó sẽ giúp ích cho bạn.
Cũng lưu ý rằng giá trị của bạn quá lớn. Bạn cần phải chuyển đổi chúng bằng cách sử dụng miền, nếu không, bạn sẽ nhận được một pixel trên mỗi đô la (hoặc đơn vị tiền khác), có nghĩa là các thanh được vẽ bằng 100 lần chiều rộng màn hình của bạn ở bên phải.
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>