Reputation: 1036
I have nested data, as follows:
data
> key
> values
> date
> reldif
> absdif
Everything works fine in the line diagram:
var line = svg.selectAll(".lines")
.data(data, function(d) { return d.key; })
.enter()
.append("g")
.attr("class", "lines")
.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.key); })
.style("stroke-width", 4)
However, trying to use the same technique for circles, I am failing miserably. As far as I understand, I need x, y and r (which can be absolute, like 5 or so). X should be date, Y should be reldif. How do I access these values? If I just use d.values it hands over an array that I cannot process in this way I guess.
(In general - if you have any suggestions on tutorials for nested data in D3, I'm happy to hear those suggestions. The ones I find treat every topic except for using them for graphs)
Upvotes: 2
Views: 164
Reputation: 34288
To deal with nested data, you can create nested selections. What is confusing here is that lines
did not need any nested selections.
This way of drawing lines is a little special in D3 since it is drawn using the path
SVG element. The path
element has one attribute d
which accepts a DSL. This string is a tad bit cumbersome and, hence, D3 provides helper objects d3.svg.line
.
On this helper object, one can set x
and y
accessors which will decide the x
and y
points which make the line. This object also allows you to set many other properties of the line (like which kind of interpolation you want). Finally, this helper object can be called as a function data
and it will produce the DSL needed for the d
attribute for a path.
This is how it is being used in the example you have pasted here: .attr("d", function(d) { return line(d.values); })
. Somewhere before, this line
object was created via a call to d3.svg.line
and it probably has x
accessor set to .x(function (d) { return d.date; })
and y
accessor set to .y(function (d) { return d.reldif; }
.
Note: you are overriding the line
variable when you assign the result of the call the line
again. JSHint will warn you if you do this.
Now the circle
element is considerably simpler. Here, you do not need any helper functions because, as you point out, you can set the x
, y
and r
attributes directly without an intermediate DSL. Hence, you it would look something like this:
var circles = svg.selectAll(".circles")
.data(data, function(d) { return d.key; })
.enter()
.append("g")
.attr("class", "circles")
.selectAll(".circle")
.data(function (d) { return d.values; })
.enter()
.append("circle")
.attr("class", "circle")
.attr("cx", function(d) { return d.date; })
.attr("cy", function(d) { return d.reldif; })
.attr("r", 5)
.style("stroke", function(d) { return color(d.key); })
.style("stroke-width", 4)
If d.values.date
is actually a date
object instead of a number, then you can use a time
scale to convert it to nice numbers.
Upvotes: 1
Reputation: 109242
There's a fundamental difference between lines and circles -- lines represent collections of points, while circles represent single points. In practice, this means that the data element you use to create a line is an array, while it is only a single data point for a circle.
In your example, you're passing an array (d.values
) to the drawing primitive. This doesn't work for circles because it expects a single datum. What you need for that case are nested selections that introduce a grouping element for the circles belonging to the same "line". The code would look something like this.
svg.selectAll("g")
.data(data, function(d) { return d.key; })
.enter()
.append("g")
.selectAll("circle")
.data(function(d) { return d.values; })
.enter()
.append("circle")
.attr("cx", function(d) { return x(d.date); })
// etc
The first part of the code is identical, but after appending the g
elements, you're making a subselection for circle
s that represent the elements of d.values
. After that, the code is straightforward again with circle
s appended and configured in the usual manner.
On a general note, the way nested data is used for a graph is highly dependent on the particular graph -- a collapsible tree is different from a chord diagram. The tutorial on nested selections is worth reading, but in this particular case it's not really a problem of nested data, but of the different things different graphical elements represent.
Upvotes: 2