Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scale numbers to be <= 255?

I have cells for whom the numeric value can be anything between 0 and Integer.MAX_VALUE. I would like to color code these cells correspondingly.

If the value = 0, then r = 0. If the value is Integer.MAX_VALUE, then r = 255. But what about the values in between?

I'm thinking I need a function whose limit as x => Integer.MAX_VALUE is 255. What is this function? Or is there a better way to do this?

I could just do (value / (Integer.MAX_VALUE / 255)) but that will cause many low values to be zero. So perhaps I should do it with a log function.

Most of my values will be in the range [0, 10,000]. So I want to highlight the differences there.

like image 794
Nick Heiner Avatar asked Oct 11 '09 02:10

Nick Heiner


2 Answers

The "fairest" linear scaling is actually done like this:

floor(256 * value / (Integer.MAX_VALUE + 1))

Note that this is just pseudocode and assumes floating-point calculations.

If we assume that Integer.MAX_VALUE + 1 is 2^31, and that / will give us integer division, then it simplifies to

value / 8388608

Why other answers are wrong

Some answers (as well as the question itself) suggsted a variation of (255 * value / Integer.MAX_VALUE). Presumably this has to be converted to an integer, either using round() or floor().

If using floor(), the only value that produces 255 is Integer.MAX_VALUE itself. This distribution is uneven.

If using round(), 0 and 255 will each get hit half as many times as 1-254. Also uneven.

Using the scaling method I mention above, no such problem occurs.

Non-linear methods

If you want to use logs, try this:

255 * log(value + 1) / log(Integer.MAX_VALUE + 1)

You could also just take the square root of the value (this wouldn't go all the way to 255, but you could scale it up if you wanted to).

like image 199
Artelius Avatar answered Sep 24 '22 01:09

Artelius


I figured a log fit would be good for this, but looking at the results, I'm not so sure.

However, Wolfram|Alpha is great for experimenting with this sort of thing:

I started with that, and ended up with:

r(x) = floor(((11.5553 * log(14.4266 * (x + 1.0))) - 30.8419) / 0.9687)

Interestingly, it turns out that this gives nearly identical results to Artelius's answer of:

r(x) = floor(255 * log(x + 1) / log(2^31 + 1)

IMHO, you'd be best served with a split function for 0-10000 and 10000-2^31.

like image 38
Bob Aman Avatar answered Sep 26 '22 01:09

Bob Aman