0

I am designing a scatter plot. In following example the input domain of scale function is [0.04, 0.9]. .tickFormat(d3.format(".1s")) is used on axis in order to display SI abbriviation if needed. While running following snipped, you will notice, that instead of displaying 0.5 the label is showing 500m:

var margin = {top: 20, right: 0, bottom: 20, left: 0},
    width = 300 - margin.left - margin.right,
    height = 175 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .domain([0])
    .rangePoints([0, width], 1);

var y = d3.scale.linear()
    .domain([0.04, 0.9])
    .range([height, 0]);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append("g")
    .attr("transform", "translate(" + x(0) + ",0)")
    .attr("class", "axis")
    .call(d3.svg.axis()
      .scale(y)
      .orient("left")
      .ticks(2)
      .tickFormat(d3.format(".1s")));
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.axis text {
  font: 10px sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

I want d3 to stick with 0.5 in this case. It should only switch to SI abbriviation if the input domain goes down to something like [0.004, 0.009].

http://jsfiddle.net/kreide/hw9vcnkc/ (jsfiddle if you need to try it out with my example)

Alexander Weber
  • 789
  • 4
  • 16

2 Answers2

1

D3 doesn't have any built-in provisions for special casing like this, but you can easily do this yourself. For example, to omit the "m" suffix only if the number is at least 0.1, use the following code:

.tickFormat(function(d) {
      console.log(d3.formatPrefix(d));
      if(d3.formatPrefix(d).symbol == "m" && d >= 0.1) {
          return d;
      } else {
          return d3.format(".1s")(d);
      }
  })

Complete demo here.

Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • Too bad there is no generic way, as I would need to provide a pattern while working with micro-units and so on. For example a domain **[0.0001, 0.0002]** - instead of **100µ** I would like to show **0.1m**. The point is really in going to next SI in **/100 steps**, instead of **/1000**. I guess I need to provide a function like yours for all possible units. It is a best solution I could get though, so thanks a lot for pointing it out. – Alexander Weber Dec 13 '14 at 15:39
  • 1
    In that case you could have a look at the implementation of the SI unit format and adapt that to your needs. – Lars Kotthoff Dec 13 '14 at 15:52
1

By the way, if anyone is looking for a way to do this for D3 v4:

.tickFormat(function(d) {
  let val = formatPrefix(".1s", d)(d);
  console.log(val);
  // if the formatted number uses the SI prefix "m" for milli, then just return the raw number
  // ie return .5 instead of 500m
  if(val.match(/m$/) && d >= 0.1) {
    return d;
  } else {
    return format(".1s")(d); //all other numbers should get the SI treatment
  }
})
danbrellis
  • 370
  • 3
  • 13