Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display integers with max precision but fixed length in d3 (use SI notation)

Tags:

d3.js

dc.js

I'm using d3 to display the number of persons in a group. Based on how the user filter, it can be more than 100k or a single digit number

I tried d3.format(".2s"), and it works well most of the time, but I have two problems:

  • when the number is <10, it displays 8​.0, I would need to display 8
  • when the number is between 100 and 999, it drops the last digit, so 442 persons are displayed as 440

My ideal format function would display the integer with the maximum precision possible in a fixed maximum number of characters/numbers. so if I want to limit the display to 3 numbers/4 chars, it would:

  • display the number up to 999
  • display 1k from 1000 to 1049
  • display 1.1k from 1050 to 1099
  • display 123k from 123'000 to 123'499
  • ...

is there a way to specify a maximum display size instead of a specific precision?

(and before any mathematician burns me to have such a cavalier attitude to precision, the number display is to give an indication, the users always have an option to see the real number, without any loss of precision ;)

like image 918
Xavier Avatar asked Oct 21 '25 03:10

Xavier


1 Answers

You can achieve it by specifying custom formatting function. If I understand you correctly, it should look like this:

function customFormat(value) {
    var formattedValue = value;

  if (value > 999 && value < 100000) {
    if (value % 1000 < 50) {
        formattedValue = d3.format('.0s')(value);
    } else {
        formattedValue = d3.format('.2s')(value);
    }
  } else if (value >= 100000) {
    formattedValue = d3.format('.3s')(value);
  }

    return formattedValue;
}

Little demo how it works:

var svgOne = d3.select("#scale-one")
    .append("svg")
    .attr("width", 500)
    .attr("height", 50)
    .append("g")
    .attr("transform", "translate(20,0)");

var svgTwo = d3.select("#scale-two")
    .append("svg")
    .attr("width", 500)
    .attr("height", 100)
    .append("g")
    .attr("transform", "translate(20,0)");
    
var x = d3.scaleLinear()
	.range([0,490])
  .domain([0,2000]);

function customFormat(value) {
	var formattedValue = value;
  
  if (value > 999 && value < 100000) {
    if (value % 1000 < 50) {
    	formattedValue = d3.format('.0s')(value);
    } else {
    	formattedValue = d3.format('.2s')(value);
    }
  } else if (value >= 100000) {
    formattedValue = d3.format('.3s')(value);
  }
  
	return formattedValue;
}

var xAxis = d3.axisBottom(x)
	.tickValues([8, 777, 1049, 1750])
  .tickFormat(customFormat);

var xLargeNumbers = d3.scaleLinear()
	.range([0,490])
  .domain([123000, 126000]);
  
var xAxisLargeNumbers = d3.axisBottom(xLargeNumbers)
	.tickValues([123499, 123999])
  .tickFormat(customFormat);
  
svgOne.append("g")
		.attr("class", "x axis")
    .attr("transform", "translate(0,5)")
    .call(xAxis);

svgTwo.append("g")
		.attr("class", "x axis")
    .attr("transform", "translate(0,5)")
    .call(xAxisLargeNumbers);
<script src="https://d3js.org/d3.v4.js"></script>
<div>Ticks: [8, 777, 1049, 1750] -</div>
<div id="scale-one"></div>
<div>Ticks: [123499, 123999] -</div>
<div id="scale-two"></div>
like image 92
Mikhail Shabrikov Avatar answered Oct 24 '25 17:10

Mikhail Shabrikov