Skip to content
Advertisement

Creating a Logarithm Scale with Min, Max and Discrete “steps”

I’m building a scale for generating colors for a map, where various ranges of numbers correspond to colors. In the scale below, the a, b, and c values (along with the min and max) form “steps” or “buckets” corresponding to colors. Anything between -1 and min will be colored "#ffeda0 in this example, and anything between min and a will be colored differently:

  return [
    "#e7e9e9",
    -1,
    "#ffeda0",
    min,
    "#fed976",
    a
    "#feb24c",
    b
    "#fd8d3c",
    c
    "#fc4e2a",
    max
    "#e31a1c"
  ];

This scale will always start an a minimum value, called min, and will end at a maximum value, max. I’ve currently got a simple linear scale like this:

  const min = Math.min(data);
  const range = Math.max(data) - min;
  const step = range / 4;

  return [
    "#e7e9e9",
    -1,
    "#ffeda0",
    min,
    "#fed976",
    min + step,
    "#feb24c",
    min + step * 2,
    "#fd8d3c",
    min + step * 3,
    "#fc4e2a",
    max,
    "#e31a1c"
  ];

How could one write a function that, given a minimum, maximum, and the number of steps in a scale (in this case three) output the correct numbers for a logarithmic scale?

Advertisement

Answer

A logarithmic scale is linear in its logarithms. So:

const min = Math.min(data);
const max = Math.max(data);

const logmin = Math.log(min);
const logmax = Math.log(max);

const logrange = logmax - logmin;
const logstep = logrange / 4;

Now where the linear scale has

value = min + n * step;

the logarithmic scale has:

value = Math.exp(logmin + n * logstep);

This will work only if both min and max are positive, because you cannot take the logarithm of zero or a negative number and bcause in logarithmic scale, each value has a constant positive factor to the previous one. You can make this work for negative min and max by adjusting the signs.

You can do the calculations in other bases, for example 10:

const logmin = Math.log10(min);
const logmax = Math.log10(max);

value = Math.pow(10, logmin + n * logstep);

Alternatively, you can find the common factor from one step to the next by taking the n-th root of the quotient of the min and max values:

const min = Math.min(data);
const max = Math.max(data);

const fact = Math.pow(max / min, 1.0 / 4.0);

The values are then found by repeated multiplication:

value = prev_value * fact

or

value = xmin * Math.pow(fact, n);

Both methods might yield “untidy” numbers even if the scale should have nice round numbers, because the logarithms and n-th roots could produce inaccurate floating-point results. You might get better results if you pick a round factor and adjust your min/max values.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement