This is a followup of this question on SO: D3 - issues on first transition after initialization.
I have studies and implemented the key functions as explained by Mike and Jason in their answers. I also found this blog post useful http://knowledgestockpile.blogspot.com/2012/01/understanding-selectall-data-enter.html
I have used a key like
.data(svg.chartData, function(d) { return d.idSeries;})
to map one-to-one data and index.
However, I do not get the expected result and data points swap on redraw. Please have a look at http://jsbin.com/avoced/8/edit, specifically to the following relevant functions:
Chart initialization:
svg.series = svg.selectAll(".series").data(svg.chartData, function(d) { return d.idSer;})
svg.series.enter().append("g").classed("series", true)
svg.rects = svg.series.selectAll("rect").data(Object, function(d) { return d.x;})
svg.rects.enter().append("rect")
Redraw:
svg.series = svg.selectAll(".series").data(svg.chartData, function(d) { return d.idSer;})
svg.series.enter().append("g").classed("series", true)
svg.series.exit().remove()
svg.rects = svg.series.selectAll("rect").data(Object, function(d) { return d.x;})
svg.rects.enter().append("rect")
svg.rects.exit().remove()
Scales:
x = d3.scale.ordinal()
.domain(svg.pointsNames, function(d) { return d;})
.rangeRoundBands([0,width], .1);
What am I missing here?
Many thanks
Related
I'm trying to recreate a simple example as seen here:
https://www.d3-graph-gallery.com/graph/barplot_stacked_basicWide.html
Here is a simple version of my code (note that the data has different labels but is structurally identical):
var data = [
{period:'t1', fmc1:+10, fmc2:9, fmc3:6, fmc4:5, fmc5:2},
{period:'t2', fmc1:+11, fmc2:8, fmc3:6, fmc4:4, fmc5:3},
{period:'t3', fmc1:+12, fmc2:10, fmc3:7, fmc4:5, fmc5:3},
];
var groups = d3.map(data, function(d){return(d.period)}).keys()
var subgroups = data.columns.slice(1);
var stackedData = d3.stack()
.keys(subgroups)
(data);
var yScale = d3.scaleLinear()
.domain([0,80])
.range([height,0]);
var xScale = d3.scaleBand()
.domain(groups)
.range([0,width])
.padding([.2]);
var colorScale = d3.scaleOrdinal()
.domain(subgroups)
.range(["#003366","#366092","#4f81b9","#95b3d7","#b8cce4","#e7eef8","#a6a6a6","#d9d9d9","#ffffcc","#f6d18b","#e4a733","#b29866","#a6a6a6","#d9d9d9","#e7eef8","#b8cce4","#95b3d7","#4f81b9","#366092","#003366"]);
graphGroup.append("g")
.selectAll("g")
.data(stackedData)
.enter().append("g")
.attr("fill", function(d) { return colorScale(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return xScale(d.data.period); })
.attr("y", function(d) { return yScale(d[1]); })
.attr("height", function(d) { return yScale(d[0]) - yScale(d[1]); })
.attr("width",xScale.bandwidth())
<script src="https://d3js.org/d3.v5.min.js"></script>
I'm confronted with an error that reads:
TypeError: Cannot read property 'slice' of undefined # line 47.
That line is:
var subgroups = data.columns.slice(1);
I confirmed in the console that data.columns is indeed, undefined. This begs the question as to what the author of the d3-graph-gallery is trying to do. I'm convinced there is an explanation, and the visual seems to work just fine on the website. Initial troubleshooting on my end didn't turn up much, doesn't seem to be a version issue.
Question
If data.columns.slice(1) is a conventional approach to constructing a stacked bar chart (as the tutorial would lead one to believe) why isn't it working for me? I'm hoping there is a quick fix or something that can be tweaked easily so that I can follow the rest of the tutorial without diverging too much in terms of syntax and methodology.
data is an array. JavaScript arrays can be given properties, but it's not really considered good practice, because you're "sneaking" stuff into the object.
In this case, data.columns is set by the CSV reader, which you don't use here. The following is equivalent to the value of data.columns:
Object.keys(data[0])
I understand that merge can be used to combine enter and update selections in d3 v4, as in the simple example here: https://bl.ocks.org/mbostock/3808218.
I have a scatter plot in which multiple variables are displayed on a shared x-axis, for different groups selected by a dropdown box. When a new group is selected, the overall set of datapoints is updated, with points for each variable added like this:
.each(function(d, i) {
var min = d3.min(d.values, function(d) { return d.value; } );
var max = d3.max(d.values, function(d) { return d.value; } );
// Join new data with old elements
var points = d3.select(this).selectAll("circle")
.data(d.values, function(d) { return (d.Plot); } );
// Add new elements
points.enter().append("circle")
.attr("cy", y(d.key))
.attr("r", 10)
.style("opacity", 0.5)
.style("fill", function(d) { return elevColor(d.Elevation); })
.merge(points) //(?)
.transition()
.attr("cx", function(d) { return x((d.value-min)/(max-min)); });
// Remove old elements not present in new data
points.exit().remove();
This whole piece of code is largely duplicated for the overall enter selection and again in the overall update selection (as opposed to the individual variables), which seems less than ideal. How would merge be used to to remove this duplicated code?
The full example is here: http://plnkr.co/edit/VE0CtevC3XSCpeLtJmxq?p=preview
I'm the author of the solution for your past question, which you linked in this one. I provided that solution in a comment, not as a proper answer, because I was in a hurry and I wrote a lazy solution, full of duplication — as you say here. As I commented in the same question, the solution for reducing the duplication is using merge.
Right now, in your code, there is duplication regarding the setup of the "update" and "enter" selections:
var update = g.selectAll(".datapoints")
.data(filtered[0].values);
var enter = update.enter().append("g")
.attr("class", "datapoints");
update.each(function(d, i){
//code here
});
enter.each(function(d, i){
//same code here
});
To avoid the duplication, we merge the selections. This is how you can do it:
var enter = update.enter().append("g")
.attr("class", "datapoints")
.merge(update)
.each(function(d, i) {
//etc...
Here is the updated Plunker: http://plnkr.co/edit/MADPLmfiqpLSj9aGK8SC?p=preview
So I've been trying to get my d3.js bar chart to transition to different values when a button is pressed. Though, at the moment some elements seem to be adding, but extremely wide and all over the place, as well as the previous elements not being removed.
This is my code:
function updateData(time) {
timeValue = time;
// Get the data again
d3.tsv(timeValue + ".tsv", function(error, data) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
//Selecting Data
var bars = svg.selectAll("rect")
.data(data);
//Removing
bars.exit().remove("rect");
//Changes
bars.enter().append('rect')
.attr("class", "bar")
bars.attr('x', function(d) { return x(d.letter); })
.attr('width', x.rangeBand())
.attr('y', function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
//var svg = d3.select("div.innerScreen2").transition();
});
Now I've looked at similar questions asked and tried to apply the solutions, but nothing seems to get removed or change :/ Maybe I have code in the wrong place? Any help would be much appreciated
There are two problems to solve in your question. The first issue is related to the update pattern that some elements are added and some not. Do you have an unique identifier in your data set? If yes, you can use a function within the update process:
// Join new data with old elements, if any.
var text = svg.selectAll("text")
.data(data, function(d) { return d.id; });
Source:http://bl.ocks.org/mbostock/3808234
The second issue is related to your x/y positions and may depend on your underlying data. An data excerpt or debugging information are required to solve this issue.
I am trying to get rid of the outer circle of the bubble chart. But actually now I am at my wit's end... It seems there is few tutorial online on how to plot bubble chart using csv Data. Please check out my working PLUNK and help me out.
PLUNK: http://plnkr.co/edit/87WLm3OmK1jRtcq8p96u?p=preview
d3.csv("count_s.csv", function(csvData) {
var years = [2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014];
pack.value(function(d) {
return +d["count" + years[i]];
});
var data = {
name: "city",
children: csvData
};
var node = svg1.selectAll("g.node")
.data(pack.nodes(data), function(d) {
return d.city;
});
The code responsible for circle creation in your example is this (file bubble.js, lines 63-70):
//Add the Circles
var circles = nodeEnter.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color1(d.city);
});
All you need to do is to put the line
.filter(function(d){ return d.parent; })
before call to append(), like this:
//Add the Circles
var circles = nodeEnter
.filter(function(d){ return d.parent; })
.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color1(d.city);
});
and you will get:
The explanation of the solution is that added line simply excludes any circle that does not have a parent (which is actually only the outermost circle) from rendering.
Modified plunk is here.
NOTE: The text in the middle of the outer circle is still displayed. If you do not want it either, you may apply similar code solutions as the one used for the circle itself.
I've created a plunk to demonstrate my problem.
The issue is that my .enter(),update(),exit() method is not working for my d3.chart.layout() visualization.
Instead, I get the classic "double post" problem. My keys are the same, however.
What I want to happen is for my d3 steam graph to update its data (such that the y values all go to 0, and the chart disappears). My data binding is coded normally:
var steam = svg.selectAll(".layer")
.data(layers, function(d){console.log(d); return d.key})
steam.enter().append("path")
steam.style("fill", function(d, i) { return z(i); }).style("opacity","0").transition().duration(400)
.style("opacity","1")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
steam.exit().transition().duration(500).remove()
What is happening/any ideas?
So I got it to work, though I'm still confused as to why it works this way. I needed to move the svg adding out of the update function and into the namespace (that was obvious).
But the update became this, with a transition() method. Can anyone help me understand where I went wrong?
test = svg.selectAll("path").data(layers, function(d){return d.key})
test.enter().append("path")
test.style("opacity",1).style("fill", function(d, i) { return z(i); })
.attr("class", "layer").transition()
.duration(2000)
.attr("d", function(d) { return area(d.values); });
test.exit().transition().duration(1500).style("opacity",0).remove();