I'm trying to make a d3 plot based on Focus+Context example.
It works fine, but I'm not able to set a minimal value for the y-axis.
I taught I understood the theory of input domain and output range (http://chimera.labs.oreilly.com/books/1230000000345/ch07.html), but it seems I'm missing something.
here is my code: http://jsfiddle.net/cecdsmnv/1/
I want the y axis to go from minAltitude to maxAltitude
any idea?
You have all the right ideas.
The settings you made for y.domain will be overwritten in line 72. Change around line to (switch min and max):
var z = 50;
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([minAltitude, maxAltitude + z]);
x2.domain(x.domain());
y2.domain(y.domain());
I added a z padding variable there in case you want some heading space at the top.
Related
I'm trying to update my x axis in a D3js bar chart (is partially working) depending on a user filter, the bars are actually changing but is not doing it well. I don't really know where is the problem and I need some help.
in this part of the code I'm updating the bar chart
function update(selectedGroup) {
svg.selectAll("rect").remove()
var groups = d3.map(dataFilter, function(d){return(d.group)}).keys();
x.domain(groups);
var dataFilter = result.filter(function(d){return d.group==selectedGroup});
console.log(dataFilter);
var rectG=rectangulos(dataFilter)
}
the complete bar chart
how is working now:
the result should be something like this
I have an live example here
There is a relatively straightforward reason you are seeing this behavior.
When the domain of the scale x is all the groups x.bandwidth() is small. But when the domain of x is only one value, x.bandwidth() is large. In both cases, the first band starts in the same location.
Next we have a nested scale here xSubgroup - the range of this is equal to x.bandwidth(). When the domain of x changes, we need to update the range of xSubgroup. If we don't do this, the bars will still be very thin and start at the beginning of the axis (as the bars' bandwidth aren't changing even if the group's bandwidth does). You don't update the sub scale's range, but we need to do that:
x.domain(groups);
xSubgroup.range([0, x.bandwidth()])
With this we get the update we're looking for.
But the axis labels remain unchanged. Updating a scale doesn't update the axis unless we explicitly do so. I'll break up your method chaining and store a reference for the g holding the axis:
var xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
xAxis.selectAll("text")
...
Now we can update the axis, I'm carrying forward the text styling as well. You can simplify the code by using an update function to do all the entering/exiting/updating of axes and data - here we have some duplication in that both the initial set up and the update function have overlap.
To update the axis we use:
// Call the axis again to update
xAxis.call(d3.axisBottom(x))
xAxis.selectAll("text")
.style("text-anchor", "end")
.attr("font-size", "55px")
.attr("y", "-7")
.attr("x", "-7")
.attr("transform", "rotate(-90)");
Which gives the desired behavior if I understand correctly, updated plunkr
I'm trying to recreate the following in D3
And I've got the following so far: http://codepen.io/jpezninjo/pen/dNwmVK
I looked it up and the best I could find was this answer: Show every other tick label on d3 time axis?, but I'm not using a class to create each column label. I think the following two lines are the ones that control my labels, but I'm not sure how to go about this.
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
x.domain(data.map(function(d) { return d.key; }));
I'm also trying to figure out how I can put some padding on the left and right of the bars
At least two possible ways:
Make your X axis a time axis, and use d3.timeDay.every(2) to specify every 2nd day. That approach is shown here: http://codepen.io/anon/pen/YNdaRB.
Key part: var axisBottom = d3.axisBottom(x).ticks(d3.timeDay).tickArguments([d3.timeDay.every(2)]);.
To make this work, I also had to (a) make d.key equal to the result from parseDate instead of the formatted date string, (b) hard-code a width for the bars instead of using x.bandwidth(), and (c) translate the x axis by width/2 px to center it under the bars (line 94). Might be nicer ways to do (b) and (c) but I mainly wanted to show d3.timeDay.every(2)'s ability (docs).
Use your current approach but apply a style to every 2nd tick. d3 adds some classes automatically so selecting the ticks is easy. This is what they described in the post you linked to. http://codepen.io/anon/pen/qRLogy?editors=1010
Key part: d3.selectAll(".tick text").style("display", function (d, i) { return i % 2 ? "none" : "initial" })
Im relatively new to d3 and nvd3 and wanted to create a simple scatterplot, just like the example but with an ordinal y-axis. So y axis values are categorical strings. This is what I thought I needed to do:
var xfun = function (d) { return d.Pos } //simple ints
, yfun = function (d) { return d.Title } //the ordinal values
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.color(d3.scale.category10().range())
.margin({ top: 30, right: 20, bottom: 50, left: 130 })
.tooltips(false)
.x(xfun)
.y(yfun);
// create an ordinal scale with some test values
var ys = d3.scale.ordinal()
.domain(["this","is","an","ordinal","scale"])
.range(5);
// tell nvd3 to use it
chart.yAxis.scale(ys);
// add to the page
nv.addGraph(function () {
d3.select(selector).datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
However, no luck:
Error: Invalid value for <circle> attribute cy="NaN" d3.js:690
Error: Invalid value for <line> attribute y1="NaN" d3.js:690
Error: Invalid value for <line> attribute y2="NaN" d3.js:690
Error: Invalid value for <circle> attribute cy="NaN" d3.js:7532
Uncaught TypeError: Cannot read property '0' of undefined
..
And the y-axis simply shows a linear axis from -1 to 1. Iterestingly there are some circles plotted at y=-1 and y=1 (the extremes).
To manually force correct values for cy I tried adding after call(chart):
d3.selectAll("#mychart circle").attr("cy", function(d){
return = ys(yfun(d));
});
But still the same error. How do I get the ordinal scale to work properly? Note I also need it to update correctly when I use the nvd3 legend to switch between dataseries (which will contain different x/y data).
There is a related question on github, but no solution.
Update: after some debugging I tried replacing chart.yAxis.scale(ys) with chart.scatter.y(ys) and this gets rid of the errors. I can also drop the manual selectAll.
However, the y-axis still shows a linear scale from 0.99-1.01 and all points are plotted at y=1. So a step closer but no ordinal scale yet.
In case somebody else stumbles upon this, here's what worked for me: instead of trying to force an ordinal scale on the axis (X, in my case), I used a linear scale, but provided a custom tickFormat function that returned the desired label.
chart.xAxis.tickFormat(function(d){
return labelValues[d];
});
Where labelValues maps between the numerical value and the desired label.
There is an easy solution by #robinfhu.
Don't use an ordinal scale!
Instead, set your x function to return the index of the element:
chart.x(function(d,i){ return i;})
And set your tickFormat function to read the proper label:
tickFormat(function(d) {return that.data[0].values[d].x;})
Working example: Plunker
Copy and paste:
nv.models.lineChart()
.x(function(d, i) {
return i;
}).xAxis
.tickFormat(function(d) {
return that.data[0].values[d].x;
});
I haven't used nvd3.js, but from my d3 experience, the error seems to be the range on your scale. For an ordinal scale, you need to define an array of corresponding values for the domain to map to. For example, I could set the following scale:
var myScale = d3.scale.ordinal()
.domain(['this','is','an','ordinal','scale'])
.range([1,2,3,4,5])
This scale would map my domain to numbers in the range 1:5, with 'this' -> 1, 'is' -> 2, etc. If I didn't want to set each point individually, I could also define my range with rangePoints, as follows:
var myScale = d3.scale.ordinal()
.domain(['this','is','an','ordinal','scale'])
.rangePoints([1,5])
rangePoints gives me an evenly spaced range of points from the minimum specified value to the max specified value, so this would result in the same range being generated.
I've made some circles that illustrate how to do this here.
When you change to a different data series, you'll have to update your domain. Since range corresponds to where on the page your points are mapping to, you won't need to update it, unless nvd3.js applies a secondary mapping and does ordinalDomain -> integers -> pointLocation in two steps.
Another option is to apply the scale in the function definition.
yfun = function (d) { return ys(d.Title) }
I'm trying to do a chart based on http://mbostock.github.com/d3/talk/20111116/bar-hierarchy.html, the only difference being that I'd like to use a log scale for the x-axis.
Here's my fiddle: http://jsfiddle.net/JhDVC/5/
As you can see, the x-axis is defined at line 4:
x = d3.scale.linear().range([0, w]),
If I change it for
x = d3.scale.log().range([0, w]),
Then it doesn't work (nothing is rendered), throwing these error messages:
Error: Invalid value for <rect> attribute width="NaN"
Changing the domain setting from
x.domain([0, root.value]).nice();
to
x.domain([1, root.value]).nice();
shows me the z axis (names) but still no bars or values.
There are a few other places where the domain for the scale is set. You need to update those as well.
Working jsfiddle here.
And here's some code so that it allows me to post this:
x.domain([1, root.value]).nice();
Your range includes zero - log(0) is undefined.
I have the following D3 chart (use 12842311 as input): http://www.uscfstats.com/deltas
After loading the chart, click the grid button (only after loading). A grid will appear. The obvious problem is that the vertical lines are only on the top half of the chart.
This is because D3, as far as I know, only lets you specify an offset for the beginning of the tick mark, which is how I'm doing it, and then doesn't let you specify an ending offset. I also think that you have to do it with tick marks because otherwise you don't know the location of the ticks unless you do something complicated.
How can I get around this limitation and do a grid with vertical lines across the x-axis? It seems like it shouldn't be difficult. Am I even going about the creation of a grid in the right way?
var xAxis = d3.svg.axis().orient("bottom").scale(x).ticks(15);
svg.append("g").attr("class","x axis").call(xAxis).attr("transform","translate(60, " + height + ")");
The easiest way I could get this to work was to manually resize and translate the tick marks.
svg.selectAll(".y line")
.attr("x2", width);
svg.selectAll(".x line")
.attr("y2", height)
.attr("transform", "translate(0," + (-y(0)) + ")");
You can see a working example at http://jsfiddle.net/2y3er/2/.
You can make two sets of tick SVG elements, one extending in the positive direction and the other in the negative direction.
Modify your d3 setup to include another axis or two, but this time include the modified tick parameters in your call method:
Dim xAxis = d3.svg.axis().scale(...).ticks(... // define your xAxis generator
Dim xAxisSVG = d3.selectAll(...)... // and your standared axis
.call(xAxis);
Dim xPositiveTicks = d3.selectAll(...)... // positive grid lines
.call(xAxis.tickSize(height));
Dim xNegativeTicks = d3.selecatAll(...)... // negative grid lines
.call(xAxis.tickSize(-1 * height));
I know that's not terribly thorough, but that method has worked well for me in the past.