I have a D3.js multiline graph with circles added on every path peak. When I update my graph, the paths update just fine with the new data but the circles don't seem to get updated at all. Here's my code: http://jsbin.com/eMuQOHoV/3/edit
Does anyone know what I'm doing wrong?
You need to update the data point circles in the same way that you've created them. In particular, you're using a nested selection when creating them, but not when updating. This means that the data won't get matched correctly on update and nothing happens.
The code for the update should look like this.
var sel = svg.selectAll('.series')
.data(sources);
sel.select('path')
.transition()
// etc
// update circles
sel.selectAll('.datapoint')
.data(function (d) {
return d.values;
})
// etc
Complete jsbin here.
Related
I'm trying to build kind of real time graph using D3.js. Code is available at https://plnkr.co/edit/hrawv8CTBIsJf2QWTBMb?p=preview.
The source data represent user authentication results from different organizations. For each organization there is a name, ok count and fail count. The graph should be dynamically (getting the data in loop) updated based on data.
The code is based on https://bl.ocks.org/mbostock/3808234.
There are few problems and few things i'm not sure about.
exit function only selects the red bars based on data update:
// JOIN new data with old elements
// specify function for data matching - correct?
var boxes = svg.selectAll(".box").data(data, function(d) {
return d.inst_name;
});
// EXIT old elements not present in new data
// this works somehow strange
// it does select all red boxes
boxes.exit().transition(t).remove();
Why does exit() select only red bars and not all? From my point of understanding the d3 documentation exit() should only select such elements that do not have any new data. Shouldn't that be all bars in case of infinite loop and constant data file?
This obviously breaks the graph quite a lot (see plunker). I need the exit to select only bars, which are not available in data file anymore. See example below.
initial state of data file:
inst_name,ok,fail
inst1,24,-1
inst2,23,-3
...
updated state of data file:
inst_name,ok,fail
inst1,26,-1
inst14,22,-4
...
The bars (both blue and red) for inst2 from intial state should be removed (and replaced by data of inst14) when the data is updated. Why is this not working?
I've read, that new data are matched against older using index. I've specified that inst_name should be used:
var boxes = svg.selectAll(".box").data(data, function(d) {
return d.inst_name;
});
Is this necessary (I've used it everywhere when inserting data)?
Also the transition for removing the elements does not work. What is the problem?
I'm also not sure if specifying data is necessary when adding new bars:
var boxes = svg.selectAll(".box").data(data, function(d) {
return d.inst_name;
});
.....
// add new element in new data
svg.selectAll(".blue")
.data(data, function(d) { // is this necessary ?
return d.inst_name;
}) // use function for new data matching against inst_name, necessary?
.enter().append("rect")
.transition(t)
.attr("class", function(d) {
return "blue box "
})
.attr("x", function(d) {
return x(d.inst_name);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.ok);
})
.attr("height", function(d) {
return height - y(d.ok + min);
})
Thanks for help.
EDIT
The underlying data get changed by script (this was not written clearly in the original post), so it can change independently of the graph state. The data should be only growing.
You've asked a lot of questions.
Why does exit() select only red bars and not all? From my point of understanding the d3 documentation exit() should only select such elements that do not have any new data. Shouldn't that be all bars in case of infinite loop and constant data file?
First, you build two sets of bars (blue [ok] and red [fail]). When you data bind these you give them the same key function, which identifies them by inst_name. You then do your data update, which now selects all the bars at once with:
svg.selectAll(".box")
You again data-bind with the same key function. Your data has 10 values in the array but you just selected 20 bars. The second 10 bars exit (the red ones) because to d3 they are not in your 10 data-points
The bars (both blue and red) for inst2 from intial state should be removed (and replaced by data of inst14) when the data is updated. Why is this not working?
I don't see that in your plunker, you are giving it the same data over and over.
Also the transition for removing the elements does not work. What is the problem?
You haven't given the transition anything to do. It'll run it, then at the end remove the rects. What you need is something for it to transition, like "height":
boxes.exit().transition(t).attr('height', 0).remove();
This will shrink them to 0 height.
So how do we clean up your code?
First, I would operate on g elements each one paired to an item in your data array. You then place both bars in the g that belong to that data point. Take a look here, I've started to clean-up your code (incomplete, though, hopefully it gets you going).
I am new to D3.js. However after being practiced the examples of this site, I tried to play ahead with Mr.John Coogan's map given here. The output that I found in his site is as under
But when I am trying to do the same thing by placing his .js,css,.json and index.html in plunker it is coming as
Problems
a) No States are getting displayed
b) Zoom and Pan is not working
In another word, at this point of time I am looking only for the Indian map to work exactly as the example shown by Mr. Coogan.
What needs to be done for this?
Here's the working plunk: http://plnkr.co/1EqpIFecwJmkbvypTyQD?p=preview
You needed to uncomment this line:
.call(d3.behavior.zoom().on("zoom", redraw))
on line 40 of the index.html in your plunk, and then zoom and pan will work.
The state colors (based on wealth) are not showing because of various, more complex errors. The error shown in the console (svg is not defined referencing line 78) is just the start (you need to replace svg with india, which is defined).
In fact the whole original gist your example is based on is really just a work in progress, but most of the answers for how to fix it can be found in this thread from the google group, from Mike Bostock himself.
Essentially, the json data loads asynchronously, so need to be loaded in series.
// load the two JSON files in series
d3.json("states.json", function(states) {
d3.json("wealth.json", function(wealthdata) {
...
});
});
Then you can just apend the relevant colorbrewer CSS class when you first create each path:
india.selectAll("path")
.data(states.features)
.enter().append("path")
.attr("d", path)
.attr("class", function(d) {
return "q" + quantize(wealthdata[d.id]) + "-9";
})
.attr("d", path);
But you also need to define the quantize scale, range...:
var quantize = d3.scale.quantize()
.range(d3.range(9));
... and domain (which you can only do once the data has been loaded:
quantize.domain([0, d3.max(d3.values(wealthdata))]);
I'm trying to create several charts using the d3.chart framework. This seems like it would be a common use case: the whole point of d3.chart is for the charts to be reusable after all! If you can just point me to an example that would be awesome (:
I went through this (https://github.com/misoproject/d3.chart/wiki/quickstart) tutorial to create a very basic "circle graph". (I copied the code exactly). Now what I want to do is create a separate chart for several sets of data.
I edited it slightly.
Before editing, to set up the chart we called:
var data = [1,3,4,6,10];
var chart = d3.select("body")
.append("svg")
.chart("Circles")
.width(100)
.height(50)
.radius(5);
chart.draw(data);
I tried to change it to:
var data = [{key:1, values:[1,3,4,6,10]},
{key:2, values:[5,2,10,8,11]},
{key:3, values:[1,5,9,16,12]}]
var chart = d3.select("body")
.selectAll("chart)
.data(data)
.enter()
.append("svg")
.chart("Circles")
.width(100)
.height(50)
.radius(5);
chart.draw(function(d) { return d.values; });
This doesn't work however. You can see the corner of a circle in 3 diferent places, but
the rest of it is cut off. However if replace
chart.draw(function(d) { return d.values; });
with
chart.draw([1,3,4,6,10]);
it correctly generates 3 circle graphs, all with that one dataset. And when I add
chart.draw(function(d) { console.log(d.values) return d.values; });
The console shows the 3 arrays I'm trying to pass it! I don't understand what is happening here that's breaking the code. Shouldn't it be the exact same thing as passing the actual arrays to 3 separate charts?
Here's a link to the JS bin with it set up: http://jsbin.com/jenofovogoke/1/edit?html,js,console,output Feel free to mess around with it!
The code is wayyy at the bottom.
I'm pretty new to java script and d3, and entirely new to d3.chart. Any help would be super appreciated!
I asked Irene Ros, who helps run d3.chart, and she informed me that the problem is that d3.chart's draw method can only take an array or a data object- it cannot take a function. She gave me a few helpful hints for ways to get around this: by using a transform function to edit my data within the chart, rather than using a function, or by creating a chart that holds multiple charts (see https://gist.github.com/jugglinmike/6004102 for a great example of this).
However in the end I found the simplest solution for me was to manually set the data. It feels like a bit off a hack because D3 does this for you already, but it was much simpler than changing the whole set up of my chart, and allows for nested data (yay!).
var svg = d3.select("body")
.selectAll("svg")
.data(data)
.enter()
.append("svg");
svg.each(function(d, i) {
var chart = d3.select(this)
.chart("Circles")
.height(50)
.width(100)
.radius(5)
var data = d.values;
chart.draw(data);
});
I have a reusable module based on the d3.layout.pack graph example.
I added a transition on exit on the node elements but it seems like the transition works only for one data set and it's not working for the other.
Basically, to simulate the data update I am calling a function with setInterval this way:
function test(){
d3.select('#vis')
.datum(data2)
.call(cluster);
}
setInterval(test, 1500);
...and I added the transition this way:
c.exit().transition()
.duration(700)
.attr("r", function(d){ return 0; })
.remove();
You can find the data update section in the bottom of the file and find the exit transition handling on line 431.
Could you please check what's wrong?
The behaviour you're seeing is caused by the way data is matched in D3. For one of the data sets, all the data elements are matched by existing DOM elements and hence the exit selection is empty -- you're simply updating position, dimensions, etc. of the existing elements.
The way to "fix" this is to tell D3 explicitly how you want data to be matched -- for example by changing line 424 to
.data(nodes, function(d) { return d.name; });
which will compare the name to determine whether a data element is represented by a DOM element. Modified jsfiddle here.
Using d3 I want to draw several time series line charts on a single page, each one featuring two lines.
Using the example on this page for multiple charts, I've got code working with single lines on each chart. But I'm not sure how best to modify that example to work with multi-line charts.
The example does this:
d3.csv("sp500.csv", function(data) {
var formatDate = d3.time.format("%b %Y");
d3.select("#example")
.datum(data)
.call(timeSeriesChart()
.x(function(d) { return formatDate.parse(d.date); })
.y(function(d) { return +d.price; }));
});
with TimeSeriesChart() defined in this file.
How would I best adapt this to cope with two (or more) lines (with the same x-axis values, and the same y scales)?
FWIW, my data is in JS arrays/hashes, rather than being read from CSV, but I guess the principle will be the same.
You can pass in your data structure that contains data for several lines in the same way. The only difference would in how you would handle the data in the referenced file. You need to change the function in
selection.each(function(data) {
First, you need to adapt the preprocessing being done and similarly the code that determines the limits of the axes. Further down, you would change
// Update the line path.
g.select(".line")
.attr("d", line);
to something like
g.selectAll(".line").data(<data for your two lines here>)
.enter()
.append("path")
.attr("class", "line")
.attr("d", line);
and remove the line
gEnter.append("path").attr("class", "line");
before that.
The exact changes will depend on the format that you're passing in.
An alternative (and if you're just starting probably easier) approach would be to simply add the additional data in a new attribute, add a new line generator that accesses that data and repeat the code that generates the line and calls the line generator with a different class name and the different generator. This is a hacky approach that I wouldn't recommend in general, but it might be easier to get started that way.