[英]d3js - How to plot a multi line chart chart for multiple groups of items that can be updated?
我是 d3 的新手,目前有一个简单的折线图,可以为单个组的某些数据显示两条线。 我想做的是能够以灵活的方式为多个组创建多行。 每个组将有几行,它们将为组父级进行颜色编码 - 在我的数据中,在这种情况下,它是资产或股票行情
目前您可以在我的代码中看到我正在为每个路径手动创建一个数组,但我想如果我开始拥有 100 条路径,这可能会变得非常混乱。 有一个更好的方法吗?
我试过使用 d3.nest,但我一生都无法弄清楚数据应该如何,因此可以为每组行应用更新/进入/退出模式
这是我的 WIP 代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<div id="graphDiv"></div>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var data_set = [{
'Date': '2009-03-23',
'Raw': 25,
'Raw2': 25,
'Asset': 'A'
},
{
'Date': '2009-03-24',
'Raw': 28,
'Raw2': 25.4,
'Asset': 'A'
},
{
'Date': '2009-03-25',
'Raw': 26,
'Raw2': 25.37,
'Asset': 'B'
},
{
'Date': '2009-03-26',
'Raw': 22,
'Raw2': 25.03,
'Asset': 'B'
},
{
'Date': '2009-03-27',
'Raw': 19,
'Raw2': 24.42,
'Asset': 'C'
},
{
'Date': '2009-03-28',
'Raw': 23,
'Raw2': 24.28,
'Asset': 'D'
}
]
var margin = {
top: 30,
right: 50,
bottom: 30,
left: 50
};
var svgWidth = 600;
var svgHeight = 1000;
var graphWidth = svgWidth - margin.left - margin.right;
var graphHeight = svgHeight - margin.top - margin.bottom;
// var parseDate = d3.timeParse("%d/%m/%Y");
var parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, graphWidth]);
var y = d3.scaleLinear().range([graphHeight, 0]);
var z = d3.scaleOrdinal(d3.schemeCategory10); // for colours
var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(10);
// Need to create the lines manually for each bit of data
var line = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.y);
});
// Creates the SVG area within the div on the dom
// Just doing this once
var svg = d3.select("#graphDiv")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
.call(d3.zoom().on("zoom", function() {
svg.attr("transform", d3.event.transform)
}));
// Add the X Axis
g.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + graphHeight + ")")
.call(xAxis);
// Text label for x axis
g.append("text")
.style("text-anchor", "middle")
.text("timeseries dates")
.attr("transform", "translate(" + (graphWidth / 2) + " ," + (graphHeight + margin.top) + ")");
// Add the Y Axis
g.append("g")
.attr("class", "y axis")
.call(yAxis);
// text label for the y axis
g.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (graphHeight / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("price points");
function drawGraph(data_set) {
let pathData = []
//assume 2 paths
pathData.push([])
pathData.push([])
// Pass in the data here
data_set.forEach(function(d) {
let path0 = {}
let path1 = {}
path0.date = parseDate(d.Date)
path1.date = parseDate(d.Date)
path0.y = +d.Raw
path1.y = +d.Raw2
pathData[0].push(path0)
pathData[1].push(path1)
});
x.domain(d3.extent(data_set, function(d) {
return parseDate(d.Date);
}));
y.domain([
d3.min(data_set, function(d) {
return Math.min(d.Raw, d.Raw2)
}),
d3.max(data_set, function(d) {
return Math.max(d.Raw, d.Raw2)
})
]);
var lines = g.selectAll(".path")
.data(pathData)
lines.exit().remove()
var enter = lines.enter()
.append("path")
.attr("class", "path")
.style("stroke", (d, i) => z(i))
var merge = enter.merge(lines)
.attr("d", line)
}
// display initial chart
window.onload = drawGraph(data_set)
// Push new data every 5 seconds for a specific date
var h = setInterval(function() {
data_set.push({
'Date': '2009-03-29',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
}, {
'Date': '2009-03-30',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
}, {
'Date': '2009-03-31',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
}, {
'Date': '2009-04-01',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
}, {
'Date': '2009-04-02',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
}, {
'Date': '2009-04-03',
'Raw': Math.floor(Math.random() * 50),
'Raw2': Math.floor(Math.random() * 25),
'Asset': 'A'
});
drawGraph(data_set);
}, 5000);
</script>
</body>```
您的代码中有几个问题:
你没有更新你的轴。 因此,您的 x 轴显示的是 2000 年 1 月的日期,而不是 2009 年 3 月/4 月的日期。 要解决此问题,您必须将drawGraph
函数中的轴更新为:
svg.selectAll('.x.axis').call(xAxis); svg.selectAll('.y.axis').call(yAxis);
在循环的第一次迭代之后,您使用setInterval
测试不断地重复发送具有相同日期的数据。 因此,您的data_set
将相同日期的数据传递到您的pathData
。 为了解决这个问题,测试最好构建为:
let newdata = [{ 'Date': '2009-03-29', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }, { 'Date': '2009-03-30', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }, { 'Date': '2009-03-31', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }, { 'Date': '2009-04-01', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }, { 'Date': '2009-04-02', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }, { 'Date': '2009-04-03', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' } ] let counter = 0; // Push new data every 5 seconds for a specific date var h = setInterval(function () { if (counter < newdata.length) { // this limits the steps to only the length of the newdata data_set.push(newdata[counter]); counter++; drawGraph(data_set); } }, 3000);
但是,如果您预计新数据将在相同的日期进入,则必须在使用以下drawGraph
在setInterval
调用drawGraph
之前更新data_set
:
// Push new data every 5 seconds for a specific date
var h = setInterval(function () {
//obtain the index from the data_set for the same date
let index = data_set.findIndex((f) => f.Date === newdata[counter].Date);
if (index === -1) { // if data with same date not found push the new data
data_set.push(newdata[counter]);
} else { //else if it is found replace the old data
data_set[index] = newdata[counter];
}
counter++;
drawGraph(data_set);
}, 3000);
工作块在这里。 注意,在这个例子中, newdata
也包含在结束旧日期一些更多的数据。 因此,您将看到线条根据新日期上下跳跃。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.