0
How to get a good scale for graphs
I'm trying to get the best interval of floats that contains all floats of an array to display them in the y axis of a graph Example: For the numbers 13, 19, 15.7, 11.2, 18 the best interval would be [10-20] For the numbers 3.9, 3.1, 4.5, 3.7, 4.2 the best interval would be [3-5] The floats are not limited ( it can be -3.14 and 1501234 at same array ) but I want to get the best display scale I tried to get the interval as [min - x, max + x] Where x is a 5% of (max-min) but I get weird decimal values for the borders...when I need some exact values
6 ответов
+ 2
I ran into the same issue a while ago and the solution is not super easy but here goes. There are two main approaches:
Round to the nearest multiple of, say, 1000:
ymin = Math.floor(ymin/1000)*1000
ymax = Math.ceil(ymax/1000)*1000
Ex: [2, 1800] -> [0, 2000]
Ex: [1, 2] -> [0, 1000]
This is nice if your values are roughly in the thousands, but if the range is too small or large, 1000 might not be a good rounding factor.
Alternatively you could round to the nearest power of 10:
ymax = Math.exp(Math.ceil(Math.log10(ymax)))
ymin = same, with Math.floor
Ex: [7, 89] -> [0, 100]
Ex: [14, 55555] -> [10, 100000]
This is almost nice but it will round for example 1001 to 10000 creating a big empty space in the graph.
By combining both approaches we get the best of both worlds. Let's get the order of magnitude and then round to the nearest tenth of that:
omax = Math.exp(Math.floor(Math.log10(ymax)))
ymax = Math.ceil(ymax/omax)*omax
ymin is the same with floors/ceils reversed.
Ex: [23, 4738] -> [20, 5000]
Nice!
+ 2
PS: Negative numbers need different rounding. When I say "ceil" I mean, round away from 0. "floor" means round towards 0. But I don't know much java so you're gonna have to figure that one out.
PPS: adding ±5% to your range like you are doing now is still a good idea, before you do all this rounding stuff, to let the chart breathe.
PPPS: I see you have very small ranges too, like your second example. I didn't consider this, my code will round to [0, 10] but I think the way to do it is check wheter ymin and ymax are the same order of magnitude, round "omax" less aggressively in that case. I have to sleep now but let me think it over.
+ 1
Coder Kitten Depends on what your needs are. That approach gives you a graph that'll always fill up all the canvas space nicely, however if the graph goes from say 4328 to 6729 that's not very humanly readable in my opinion.
Rounding this to [4000, 7000] just looks a lot nicer.
Theres a related problem where you want to put labels on the y axis in regular intervals - say on the y axis you want 10 labels between your ymin and ymax values. If you've rounded to the nearest power of 10 of the ymax value that becomes trivial and you get nice looking y axis labels, rounding just to the nearest integer gets you back into fractional city.
I found that that approach gave me nice looking graphs for what I was doing but doing simple rounding has it's uses.
Now that I've slept it over I think I figured out what to do and how to deal with tiny ranges but I guess I should just whip up a demo at that point :)
+ 1
Thanks Schindlabua , the log function gave me a great hint
☺🙏
Now I'm trying something like,
First step:
Get the step 😂😂
What I mean is
step = Math.pow(10, Math.floor(Math.log10(ymax-ymin)))
With that I get subIntervals of my target
Ex: if I wish to get the interval [6000, 10000] then I know that my target interval is composed of (1 to 9 ) subIntervals of size 1000
Step 2
Get boders
Here I would just try to get the closet value to ymax and ymin that is a multiple of my step
For Interval min it's the first smaller multiple of my step than ymin
And for Interval max it's the next biggest multiple of my step than ymax...
Of course all of that after adding ±5% to ymin and ymax for more breath and error margin
+ 1
Khalil Galalem looking good! I might steal that actually. :P