John Sly
John Sly

Reputation: 769

ticks with an ordinal scale?

I've been having a hard time getting ticks to work with something I've been playing with in D3. I'm still getting my feet wet with D3, but I've been learning it more and more. Taking an example I found online, I'm starting to turn it into something else.

The problem is, this uses an ordinal scale, and I can't find a way to set the ticks on my xaxis. I'd love to have it show ever 10th tick for example. but I haven't been able to have anything work so far.

Here's a link: http://freeptools.com/mapster/testing-funny2.php

Upvotes: 0

Views: 5545

Answers (2)

AmeliaBR
AmeliaBR

Reputation: 27544

As you've discovered, an ordinal scale doesn't have a ticks function to generate a set number of tick marks. Instead the axis object uses the entire set of values in the scale's domain to create the ticks.

This makes sense in the general case, since can't always interpolate between the category values of an ordinal scale. As I've described it previously

[The values for an ordinal scale] are assumed to be distinct categories, which might not have a natural order. They could be "apples", "oranges", and "bananas". There's no way to estimate a middle value ("oranges") based on the values of adjacent labels ("apples" and "bananas"), so by default all the labels are shown, even if they end up overlapping and unreadable.

That said, in many cases the categories do have a natural order (true "ordinal" data instead of pure categorical data) and it does make sense to skip labels.

To do that, you'll need to create your own function to generate the correct array of values to use (or hard code the array of labels if it will always be the same). Then use the axis object's .tickValues(array) function to explicitly set those tick values.

For example, in the previously linked answer (which was actually a question about controlling the ticks on a linear scale), I showed how to use one of the d3 time interval range to generate the tick values.

For your case, a method to filter the domain array based on index might work:

var ticks = scale.domain().filter(function(d,i){ return !(i%10); } );
axis.tickValues( ticks );

Explanation:

  • If scale is your ordinal scale, calling its domain() method with no parameters returns the array of all values.
  • The standard array filter() method creates a new array that consists of only the elements that return true when passed to the filter function.
  • i%10 (i modulo 10) returns 0 for index values that are multiples of 10.
  • Taking the boolean not (!) of that result will return true for multiples of 10.
  • This creates an array with only every 10th entry from your domain, which you then use to explicitly set the tick values for the axis.

One last thing: since all the axis sees is the final list of values, it won't automatically change when the data does; you'll need to remember to redo the tick value calculation any time the data updates.

P.S. This answer has more on controlling tick values, although most of the options only work with linear scales.

Upvotes: 14

Ben Lesh
Ben Lesh

Reputation: 108491

If you're looking to programmatically get the "center point" of an ordinal scale created with rangeRoundBands, you can take each of the returned range() values (the x starts of each band), and add 1/2 of the rangeBand() (the width of the band):

var ordScale = d3.scale.ordinal().domain(['a','b','c']).rangeRoundBands([0, 100], .1, .1);

var rangeBand = ordScale.rangeBand();

var ticks = ordScale.range().map(function(r) {
  return r + (rangeBand / 2);
});

EDIT: There are also helpers for just simply creating an Axis, if that's what you to do.

Upvotes: 3

Related Questions