Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 Logarithmic Tick Labels as Powers of 10

Using D3 I want to create an X Axis that looks like:

image of desired log scale labels

I've worked out how to do the axis and ticks, but not the labels using the following:

var svgWidth = 500;
var svgHeight = 500;
var svgAxisPadding = 20;

var xScale = d3.scale.log()
  .domain([Math.pow(10, 5), Math.pow(10, 7)])
  .range([svgAxisPadding, svgWidth - svgAxisPadding]);

var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient('bottom')
  .ticks(0, "e");

var svg = d3.select('#diagram')
  .append('svg')
  .attr('width', svgWidth)
  .attr('height', svgHeight);

svg.append('g')
  .attr("class", "axis")
  .call(xAxis);

And here's a jsFiddle with the complete code.

like image 714
Mike Ward Avatar asked Sep 27 '13 16:09

Mike Ward


People also ask

What are ticks d3?

d3. ticks generates an array of nicely-rounded numbers inside an interval [start, stop]. The third argument indicates the approximate count of values needed. For example, to cover the extent [0, 10] with about 100 values, d3.ticks will return these ticks: start = 0.

What is d3 range?

d3. range returns an array of evenly-spaced numbers. In its simplest form, it returns the integers from zero to the specified end minus one. Array(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

What is scaleBand d3?

scaleBand() function in D3. js is used to construct a new band scale with the domain specified as an array of values and the range as the minimum and maximum extents of the bands. This function splits the range into n bands where n is the number of values in the domain array.


2 Answers

You could use unicode:

var superscript = "⁰¹²³⁴⁵⁶⁷⁸⁹",
    formatPower = function(d) { return (d + "").split("").map(function(c) { return superscript[c]; }).join(""); },
    formatTick = function(d) { return 10 + formatPower(Math.round(Math.log(d) / Math.LN10)); };

For example, formatTick(1e5) returns "10⁵". Example at bl.ocks.org/6738109:

axis

The downside of this approach is that the vertical alignment of the superscript numerals seems inconsistent. So using post-selection (say, selecting the text elements and adding a tspan element for the superscript to each) might be better. Another example at bl.ocks.org/6738229:

axis

like image 91
mbostock Avatar answered Sep 29 '22 20:09

mbostock


There's a tickFormat function available on the axis. Unfortunately, it expects a String as a return value and plops that on the axis. This would be great if you wanted to display 10^6, but not as helpful when you want to use the superscript notation.

A workaround is to create 2 axes: one for displaying the 10 and another for displaying the exponent. Here's an example:

var svgWidth = 500;
var svgHeight = 500;
var svgAxisPadding = 20;

var xScale = d3.scale.log()
    .domain([Math.pow(10, 5), Math.pow(10, 7)])
    .range([svgAxisPadding, svgWidth - svgAxisPadding]);

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(0, "e")
    .tickFormat(function (d) {
        var log = Math.log(d) / Math.LN10;
        return Math.abs(Math.round(log) - log) < 1e-6 ? 10 : '';
    });

var xAxis2 = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(0, "e")
    .tickFormat(function (d) {
        var log = Math.log(d) / Math.LN10;
        return Math.abs(Math.round(log) - log) < 1e-6 ? Math.round(log) : '';
    });

var svg = d3.select('#diagram')
    .append('svg')
    .attr('width', svgWidth)
    .attr('height', svgHeight);

svg.append('g')
    .attr("class", "axis")
    .call(xAxis);

svg.append('g')
    .attr("class", "axis")
    .attr("transform", "translate(12, -5)") //shifted up and to the right
    .style("font-size", "12px")
    .call(xAxis2);

It's not necessarily the most elegant solution, but it works.

like image 32
Towler Avatar answered Sep 29 '22 20:09

Towler