Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reasonable optimized chart scaling

I need to make a chart with an optimized y axis maximum value.

The current method I have of making charts simply uses the maximum value of all the graphs, then divides it by ten, and uses that as grid lines. I didn't write it.

Update Note: These graphs have been changed. As soon as I fixed the code, my dynamic graphs started working, making this question nonsensical (because the examples no longer had any errors in them). I've updated these with static images, but some of the answers refrence different values. Keep that in mind. Old Chart There were between 12003 and 14003 inbound calls so far in February. Informative, but ugly.

I'd like to avoid charts that look like a monkey came up with the y-axis numbers.

Using the Google charts API helps a little bit, but it's still not quite what I want. Google API Chart The numbers are clean, but the top of the y value is always the same as the maximum value on the chart. This chart scales from 0 to 1357. I need to have calculated the proper value of 1400, problematically.


I'm throwing in rbobby's defanition of a 'nice' number here because it explains it so well.

  • A "nice" number is one that has 3 or fewer non-zero digits (eg. 1230000)
  • A "nice" number has the same or few non-zero digits than zero digits (eg 1230 is not nice, 1200 is nice)
  • The nicest numbers are ones with multiples of 3 zeros (eg. "1,000", "1,000,000")
  • The second nicest numbers are onces with multples of 3 zeros plus 2 zeros (eg. "1,500,000", "1,200")

Solution

New Chart

I found the way to get the results that I want using a modified version of Mark Ransom's idea.

Fist, Mark Ransom's code determines the optimum spacing between ticks, when given the number of ticks. Sometimes this number ends up being more than twice what the highest value on the chart is, depending on how many grid lines you want.

What I'm doing is I'm running Mark's code with 5, 6, 7, 8, 9, and 10 grid lines (ticks) to find which of those is the lowest. With a value of 23, the height of the chart goes to 25, with a grid line at 5, 10, 15, 20, and 25. With a value of 26, the chart's height is 30, with grid lines at 5, 10, 15, 20, 25, and 30. It has the same spacing between grid lines, but there are more of them.

So here's the steps to just-about copy what Excel does to make charts all fancy.

  1. Temporarily bump up the chart's highest value by about 5% (so that there is always some space between the chart's highest point and the top of the chart area. We want 99.9 to round up to 120)
  2. Find the optimum grid line placement for 5, 6, 7, 8, 9, and 10 grid lines.
  3. Pick out the lowest of those numbers. Remember the number of grid lines it took to get that value.
  4. Now you have the optimum chart height. The lines/bar will never butt up against the top of the chart and you have the optimum number of ticks.

PHP:

function roundUp($maxValue){     $optiMax = $maxValue * 2;     for ($i = 5; $i <= 10; $i++){         $tmpMaxValue = bestTick($maxValue,$i);         if (($optiMax > $tmpMaxValue) and ($tmpMaxValue > ($maxValue + $maxValue * 0.05))){             $optiMax = $tmpMaxValue;             $optiTicks = $i;         }     }     return $optiMax; } function bestTick($maxValue, $mostTicks){     $minimum = $maxValue / $mostTicks;     $magnitude = pow(10,floor(log($minimum) / log(10)));     $residual = $minimum / $magnitude;     if ($residual > 5){         $tick = 10 * $magnitude;     } elseif ($residual > 2) {         $tick = 5 * $magnitude;     } elseif ($residual > 1){         $tick = 2 * $magnitude;     } else {         $tick = $magnitude;     }     return ($tick * $mostTicks); } 

Python:

import math  def BestTick(largest, mostticks):     minimum = largest / mostticks     magnitude = 10 ** math.floor(math.log(minimum) / math.log(10))     residual = minimum / magnitude     if residual > 5:         tick = 10 * magnitude     elif residual > 2:         tick = 5 * magnitude     elif residual > 1:         tick = 2 * magnitude     else:         tick = magnitude     return tick  value = int(input("")) optMax = value * 2 for i in range(5,11):     maxValue = BestTick(value,i) * i     print maxValue     if (optMax > maxValue) and (maxValue > value  + (value*.05)):         optMax = maxValue         optTicks = i print "\nTest Value: " + str(value + (value * .05)) + "\n\nChart Height: " + str(optMax) + " Ticks: " + str(optTicks) 
like image 898
Grant Avatar asked Mar 04 '09 18:03

Grant


People also ask

What is a scaling graph?

In math, a scale in graphs can be defined as the system of marks at fixed intervals, which define the relation between the units being used and their representation on the graph. Here, for instance, the scale of the graph is 1 interval being equal to 10 units.


2 Answers

This is from a previous similar question:

Algorithm for "nice" grid line intervals on a graph

I've done this with kind of a brute force method. First, figure out the maximum number of tick marks you can fit into the space. Divide the total range of values by the number of ticks; this is the minimum spacing of the tick. Now calculate the floor of the logarithm base 10 to get the magnitude of the tick, and divide by this value. You should end up with something in the range of 1 to 10. Simply choose the round number greater than or equal to the value and multiply it by the logarithm calculated earlier. This is your final tick spacing.

Example in Python:

import math  def BestTick(largest, mostticks):     minimum = largest / mostticks     magnitude = 10 ** math.floor(math.log(minimum) / math.log(10))     residual = minimum / magnitude     if residual > 5:         tick = 10 * magnitude     elif residual > 2:         tick = 5 * magnitude     elif residual > 1:         tick = 2 * magnitude     else:         tick = magnitude     return tick 
like image 80
Mark Ransom Avatar answered Oct 14 '22 07:10

Mark Ransom


You could round up to two significant figures. The following pseudocode should work:

// maxValue is the largest value in your chart magnitude = floor(log10(maxValue)) base = 10^(magnitude - 1) chartHeight = ceiling(maxValue / base) * base 

For example, if maxValue is 1357, then magnitude is 3 and base is 100. Dividing by 100, rounding up, and multiplying by 100 has the result of rounding up to the next multiple of 100, i.e. rounding up to two significant figures. In this case, the result if 1400 (1357 ⇒ 13.57 ⇒ 14 ⇒ 1400).

like image 23
Adam Rosenfield Avatar answered Oct 14 '22 07:10

Adam Rosenfield