Search code examples
typescriptd3.jsaxisaxis-labels

Cannot set d3 axis to only integers


I've looked at a couple of answers on here:

  1. how-to-limit-d3-svg-axis-to-integer-labels
  2. d3-tick-marks-on-integers-only

But they're not working for me.

I have an array of years, for example:

years: Array<number> = [2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017];
let minXAxis = Math.min(...this.years);
let maxXAxis = Math.max(...this.years);

this.xScale = d3.scaleLinear().range([this.margins.left, this.width - this.margins.right]).domain([minXAxis, maxXAxis]);

this.xAxis = svg.append("g")
  .attr("class", "axis axis-x")
  .attr("transform", `translate(0, ${this.height - this.margins.bottom})`)
  .call(d3.axisBottom(this.xScale));

Doing just this gives me the following.

pic

Then when I used .tickFormat(d3.format("d")) like so:

this.xAxis = svg.append("g")
  .attr("class", "axis axis-x")
  .attr("transform", `translate(0, ${this.height - this.margins.bottom})`)
  // set to only display ticks for digits
  .call(d3.axisBottom(this.xScale).tickFormat(d3.format("d")));

I get the following

pic 2

As you can see, it got rid of the decimal, but it still lists as duplicates, e.g 2011, 2011, ...

How do I fix this so the x axis only shows: 2010, 2011, 2012, ...?


Solution

  • There is no duplicate in that axis: those are values that just seem to be the same because you got rid of the decimals.

    The problem here is simple: you are using the wrong scale for the task. You should use a time scale or, if you want to treat those years as qualitative (categorical) variables, you should use an ordinal scale (like d3.scalePoint) instead.

    Have in mind that a year is not a regular number: 2012 is a year, but what is 2012.2412223? If you use a linear scale you are treating those years exactly like this: pure numbers.

    Therefore, the solution is just dropping the linear scale and using a time scale (or an ordinal scale).

    However, if (for whatever reason) you want to stick with the linear scale and treat the years like numbers (which they are not), use tickValues to make sure that only the values in the years array will show up in the axis:

    d3.axisBottom(this.xScale)
        .tickValues(years)
    

    Here is a demo:

    var years = [2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017];
    var svg = d3.select("svg");
    var scale = d3.scaleLinear()
      .domain(d3.extent(years))
      .range([20, 480]);
    var axis = d3.axisBottom(scale)
      .tickValues(years)
      .tickFormat(d3.format("d"));
    var gX = svg.append("g")
      .attr("transform", "translate(0,50)");
    axis(gX);
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="500" height="100"></svg>