0
votes

I'm trying to find an algorithm for generating a Y axis for a chart engine I'm writing and am at the pulling out hair stage.

Searching around yields various solutions however I'm struggling to find one that caters for all data ranges.

Here's what I've got so far:

// Raise the max and lower the min so that we get a prettier looking chart.
var tickRangeMinMax = maxValue - minValue;
var min = tickRangeMinMax * Math.round(minValue / tickRangeMinMax);
var max = tickRangeMinMax * Math.round(1 + (maxValue / tickRangeMinMax));

This gives me a new range for which I'd like to generate a Y axis.

I calculate the distance between each YAxis label as follows:

var ticks = tickRange(min, max, labelCount);

function tickRange(minVal, maxVal, tickCount) {        
    var range = maxVal - minVal;
    var unRoundedTicksSize = range / (tickCount - 1);
    var x = Math.ceil(log10(unRoundedTicksSize) - 1);
    var pow10X = Math.pow(10, x);
    var roundedTickRange = Math.ceil(unRoundedTicksSize / pow10X) * pow10X;

    return roundedTickRange;
}

I've also tried calculating the ticks using the much simpler algorithm:

return (max - min) / labelCount

The former method works well with small ranges such as 23 -> 200 however neither of these methods work well for me when I've got a range of say, 0 -> 3000.

In the case of 0 -> 3000 I end up with negative values in some of my labels.

I add labels to the label collection by looping over the labelCount, in my case it's 5, and subtracting the tick range from the previous label value. I start with the max value.

1
What does it mean to not work, looks visually unpleasing? - chill
It means I get negative values in my Y axis. If you calculate the tick range of 0 -> 3000 and try to subtract that tick range in a loop over the labelCount, you'll observe that the tick range doesn't go into the max value labelCount number of times. - Jamie Dixon
For me your algorithm looks good, only I start from the low end of the range and the last label may be above the range, which is not a problem IMO for a chart. - chill
Thanks @chill. That's actually quite helpful. I decided to switch the loop around and go from min to max, which works as expected. I've also changed my algorithm for raising and lowering the boundaries. The values that were being generated were just too large (or low) previously, especially when you have one or two outliers. - Jamie Dixon

1 Answers

0
votes

For reference, same as yours only in Python (don't speak JS):

import math

def ticks (low, high, labels):
        nlabels = len(labels)
        tick = (high - low) / (nlabels - 1)
        x = math.floor(math.log10(tick))
        pow10x = math.pow(10,x)
        tick = int(math.ceil(tick/pow10x)*pow10x)
        return zip (range(low,high+tick,tick), labels)


print (ticks (0, 225, ['a', 'b', 'c', 'd', 'e']))

PS. maybe floor on the logarithm is better