I've made a plunker that updates data from one csv file to another, the yaxis updates accordingly but the rectangles don't.
The .attr("height", function(d) { return Math.abs(y(d[0])) - y(d[1]); }); portion of the code still has the old data from the previous file (I'm guessing).
I'm guessing this is because I haven't declared .data(series) in the updateData() function, I remember doing something like this in another chart
g.selectAll(".bar").data(series).transition()
etc...
but this doesn't work in this chart.
I can't figure it out, any help is appreciated!
The problem was that you didn't join the new data to existing bars.
To make this work well, you will want to specify a key for category of data when you join the series to the g elements to ensure consistency (although I notice that category-1 is positive in the first dataset, and negative in the second, but this is test data i guess)
Here's the updated plunkr (https://plnkr.co/edit/EoEvVWiTji7y5V3SQTKJ?p=info), with the relevant code highlighted below:
g.append("g")
.selectAll("g")
.data(series, function(d){ return d.key }) //add function to assign a key
.enter().append("g")
.attr("class", "bars") //so its easy to select later on
//etc
...
function updateData() {
d3.csv("data2.csv", type, function(error, data) {
///etc
let bars = d3.selectAll(".bars") //select the g elements
bars.data(series, function(d){ return d.key }) //join the new data
.selectAll(".bar")
.data(function(d) { return d; })
.transition()
.duration(750)
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return Math.abs(y(d[0])) - y(d[1]); });
Related
My code looks like the following (omitting the scales that I'm using):
// Nesting data attaching "name" as data key.
var dataNest = d3.nest()
.key(function(d) { return d.name; })
.entries(data);
// Defining what the transition for the path.
var baseTransition = d3.transition()
.duration(1000)
.ease(d3.easeLinear);
// Bind data to SVG (use name as key).
var state = svg2
.selectAll(".line2")
.data(dataNest, function(d, i) { return d.key; });
// Enter data join.
state.enter()
.append("path")
.attr("class", "line2");
// Set transition and define new path being transition to.
state.transition(baseTransition)
.style("stroke", "#000")
.style("fill", "none")
.attr("id", function(d) { return 'tag'+d.key.replace(/\s+/g, ''); })
.attr("d", function(d) { return line(d.values); });
state.exit().remove();
I'm mostly following this example in which the transitions are working on top of a drop down list. However, while the code I have above displays paths, it does not transition between the paths. Are there any obvious flaws in my approach?
EDIT: I have a more concrete example of what I want to do with this JSFiddle. I want to transition from data to data2 but the code immediately renders data2.
I have this table and chart with scattergraph:
https://jsfiddle.net/horacebury/bygscx8b/6/
And I'm trying to update the positions of the scatter dots when the values in the second table column change.
Based on this SO I thought I could just use a single line (as I'm not changing the number of points, just their positions):
https://stackoverflow.com/a/16071155/71376
However, this code:
svg.selectAll("circle")
.data(data)
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
});
Is giving me this error:
Uncaught TypeError: svg.selectAll(...).data is not a function
The primary issue is that:
svg.selectAll("circle") is not a typical selection as you have redefined svg to be a transition rather than a generic selection:
var svg = d3.select("#chart").transition();
Any selection using this svg variable will return a transition (from the API documentation), for example with transition.selectAll():
For each selected element, selects all descendant elements that match
the specified selector string, if any, and returns a transition on the
resulting selection.
For transitions, the .data method is not available.
If you use d3.selectAll('circle') you will have more success. Alternatively, you could drop the .transition() when you define svg and apply it only to individual elements:
var svg = d3.select('#chart');
svg.select(".line").transition()
.duration(1000).attr("d", valueline(data));
...
Here is an updated fiddle taking the latter approach.
Also, for your update transition you might want to change scale and values you are using to get your new x,y values (to match your variable names):
//Update all circles
svg.selectAll("circle")
.data(data)
.transition()
.duration(1000)
.attr("cx", function(d) {
return x(d.date);
})
.attr("cy", function(d) {
return y(d.close);
});
}
I have this d3.js project donut chart. For some reason, I am not able to access the data with in the onmousemove. The i value become zero is all the functions I pass within that event. I want to access the data of the particular slice where the mouse has moved.
How do I resolve this? Someone pls hlp!
Here is my code so far:
piesvg.selectAll("path")
.data(pie(data))
.enter().append("g")
.attr('class', 'slice')
var slice = d3.selectAll('g.slice')
.append('path')
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.attr('fill', function(d, i) {
return colorspie(i)
})
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150))
.on("mousemove", function(data){
piesvg.select(".text-tooltip")
.attr("fill", function(d,i){return colorspie(i)})
.text(function(d, i){return d[i].domain + ":" + parseInt(d[i].value * 20)}); //Considers i as 0, so no matter whichever slice the mouse is on, the data of only first one is shown
});
Here is the full code:
https://jsfiddle.net/QuikProBro/xveyLfyd/1/
I dont know how to add external files in js fiddle so it doesn't work....
Here is the .tsv that is missing:
value domain
1.3038675 Cloud
2.2541437 Networking
0.15469614 Security
0.8287293 Storage
0.7292818 Analytics
0.61878455 Intelligence
1.7016574 Infra
0.4088398 Platform
Your piesvg.select is bound to be zero-indexed for i and in all probability undefined for d as it takes those values from a single tooltip element, not the slices. Hard to be 100% sure from the snippet, but I suspect you're wanting to access and use the 'data' and 'i' from the original selectAll on the slices.
.on("mousemove", function(d, i){
piesvg.select(".text-tooltip")
.attr("fill", colorspie(i))
.text(d.data.domain + ":" + parseInt(d.data.value * 20));
});
Edited as pie slices store original data in d.data property ^^^
Working with two charts in D3. I have a pie chat displaying parent data regarding a budget. When the user mouses over a pie slice, I am trying to push that slice's array data to a bar chart.
My data is setup like so:
{"Department":"Judiciary",
"Funds1415":317432,
"Fundsb":"317.4",
"annual": [ 282,288,307,276,276,298,309,317,317 ]
},
I'm trying to use this to pass the annual array to the barchart:
path.on('mouseover', function(d) {
...
bars.selectAll('rect').transition().attr("y", function(d) { return h - d.data.annual /125; });
bars.selectAll('rect').transition().attr("height", function(d) { return d.data.annual / 125; });
});
And here's the barchart I'm trying to send it to:
var bars = svg.selectAll("rect")
.data(budget)
.enter()
.append("rect")
.attr("class", "barchart")
.attr("transform", "translate(26,109)")
.attr("fill", function(d, i) {
return color2(i);
})
.attr('class', 'barchart')
.attr("x", function(d, i) {
return i * 14;
})
.attr("width", 12)
.attr("y", 100)
.attr("height", 100);
Link to full code here:
http://jsbin.com/zayopecuto/1/edit?html,js,output
Everything 'seems' to be working, except the data either isn't passing or it isn't updating the bar chart.
I've been banging my head up against this for a couple of days, to no avail. Originally I was thinking of placing the annual data in separate arrays and just transitioning from data source to data source on mouseover, but that seems backward and unnecessary.
First, your selector is wrong. bars is already a collection of rects, so you can't re-select the rects. Second, you haven't bound "updated" data to those rects. So, with this in mind, it becomes:
bars
.data(d.data.annual)
.transition()
.attr("height", function(d) {
return d / 125;
})
.attr("y", function(d) {
return h - d /125;
});
Here's an updated example.
What I understand from your code and comment is that, you have data points for your donut chart and each data object contains a property called 'annual' which you want to use as a input data for the bar chart.
You should be calling a separate function to plot your bar chart passing it the annual data array.
Clear the existing bar chart on 'mouseout' event, so that a new bar chart can be plotted on the next 'mouseover' event. You can use jQuery empty() function for clearing out the chart container.
I have a scatter graph built with d3.js. It plots circles in the graph for the spending habits of specific people.
I have a select menu that changes the specific user and updates the circles on the scatter graph.
The problem is the old circles are not removed on update.
Where are how should I use .remove() .update(), please see this plnkr for a working example
http://plnkr.co/edit/qtj1ulsVVCW2vGBvDLXO?p=info
First, Alan, I suggest you to adhere to some coding style convention to make your code readable. I know that D3 examples, and the library code per se, almost never promote code readability, but it's in your interest first, because it's much easier to maintain readable code.
Second, you need to understand how D3 works with enter, update and exit sets, when you change data. Mike Bostock's Thinking with Joins may be a good start. Unless you understand how the joins work, you won't be able to program dynamic D3 charts.
Third, here's a bug in updateScatter. name.length makes no sense because your first name variable is value. So it's not the case of deleting old data in the first place.
// Update circles for the selected user chosen in the select menu.
svg.selectAll(".markers")
.data(data.filter(function(d){ return d.FirstName.substring(0, name.length) === value;}))
.transition().duration(1000)
.attr("cx", function(d) { return xScale(d.timestamp); })
.attr("cy", function(d) { return yScale(d.price); });
Also what that weird equality comparison is d.FirstName.substring(0, name.length) === name. Your first name data is not even spaced in CSV file. Plain d.FirstName == name is fair enough. If you expect trailing spaces anyway, just trim your strings in the place where you coerce prices and dates.
This is how correct updateScatter may look look like:
function updateScatter()
{
var selectedFirstName = this.value;
var selectedData = data.filter(function(d)
{
return d.FirstName == selectedFirstName;
});
yScale.domain([
0,
d3.max(selectedData.map(function(d)
{
return d.price;
}))
]);
svg.select(".y.axis")
.transition().duration(750)
.call(yAxis);
// create *update* set
var markers = svg.selectAll(".markers").data(selectedData);
// create new circles, *enter* set
markers.enter()
.append('circle')
.attr("class", 'markers')
.attr("cx", function(d)
{
return xScale(d.timestamp);
})
.attr("cy", function(d)
{
return yScale(d.price);
})
.attr('r', 5)
.style('fill', function(d)
{
return colour(cValue(d));
});
// transition *update* set
markers.transition().duration(1000)
.attr("cx", function(d)
{
return xScale(d.timestamp);
})
.attr("cy", function(d)
{
return yScale(d.price);
});
// remove *exit* set
markers.exit().remove();
}