thekkm13 thekkm13 - 5 months ago 45
Javascript Question

d3 js axis Percentage Values with decimals

I'm using d3 to create a graph - (still learning both of those). I'd like my Y-axis to display values in percentages. The min and max values on the Y-axis are allocated dynamically, so the axis scale could sometimes be

25% to 38%
or sometimes even
13% to 16%
.

When there is a relatively larger range such as
25% to 28%
, I'm fine with the numbers appearing as they are, i.e.
25%, 28%, 31% ....


However, for a really small range, I notice it appears as
13%, 14%, 14%, 15%, 15%,...
(numbers repeating, probably because behind the scenes they might be something like say,
14.2%
and
14.8%
).
In such cases, I'd like the numbers to appear with 1 decimal place specified, such as
13.5%
.

I know I can specify

.tickFormat(d3.format(".1%"))


and this gives me what I need in that case. The problem is when I have a larger scale, it essentially gives me
25.0%, 28.0%, 31.0%,...
and in this case, I'd like no decimal precision.

Is there a simple way I can handle each scenario? Any guidance would be most appreciated.

Answer

As mentioned in the comments, you could check if the range is big enough and add the decimal point format depending on that but that wouldn't be very clean. I suggest you pass your own function as the tickFormat that applies the format depending if the value has a decimal portion or not. Something like:

// defined your formatters somewhere above
var decimalFormatter = d3.format(".1");

myAxis.tickFormat(function(d) { 
  if (!Number.isInteger(d)) {
    d = decimalFormatter(d); // add the decimal point for non-integers
  }
  return d + '%';
}

Sample code:

// defined your formatters somewhere above
var decimalFormatter = d3.format(".1");

var tickFormat = function(d) { 
  if (!Number.isInteger(d)) {
    d = decimalFormatter(d); // add the decimal point for non-integers
  }
  return d + '%';
};

console.log(tickFormat(12.5)); // 12.5%
console.log(tickFormat(14));   // 14%
console.log(tickFormat(15));   // 15%
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Where you can adjust the function above in any specific way you might need. For example, if you are using strings instead of numbers, you will need to first convert to a number and then use Number.isInteger because it will always return false for a string.