D3- How do I draw dots on a multi line graph? (Iterate through array)


D3- How do I draw dots on a multi line graph? (Iterate through array)



I have a multi line chart on which I would like to draw dots ( eventually to add my tooltip to.) I just can't seem to iterate through the data correctly and draw them all. My data is an array of arrays, each sub array being it's own line on which i'd like to draw the dots. x(inspected_at), y(flow_data)



The full code is here, around line 388: https://codepen.io/lahesty/pen/aKQjVK?editors=0011


var dots =
svg.selectAll('.dots').data(data).enter().append("g").attr("class",
"dot");
dots.selectAll('.dot')
.data(data)
.enter()
.append('circle')
.attr("r", 2.5)
.attr("cx", function(d) { return x(d.inspected_at); })
.attr("cy", function(d) { return y(d.flow_data); })
.style("fill", "blue").style("opacity", ".5")



I assume it's something similar because when I change the second .data(data) to .data(data[0]) The first array of dots will appear, but I'd like them all to. I also tried something like this, instead of the above:


svg.selectAll("g.dot")
.data(data)
.enter().append("g")
.attr("class", "dot")
.selectAll("circle")
.data(function(d, i) { return d[i]; })
.enter().append("circle")
.attr("r", 6)
.attr("cx", function(d,i) { return x(d.inspected_at); })
.attr("cy", function(d,i) { return y(d.flow_data); })



I use v4. Any thoughts? Your help is very much appreciated!




2 Answers
2



I'd also say you're almost there - here's how you can move forward: The first argument to data() needs to be either


data()



You can (and poss. should) give a key function.



So, the outer call to data() becomes


data()


var dots = svg.selectAll('.dots').data(data, function(d, i) { return d[0]; }).enter() // and so on



the inner call draws upon each row of the outer (i.e. data[0], data[1],
etc.) and selects the collection for inner. Conveniently, this is what you get - but unless you use a function, you'll never be able to tell D3 what to do. So, the following is a little crazy:


data[0]


data[1]


dots.selectAll('.dot')
.data(function(d) { return d; }, function(d_, i_) { return i_})
.enter() // and so on



but you need a function returning its own param (and a key generator for good measure). I did fork your pen - c here: https://codepen.io/sebredhh/pen/VdVOxQ?editors=0010 and changed lines 389 and 391. Looks like what you were looking for - hope you find this helpful...





Ah! Somehow I didn't realize that data() should resolve to an object. That makes sense. Would you mind clarifying what the underscores accomplish? Furthermore a follow up question, what about my clip-path? It used to be appended to a 'g' object (as it is for my lines) but on the dots it just cuts them off. Thanks!
– LaurenAH
2 days ago





Nevermind! Just figured out that I needed to add a ('g') group and append my clip-path to that, right before I select all my dots. I like both of these methods!
– LaurenAH
2 days ago






"The first argument to data() needs to be either an object or a function resolving to an object"... well, that's unfortunately incorrect: data doesn't accept objects, it accepts only 3 things: an array, a function or nothing. It does accept a function that returns an object, though, as you said, but not just an object.
– Gerardo Furtado
2 days ago



data





I just put in underscores to clarify what is the first level VS the second so the variables are a little more descriptive. There is no functionality associated with it. As for 'object', Gerardo is right: it's not just any object but an array. Sorry for the confusion (will fix above).
– Sebastian Rothbucher
2 days ago



You're almost done. Once you've entered the g.dot selection, you have to keep the same binding in order to propagate the data to the circles. This can be done with the identity function, either function(d) { return d; } or Object.


g.dot


function(d) { return d; }


Object



The inner iteration will be implicitly done within the Selection.


Selection


svg.selectAll("g.dot")
.data(data)
.enter().append("g")
.attr("class", "dot")
.selectAll("circle")
.data(Object) // <--- identity function here
.enter().append("circle")
.attr("r", 6)
.attr("cx", function(d,i) { return x(d.inspected_at); })
.attr("cy", function(d,i) { return y(d.flow_data); })





Interesting! I like the brevity of this. If you don't mind follow up, how might I add a clip path to this? I'm not sure how I can fit that in with the g.dot selection. Thank you!
– LaurenAH
2 days ago





Nevermind! I actually figured it out for this method. I changed: svg.selectAll("g.dot") to: svg.append('g').attr('clip-path', 'url(#clipper)').selectAll("g.dot") Looks like I needed to add a ('g') group and append my clip-path to that.
– LaurenAH
2 days ago







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Possible Unhandled Promise Rejection (id: 0): ReferenceError: user is not defined ReferenceError: user is not defined

Opening a url is failing in Swift