D3.js JSON数据到线图
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D3.js JSON数据到线图相关的知识,希望对你有一定的参考价值。
我之前切换到这个图表的原因是因为我需要这个例子here的响应性。它的源代码可以在这里找到:here。
但是,我似乎无法在此图表中输入数据,我不确定我做错了什么。在我的路径'd
中,我得到了一堆MNaN,这表明我没有正确地传递数据(它最有可能不被视为数字,因此d3的路径生成器在第一个错误时抛出这些随机值(在这种情况下哪个好,因为第一个数字无效))...
Error: <path> attribute d: Expected number, "MNaN,-552LNaN,-60…".
我现在最好的问题是导致错误的是d3.extent
不明白我正在喂它。
我的数据对象看起来像这样:
[{
"name": "Data1",
"data" :
{
"resultset": [
[
1.42,
"2018-09-18 00:00:00"
],
[
1.92,
"2018-09-18 01:00:00"
],
[
1.32,
"2018-09-18 10:33:35"
],
[
0.00,
"2018-09-18 10:43:35"
]
],
"metadata": [
{}
],
"totalrows": 8
}}]
你们中的一些人可能想知道数据的来源,所以我将发布这个组件片段:
export class MachineGraphComponent implements AfterViewInit, OnInit, OnDestroy {
eventListeners: Function[] = [];
machines: GraphData[];
static componentCount: number = 0;
//graph settings
@Input() datasources: { name: any, data: PentahoResponse[] }[]; //Here
@Input() enableAxisX = 0;
@Input() enableAxisY = 0;
我的代码(在NgAfterViewInit中):
var datasources = this.datasources;
var graphSettings = {
enableAxisX: this.enableAxisX,
enableAxisY: this.enableAxisY
}
var currentId = this.thisId;
var drawGraph = function (datasources) {
$('#chart' + currentId + '.chart').empty();
// Define margins
var margin = { top: 20, right: 20, bottom: 30, left: 20 },
width = parseInt(d3.select('#chart' + currentId + '.chart').style("width")) - margin.left - margin.right,
height = parseInt(d3.select('#chart' + currentId + '.chart').style("height")) - margin.top - margin.bottom;
// Define date parser
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
//I have tried this one in extent, like in the example linked above ^
// Define scales
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal()
.range(["#8c510a", "#dfc27d", "#35978f"]);
// Define axes
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left");
// Define lines
var line = d3.svg.line().interpolate("basis")
.x(function (d) { return xScale(d["date"]); })
.y(function (d) { return yScale(d["value"]); });
// Define svg canvas
var svg = d3.select('#chart' + currentId + '.chart').append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Read in data
var datasource: {
actualValues: any,
date: string
}[] = [];
datasources.forEach(source => {
source.data.resultset.forEach(data => {
datasource.push({
actualValues: data[0],
date: data[1]
});
});
});
// Format the data field
// var data = datasource.slice(); //should i use this?
var format = d3.time.format("%b %e %Y");
var dateFn = function (d) {
return format.parse(d.date);
};
// tried to create a dateparser, but didnt solve the problem.
// Set the color domain equal to the three product categories
datasource.forEach(x => {
x.date = dateFn(x);
});
var DatasourceNames = d3.keys(datasources.map(function (d) {
return d.name;
}));
// console.log(datasources.map(function(d){console.log("dwadwa",d.name); return d.name;}));
color.domain(DatasourceNames);
var values = DatasourceNames.map(function (category) {
// console.log("here: ",datasources[category].data.resultset);
return {
category: category,
datapoints: datasource.map(function (d) {
return {
value: d.actualValues,
date: d.date
}
})
}
})
// Set the domain of the axes
xScale.domain(d3.extent(datasource, function (d) { return d; }));
yScale.domain([0.25, 0.5]);
// Place the axes on the chart
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("opacity", graphSettings.enableAxisX)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style("opacity", graphSettings.enableAxisY)
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("y", 6)
.attr("dy", ".71em")
.attr("dx", ".71em")
.style("text-anchor", "beginning")
.text("Product Concentration");
var products = svg.selectAll(".category")
.data(values)
.enter().append("g")
.attr("class", "category");
products.append("path")
.attr("class", "line")
.attr("d", function (d) { console.log(d.datapoints); return line(d.datapoints); })
.style("stroke", function (d) { return color(d.category); });
// console.log(JSON.stringify(d3.values(values), null, 2)) // to view the structure
// console.log(values.map(function()))
// Define responsive behavior
var resize = function () {
for (var i = 0; i < MachineGraphComponent.componentCount; i++) {
var svg = d3.select('#chart' + i + '.chart');
var width = parseInt(d3.select('#chart' + i + '.chart').style("width")) - margin.left - margin.right,
height = parseInt(d3.select('#chart' + i + '.chart').style("height")) - margin.top - margin.bottom;
// console.log(i, MachineGraphComponent.componentCount);
// Update the range of the scale with new width/height
xScale.range([0, width]);
yScale.range([height, 0]);
// Update the axis and text with the new scale
svg.select('.x.axis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select('.y.axis')
.call(yAxis);
// Force D3 to recalculate and update the line
svg.selectAll('.line')
.attr("d", function (d) { return line(d.datapoints); });
// Update the tick marks
xAxis.ticks(Math.max(width / 75, 2));
yAxis.ticks(Math.max(height / 50, 2));
}
};
// Call the resize function whenever a resize event occurs
d3.select(window).on('resize', resize);
// Call the resize function
resize();
};
(function () {
drawGraph(datasources);
// drawGraph(dataObj2);
})();
在尝试添加行时:
products.append("path")
.attr("class", "line")
.attr("d", function (d) { console.log(d.datapoints); return line(d.datapoints); })
.style("stroke", function (d) { return color(d.category); });
这个控制台日志给我这个:
如果我需要澄清或详细说明,请告诉我。
是的,D3.extent()
不明白你喂它是什么。我建议直接传递最小值和最大值。最小和最大日期隐藏在数据结构的两个深层,因此您需要通过两个级别的数组来查找它们。我首先将时间转换应用于原始数据结构,因此我们可以从D3.min命令获得正确的输出。同样适用于最大值
//start by cleaning the date in your data
// the original data structure will naow have a nice D3 date.
var TimeFormatter = d3.time.format("%Y-%m-%d %H:%M:%S");
datasources.forEach(source => {
source.data.resultset.forEach(data => {
data[1] = TimeFormatter.parse(data[1]);
});
});
//find the overall min of all lines and overall max of all limes by enumerating the datasources, then the data in each data source. I'm mapping the time data[1] to the d3.min or d3.max function.
var XoverallMin = d3.min(datasources, function(ds) { return d3.min(ds.data.resultset, function(data){return data[1]}) });
var XoverallMax = d3.max(datasources, function(ds) { return d3.max(ds.data.resultset, function(data){return data[1]}) });
//once we've found the min and max then setting the domain of the xScale is easy. :-)
// Set the domain of the axes
var xScale = d3.time.scale()
.range([0, 200])
.domain([XoverallMin,XoverallMax]);
您还将所有resultset
映射到一个大的datasource
,然后将同一组数据(将所有数据组合在一起)应用于每个唯一的数据源名称。相反,如果您为每个数据源创建一个对象,这可能会有所帮助。
// you had only one datasource total,
// i changed it so you have a datasource within each of the sources
datasources.forEach(source => {
source.datasource = Array();
source.data.resultset.forEach(data => {
source.datasource.push({
value: data[0],
date: data[1]
});
});
});
// ... later in the code,
// when you create your values object, the data will be separated by
// data source.
var values = datasources.map(function (source) {
return {
category: source.name,
datapoints: source.datasource //the new object we made and added to
// each source above
};
});
这不是一个完整的解决方案,整体结构有很多错误,但它应该让你超过一些特定的障碍。
以上是关于D3.js JSON数据到线图的主要内容,如果未能解决你的问题,请参考以下文章