I am trying to display a beautiful line graph using D3. The problem I have is with the format of the data.
I have the following data (as an example):
var data = [
{
label: "name",
data: [[14444123, 0.012321312],
[14444123, 0.012321312],
[14444123, 0.012321312], ...]
},{
label: "another name",
data: [[14444123, 0.012321312],
[14444123, 0.012321312],
[14444123, 0.012321312], ...]
}
];
Each entry contains the name of it as well as a data attribute with array of points (each point is represented as an array, where item[0] is x timestamp and item[1] is the value).
My problem is that it is not working co开发者_开发百科rrectly.
This is the D3 code I have as of now:
var w = options.width,
h = options.height,
p = options.padding,
x = d3.scale.linear()
.domain([0, 1])
.range([0, w]),
y = d3.scale.linear()
.domain([options.ydomainstart, options.ydomainend])
.range([h, 0]);
var vis = d3.select(options.element)
.data(data)
.append("svg:svg")
.attr("width", w + p * 2)
.attr("height", h + p * 2)
.append("svg:g");
vis.append("svg:line")
.attr("stroke", '#808080')
.attr("x1", p)
.attr("x2", p)
.attr("y1", 0)
.attr("y2", h - p);
vis.append("svg:line")
.attr("stroke", '#808080')
.attr("x1", p)
.attr("x2", w)
.attr("y1", h - p)
.attr("y2", h - p);
var rules = vis.selectAll("g.rule")
.data(data)
.enter()
.append("svg:text")
.attr("x", w - p)
.attr("y", function(d, i) { return 15 + i*12; })
.attr("text-anchor", "end")
.attr("font-size", 12)
.attr("fill", function(d, i) { return defaultColors[i % 5]; })
.text(function(d) { return d.label;});
var lines = rules.data(function(d, i) {
return d.data;
})
.append("svg:path")
.attr("stroke", function(d, i) { return defaultColors[i % 5]; })
.attr("d", d3.svg.line()
.x(function(d) {
return x(d[0]);
})
.y(function(d) {
return y(d[1]);
}));
The problem I have appears in this part of the code:
.x(function(d) {
return x(d[0]);
})
.y(function(d) {
return y(d[1]);
}));
The data inside 'd' is NOT the point array [x, y] but instead each value inside each array.
Meaning, on first item, d contains the x coordinate, on second item, it has the y coordinate, on third item, it contains the x coordinate on next point and so on.
It's like it's recursively going into the array, and then again for each value inside.
I have no idea how to fix this.
There’s a few problems here.
First, you’re appending an svg:path element to an svg:text element. It seems to me like you’re trying to create an svg:g element with the class "rule", but your code defines the selection rules
as a set of svg:text elements. Create the svg:g elements first, and then append svg:text elements:
var rules = vis.selectAll("g.rule")
.data(data)
.enter().append("svg:g")
.attr("class", "rule");
rules.append("svg:text")
…
The second problem is that the data operator is evaluated once per group, rather than once per element. See the section "Operating on Selections" in the API reference for more details. You have one svg:svg element in vis
, so you have one group in rules
, and so your data function is only called once:
function(d, i) {
return d.data;
}
Then, the resulting data elements are mapped to the rules
selection… which already have defined data from the previous selectAll and append when they were created.
The simple fix is to use the map operator rather than the data operator, which is evaluated once per element rather than once per group.
rules.append("svg:path")
.map(function(d) { return d.data; })
.attr("d", d3.svg.line()
…
Alternatively, you could pass the data directly to the line generator, but that requires you declaring the line generator ahead of time rather than inlining it:
var line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
rules.append("svg:path")
.attr("d", function(d) { return line(d.data); })
…
Hope this helped!
精彩评论