selectAll("g").data(data).enter().append("g") does not work? - javascript

For some reason,
chart.selectAll("g").data(data).enter().append("g")
does not work, but
chart.selectAll("rect").data(data).enter().append("rect")
does work. By "work", I mean the element represented by chart finally contains many "rect"/"g, one for each data item. The second line causes the element to finally contain many rect, but nothing will appear if g is used. Any ideas why a simple change from rect to g will cause a bug?
Code:
var chart = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// setup chart axis.
chart.append("g")
.attr("class", "x axis")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yLabel);
// Pertinent code here
var rows = chart.selectAll("g").data(data).enter().append("g");

not sure what you want, but:
if you want something like:
svn
g //one g for every data row
g //y axis
g //x axis
then something like this should work:
var chart = svg
var chartLines = chart
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "line");
// setup chart axis.
chart.append("g")
.attr("class", "x axis")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yLabel);
// Pertinent code here
var rows = chart.selectAll(".line").data(data).enter().append("g");

This is because there are a lot of g elements on the page already when you run this code -- some of them added by you, some of them added by D3 when you call the axis component. There are however no rect elements, so that code works.
There are several ways around this -- you can either run the code that binds the data to the g elements first, add axis and chart in separate g elements, add a class to the g elements that you're binding the data to, or any combination of these.
Some more background and examples in this tutorial.

Related

D3.js v3 Dot Plot Histogram Duplicating Values

I am building a dot plot histogram with d3.js v3 and I have pretty much finished everything up - except for whatever reason some of my data points are duplicating (certain circles repeating themselves - not all of them, just some). I tried tweaking the axis parameters, as well as the data itself [deleted rows with null values, etc]- however sadly to no avail.
Any help would be immensely appreciated.
Here's my relevant code:
<div id="dotHappy"></div>
var data = d3.csv('happy_dot_modified.csv', function(data) {
data.forEach(function(d) {
d["city"] = d["city"];
d["Happy"] = +d["Happy"];
d["thc"] = +d["thc"];
});
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 1560 - margin.left - margin.right,
height = 1260 - margin.top - margin.bottom;
I tried this coder block but it wasn't working. (Not sure if this is even what's giving me the issue anyways - perhaps not).
// var x = d3.scale.linear()
// .range([0, width]);
So I went with this:
var x = d3.scale.ordinal()
.rangePoints([0, width])
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#dotHappy")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart = svg.append("g")
.attr("id", "chart");
Also tried tweaking this, which may or may not even be part of the problem.
x.domain(data.map(d => d.Happy));
y.domain([5, 33]);
// y.domain(data.map(d => d.city));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
// .append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Happy");
svg.append("g")
.attr("class", "y axis")
// .attr("transform", "translate(0," + width + ")")
.call(yAxis)
// .append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("THC");
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.Happy) + ".0)";
});
var dots = groups.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.thc + 1)
// return d3.range(d.thc)
})
.enter().append("circle")
.transition().duration(1000)
.attr("class", "dot")
.attr("r", 10)
.attr("cy", function(d) {
return y(d)
})
.style("fill", "blue")
.style("opacity", 1);
})
Here is a snapshot of my csv file:
city. |. Happy. | thc
Boston. 37. 23
NYC. 22. 30
Chicago. 88. 5
Following is a screenshot of what it currently looks like. So in this case, the tooltip displaying the text box 'The Sister' should be only for one circle (because it should only be one data point), however if you hover over the other 10 orange circles below it, it's all the same - indicating it has been repeated 11 times total:
Actually, all of the circles are repeating vertically. You may not see them all because the repeated circles are being overlapped by other colored circles as these other circles get drawn. For example, the yellow data point "The Sister" is repeating all the way down to the bottom, but the data points below the yellow ones, in blue, pink, green, blue, etc., drew themselves on top of the yellow repeats.
The culprit is this code:
.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.thc + 1)
// return d3.range(d.thc)
})
.enter().append("circle")
which, if you don't want it to repeat, should have been just one line:
.append("circle")
To explain what happened, this code:
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("class", "groups") //NOTE: you should add this line since you have 'selectAll(".groups")'
.attr("transform", function(d) {
return "translate(" + x(d.Happy) + ".0)";
});
already creates a g element for every row in the csv file. And for every g, you created an array using d3.range(1, +d.thc + 1), and appended a circle for each item in that array.
As an example, let's take the row representing "The Sister" data point that has a THC of 33. For that one data point, the code creates one <g>, inside of which it binds the array [1, 2, 3, ..., 33], and therefore appends 33 circles to the <g> element, with the cy attribute between y(1) and y(33).
Now, the question that follows is that, you specified a domain with a minimum of 5 with y.domain([5, 33]). Yet the data-bounded array, generated with d3.range, always begins with 1 and increments up to the value of THC. So some of the values in the array (1,2,3, and 4) always fall outside the y-axis, but d3 was able to translate it to a proper y-position. Is that possible? By default, yes, d3.scale extrapolates when the data is outside of the domain.
By default, clamping is disabled, such that if a value outside the input domain is passed to the scale, the scale may return a value outside the output range through linear extrapolation. For example, with the default domain and range of [0,1], an input value of 2 will return an output value of 2.

In d3, how do I include text and a background box as I roll over my line chart?

I'm using d3 v4. I want to display some text corresponding to where folks are mousing-over my line chart and I would like to have a background box on this text. So I'm trying this
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
var rect = focus.append("rect")
.attr("x", 9)
.attr("dy", ".35em")
.attr("width", 50)
.attr("height", 50)
.attr("fill", "yellow");
...
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.index_date) + "," + y(d.value) + ")");
rect.select("text").text(formatCurrency(d.value));
}
but what is happening instead is there is just a rectangle without any text floating over my mouseover point as you can see here -- https://jsfiddle.net/xx77tzn3/9/. How do I adjust what I have to include the text and a background box?
<svg>'s (rather unintuitively) don't allow you to append text elements to rect elements. Uncomment
//var text = focus.append("text")
and comment out:
var text = rect.append("text")
Then it the mouse function, change line 113 to this:
focus.select("text").text(formatCurrency(d.value));
Then it's just a matter of formatting the text on that <g>.
EDIT: in order to format the text to be on the rect, you need to make the coordinates of the text relative to the <g>, not the rect. I.e.:
var text = focus.append("text")
//var text = rect.append("text")
.attr("x", 10)
.attr("y", 10);
Now the text is there, and on top of the rectangle.
Updated js.fiddle

Y axes label for a d3.js bar chart appears inverted

I have a d3.js bar chart that is okay for the most part. I want my Y axes label to become horizontal as the text is pretty big, so I am trying to rotate it by 180 degree which makes the text inverted.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-65)"); //works fine
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-180)") //rotated by 180 and text gets inverted
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("avg_facebook_usage_rank");
Here is the fiddle

D3 - X Axis labels are overlapping

I'm create a line chart using D3 v4 and the labels X are overlapping.
// Add the X Axis
var xAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickValues(data.map(d=>d.date))
.tickFormat(d3.timeFormat("%d/%m %H:%M")))
.selectAll("text")
.attr("transform", "rotate(90)")
.attr("dy", ".35em")
.attr("y", 0)
.attr("x", 9)
.style("text-anchor", "start");
The full code is here: JSFiddle
Do not use given tick values. The data points near the right end of the chart are too close to each other for doing this. Just remove this line
.tickValues(data.map(d=>d.date))
Show the exact times in a tooltip if they are important.

How to set intersection point of x and y axis

I am using d3.js for scatter plot,I want to plot x and y axis such that they intersect at point(100,75).how to do this?
I am using
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (padding+223) + ")")
.call(xAxis2);
//Create Y2 axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + (padding+200) + ",0)")
.call(yAxis2);
But this will not change according to the scale,and I have used variable for scale.
Please let me know if you need more information.
You would need to offset the axes by the respective amount, which you can determine using the scales of the axes, i.e.
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + yScale(75) + ")")
.call(xAxis2);
and similarly for the y axis. You may have to tweak the offset slightly to account for other offsets, labels etc.

Categories