adding an additional tick past maximum range value to a D3 linear scale axis

5,749 views
Skip to first unread message

Gretchen Culp

unread,
Mar 6, 2014, 7:17:25 PM3/6/14
to d3...@googlegroups.com
I am using D3 to produce scatter plots of dynamic data (thus the range changes) using default scale settings with nice() and default axis settings.  I want to add an additional tick past the maximum value.  For example, if my x data have the range [0,899.1] the axis has a tick increment of 100 ([0,100,200 ...900]).  I would like it to have the ticks go up to 1000. I think I could do this by using that tick increment value but I am not sure how to obtain it.  I would be immensely grateful for any suggestions on how to achieve this!

 

Mike Bostock

unread,
Mar 7, 2014, 1:18:13 AM3/7/14
to d3...@googlegroups.com
Hi Gretchen,

Use scale.nice(count) and specify the same count that you pass to scale.ticks(count).


For example:

var x = d3.scale.linear().domain([0, 899.1]);
x.ticks(10); // [0, 100, 200, … 800]
x.nice(10);
x.domain(); // [0, 900]
x.ticks(10); // [0, 100, 200, … 800, 900]

Mike

Gretchen Culp

unread,
Mar 10, 2014, 8:05:17 PM3/10/14
to d3...@googlegroups.com, mi...@ocks.org

Thank you for your response, Mike!  By using a tick number, I can retrieve the tick labels and create an extra tick increment by altering the scales' domain.  

This is what I wound up doing (note that my scales are linear with regular increments and the domain minimums are zeros... this would probably not work in other situations):

   var x = d3.scale.linear()
      .domain([0, d3.max(indicator_data, function (d) { return parseFloat(d[0]); })])
      .range([0, graph_width])
      .nice();

   var y = d3.scale.linear()
      .domain([0, d3.max(indicator_data, function (d) { return parseFloat(d[1]); })])
      .range([graph_height, 0])
      .nice();
   x.domain([0, d3.max(x.ticks(10)) + x.ticks(10)[1]]); //add x increment to max
   y.domain([0, d3.max(y.ticks(10)) + y.ticks(10)[1]]); //add y increment to max
   var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

   var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left"); 

Oleg Seletsky

unread,
Nov 23, 2014, 6:08:18 PM11/23/14
to d3...@googlegroups.com, mi...@ocks.org
I tried this approach with slightly different values and got this:

>>>var x = d3.scale.linear().domain([0,32])
undefined
>>>x.domain()
[0, 32]
>>>x.ticks(5)
[0, 5, 10, 15, 20, 25, 30]
>>>x.nice(5)
function i(n){return o(n)} d3.v3.min.js:2
>>>x.domain()
[0, 35]
>>>x.ticks(5)
[0, 10, 20, 30]

Is this expected? I was hoping the domain would get extended to [0,40] and the ticks to be [0, 10, 20, 30, 40].

Max Goldstein

unread,
Nov 23, 2014, 11:07:51 PM11/23/14
to d3...@googlegroups.com, mi...@ocks.org
scale.nice returns a new scale, rather than modifying the old one.

>>>x.nice(5).domain()
[0, 40]

Though you wouldn't know it from the docs:

Extends the domain so that it starts and ends on nice round values.

For now, just do x = x.nice(5) and move on. 

As for whether this is expected behavior, good question. Either the code or the docs need to change but I'm not sure which.

Max Goldstein

unread,
Nov 23, 2014, 11:14:22 PM11/23/14
to d3...@googlegroups.com, mi...@ocks.org
Wait, sorry, I need to amend my last email. 32 and 35 looked so similar...

I seems -- and I can reproduce -- that calling nice DOES have a side effect on the scale itself. It ALSO returns a scale with a different domain and hence ticks. This behavior is exceedingly weird and probably a bug, but I'd have to see what Mike or Jason say.

Oleg Seletsky

unread,
Nov 23, 2014, 11:22:01 PM11/23/14
to d3...@googlegroups.com, mi...@ocks.org
Max,

Thanks for the response.

I guess my question could've been clearer...the expected/unexpected behavior I was asking about was why .nice(5) extends the scale to 35 instead of 40. Similar to the original question in this thread, I'd like to always have a tick at the end of the domain but when its extended to 35, that doesn't happen cause the ticks end up being [0, 10, 20, 30].

As far as .nice() returning a different scale, as far as I can tell, it returns a reference to the same scale, presumably for chaining purposes:

>>> var x = d3.scale.linear().domain([0,499]);
undefined
>>> var y = x.nice(5);
undefined
>>> y === x
true

Max Goldstein

unread,
Nov 23, 2014, 11:29:15 PM11/23/14
to d3...@googlegroups.com, mi...@ocks.org
Yes, that sounds right. I don't know how I got the domain to end in 40 before; I get 35 both ways now. Must be having an off-night. Sorry for the craziness.

Hristo Banov

unread,
Mar 24, 2016, 3:38:24 PM3/24/16
to d3-js, mi...@ocks.org
After days of digging, this turned out to be the only solution that worked! Thanks!
Reply all
Reply to author
Forward
0 new messages