d3 .ticks() .tickValues() not updating in chart - javascript

I have seen others with this question (What does it take for .ticks() to work?) but I still have not found a clear solution. My dataset is quite large and the x-axis tick labels (simple strings) are overlapping. I would like to set a maximum of maybe 10 ticks on the chart. A snippet of my code is below.
// Add the x Axis
svg.append("g")
.attr("transform", "translate(0," + (chart_height - margin["bottom"]) + ")")
.call(d3.axisBottom(x)
.ticks(5),
)
.selectAll("text")
.attr("y", (y(d3.extent(y_values)[0]) - y(0) + 30))
.style("font-family", "PT Sans")
.style("font-size", "14px");
I am referencing this: https://github.com/d3/d3-axis/blob/master/README.md#axis_ticks but no matter what I do, my number of ticks never changes. I tried both .ticks() and .tickValues().
Am I maybe adding .ticks() to wrong portion of code? I'm super new to d3.

Related

javascript D3 V6, struggling with chained syntax, specifically effects of '.call' and 'selectAll'

Below are two examples of similar D3 code, one works and the other doesn't. In this example, I want to change the color of the lines of an axis -
This doesn't work, stroke color lines of the axis do not get changed to green -
var x_axis = svg.append("g")
.attr("class", "axis")
.attr("transform", `translate(20, ${height - 50})`)
.call(d3.axisBottom(ordinalScale))
.selectAll("text")
.attr("transform", "translate(-5,5)rotate(-45)")
.style("text-anchor", "end")
.style("font-size", "8px")
.style("fill", "#102040");
x_axis.selectAll("line, path").style("stroke", "green");
BUT this works, the lines get changed to green:
var x_axis = svg.append("g")
.attr("class", "axis")
.attr("transform", `translate(20, ${height - 50})`)
.call(d3.axisBottom(ordinalScale));
x_axis.selectAll("text")
.attr("transform", "translate(-5,5)rotate(-45)")
.style("text-anchor", "end")
.style("font-size", "8px")
.style("fill", "#102040");
x_axis.selectAll("line, path").style("stroke", "green");
The difference being that in the first (failed) example, I chain the 'selectAll("text")' operations to the 'call(d3.axisBottom)' with the 'selectAll("line, path")' operations in a following expression, and in the second (successful) example, I have following seperate expressions for each of the text and line/path operations.
This is not critical since I can get the effect I want, but it seems to me that they should be equivalent but obviously there is some subtlety of the syntax that I do not understand. Does this have something to do with the '.call' operation?
The first code block doesn't work because x_axis doesn't contain what you think it does.
var x_axis = svg.append("g") // returns a selection of a g element
.attr("class", "axis") // returns the same selection of a g
...
.call(d3.axisBottom(ordinalScale)) // returns the same selection of a g
.selectAll("text") // returns a new selection of text elements
...
.style("fill", "#102040"); // returns the same selection of text elements
x_axis is defined by the last value returned by the chain. So,
x_axis in the above is a selection of text elements, text elements can't (and in this case don't) contain any child path or line elements, so x_axis.selectAll('line, path') will return an empty selection. Consequently, setting any property for an empty selection won't change anything.
The second code block works because x_axis remains a selection of a g - selection.call() returns the same selection that .call() was chained to, like .attr() or .style(), among other methods. Whereas selectAll() and select(), among other methods, return new selections.

Drawing Separate Domains for Axes under Multiple Small Barcharts in D3.js

My intention is to draw multiple small barcharts in one svg with different domains, building upon the example.
The main problem I am stuck on seems to be the problem of extracting values for a particular key from the output of d3.nest and defining the domain corresponding to each key. The problem arises when plotting all the values, for which the dates are drawn in the domain. Since not each key has a value corresponding to all possible dates, there is a tail plotted on the right, which breaks the order. Could you please tell me how it can be fixed, how can the inputs without a corresponding output be removed from the plotted set of values?
Here is the result:
https://plnkr.co/edit/KUnMSfJwWan3JaIpZutZ/
The data is in the following form:
CSC 20160919 4.0
MAT 20160923 16.0
In the example sample of data given, there are two dates, and two dates are going to be plotted for each small subgraph. It would be great to separate the dates corresponding to a particular key in such a way that only those dates for which there is a corresponding score are plotted.
There is a deficiency: the domains must be separate, while in the example above they are not.
Could you please tell me how I can plot the graphs for each key with the date corresponding to a particular key as defined in d3.nest?
The relevant part of the code seems to be here:
x.domain(data.map(function(d) { return d.date; }));
and here:
svg.selectAll(".bar")
.data(function(d) {return d.values;})
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.score); })
.attr("height", function(d) { return height - y(d.score); })
.attr("fill", function(d) {return color(d.score)})
APPENDIX
The following questions can be useful:
D3.js binding an object to data and appending for each key
X,Y domain data-binding in multiple grouped bar charts in d3.js
You can also find the following links helpful:
Grouped Bar Chart
Tutorial: Grouped Bar Chart
Grouping Data
HYPOTHESES
Can we nest the data twice, first by course, then by date, and then plot keys' keys on the axes? Is nesting the best way to approach the problem at all?
Here is my solution (warning: I'm the first one to admit that my code is too complicated. Let's see if someone else comes with a simpler solution).
First, we create an empty object:
var xScale = {};
And populate this new object with two functions, one for each different scale.
courses.forEach(function(d) {
xScale[d.key] = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(d.values.map(function(e) {
return e.date
}));
});
After this, we'll have two different scales:
xScale.HIS//this is the scale for the first SVG
xScale.PHY//this is the scale for the second SVG
Now you can use those scales in your bars, but not directly. We have to define what scale is going to be used in what SVG.
To do that, firstly, we'll get the key property in the data for each SVG, and then using this value to access the correct object inside xScale, which contains the proper scale:
.attr("x", function(d) {
var scale = d3.select(this.parentNode).datum().key;
return xScale[scale](d.date);
})
.attr("width", function() {
var scale = d3.select(this.parentNode).datum().key;
return xScale[scale].rangeBand()
})
The most complicated part is calling the axes. Here, we'll use each to call xAxis two times, using the first argument (the datum) to get the value of key:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.each(function(d) {
return d3.select(this).call(xAxis.scale(xScale[d.key]))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
})
All together, this is your updated plunker: https://plnkr.co/edit/XFKzQm0zp0MkhzK4Qt5d?p=preview

What are the units of d3.js zoom.translate vector?

I am trying to use the following code taken from this example to pan my time-series line-chart sideways, as new data is added to the graph:
d3.select("path")
.datum(globalData)
.attr("class", "line")
.attr("d", valueline(globalData))
.transition()
.ease("linear")
.attr("transform", "translate(" + x(-dx) + ")");
But this does't work - my line disappears. I am trying to understand what the units of the translate vector need to be for this to work. In my case dx would be the difference between the first and last x-values of the new data.
The units used in translate are screen pixels in the default coordinate system.

Can I create a Javascript chart from this table?

I just started programming with Highcharts, and wonder if it's possible to get any sort of line chart based on this table: http://www2.nve.no/h/hd/plotreal/Q/0027.00025.000/knekkpunkt.html
The table updates each half hour, and I want to program a line chart that updates accordingly.
My problem is that I have no idea if this is possible. The table data are remote, on a server I don't have access to. The data is public and open, though.
Would be grateful for some help!
One great open source project you'd want to check out is D3.js. It's a feature-rich library for generating complex visualizations (using SVG elements and the DOM) from raw data. Here is a demo I made using a snippet of your data that is powered by D3:
http://jsfiddle.net/2s6Y3/
//Define two functions that will 'map' x- and y-values into their respective axis domain
var x_scale_fn = d3.scale.linear().domain([Math.min.apply(null, times), Math.max.apply(null, times)]).range([0, width]);
var y_scale_fn = d3.scale.linear().domain([Math.max.apply(null, levels), Math.min.apply(null, levels)]).range([0, height]);
//Create a 'line' object that can read our records and turn them into x-y coordinates
var line = d3.svg.line()
.x_scale_fn(function(d, i) { return x(times[i]); })
.y_scale_fn(function(d, i) { return y(levels[i]); });
//Create a new SVG element (our chart) and give it some basic attributes
var graph = d3.select(".chart").append("svg:svg")
.attr("width", width + margins[1] + margins[3])
.attr("height", height + margins[0] + margins[2])
.append("svg:g")
.attr("transform", "translate(" + margins[3] + ", " + margins[0] + ")");
//Create our chart axes
var x_axis = d3.svg.axis().scale(x),
y_axis = d3.svg.axis().scale(y).orient("left");
//Render the x-axis
graph.append("svg:g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(x_axis)
.selectAll("text")
.attr("transform", function(d) { return "rotate(45deg)"; });
//Render the y-axs
graph.append("svg:g")
.attr("transform", "translate(-25,0)")
.attr("class", "y axis")
.call(y_axis);
//Render our data (line) onto the chart
graph.append("svg:path").attr("d", line(data));
If you want to create a project that offers a persistent, up-to-date visualization of the water data, you'll need to set up a scraper that will periodically read data from the page and format it to be used by D3 (i.e. - JSON). This could be done with a number of different tools, but one suggestion would be using Python and urllib2.
This will be possible, but not fun at all. That data is all one big preformatted string. Getting access to a table of those values will make things way easier for you. Otherwise, you'll need to look into writing some RegEx to parse that monster node's text.
Your link seems to not be working.
If you are talking about an HTML table, take a look at the demo here:
http://www.highcharts.com/demo/column-parsed
Thanks for all the help, this made me realize that my skills doesn't add up to what it takes to get this working.

Display y-axis as percentages?

I'm making a chart, and I want my y-axis tick labels to display as percentages (instead of decimals). How can I do this?
My current code looks something like
yAxis
.append("text")
.attr("class", "ticklabel")
.attr("x", 0)
.attr("y", y)
.attr("dy", ".5em")
.attr("text-anchor", "middle")
.text(y.tickFormat(5))
.attr("font-size", "10px")
I noticed that d3 has a format specifier, but I'm not sure how to use these in conjunction with tickFormat.
It looks like you authoring the axis by hand. I recommend using the d3.svg.axis component instead, which does the rendering for you. For example:
http://bl.ocks.org/1166403
If you want to format ticks as percentages, then use d3.format's % directive:
var formatPercent = d3.format(".0%");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
You can instead use the p directive if you want to round to the percentage to significant digits.
To answer your question more directly, you use d3.format instead of the scale's tickFormat. Scales provide a default tickFormat for convenience, but if you want different behavior, then you use a custom d3.format rather than the scale's default one.

Categories

Resources