5
votes

Does anyone have a decent algorithm for calculating axis minima and maxima?

When creating a chart for a given set of data items, I'd like to be able to give the algorithm:

  • the maximum (y) value in the set
  • the minimum (y) value in the set
  • the number of tick marks to appear on the axis
  • an optional value that must appear as a tick (e.g. zero when showing +ve and -ve values)

The algorithm should return

  • the largest axis value
  • the smallest axis value (although that could be inferred from the largest, the interval size and the number of ticks)
  • the interval size

The ticks should be at a regular interval should be of a "reasonable" size (e.g. 1, 3, 5, possibly even 2.5, but not any more sig figs).

The presence of the optional value will skew this, but without that value the largest item should appear between the top two tick marks, the lowest value between the bottom two.

This is a language-agnostic question, but if there's a C#/.NET library around, that would be smashing ;)

3

3 Answers

2
votes

OK, here's what I came up with for one of our applications. Note that it doesn't deal with the "optional value" scenario you mention, since our optional value is always 0, but it shouldn't be hard for you to modify.

Data is continually added to the series so we just keep the range of y values up to date by inspecting each data point as its added; this is very inexpensive and easy to keep track of. Equal minimum and maximum values are special cased: a spacing of 0 indicates that no markers should be drawn.

This solution isn't dissimilar to Andrew's suggestion above, except that it deals, in a slightly kludgy way with some arbitrary fractions of the exponent multiplier.

Lastly, this sample is in C#. Hope it helps.

    private float GetYMarkerSpacing()
    {
        YValueRange range   = m_ScrollableCanvas.
                    TimelineCanvas.DataModel.CurrentYRange;
        if ( range.RealMinimum == range.RealMaximum )
        {
            return 0;
        }

        float   absolute    = Math.Max(
                    Math.Abs( range.RealMinimum ),
                    Math.Abs( range.RealMaximum ) ),
            spacing     = 0;
        for ( int power = 0; power < 39; ++power )
        {
            float   temp    = ( float ) Math.Pow( 10, power );
            if ( temp <= absolute )
            {
                spacing = temp;
            }
            else if ( temp / 2 <= absolute )
            {
                spacing = temp / 2;
                break;
            }
            else if ( temp / 2.5 <= absolute )
            {
                spacing = temp / 2.5F;
                break;
            }
            else if ( temp / 4 <= absolute )
            {
                spacing = temp / 4;
                break;
            }
            else if ( temp / 5 <= absolute )
            {
                spacing = temp / 5;
                break;
            }
            else
            {
                break;
            }
        }

        return spacing;
    }
0
votes

I've been using the jQuery flot graph library. It's open source and does axis/tick generation quite well. I'd suggest looking at it's code and pinching some ideas from there.

0
votes

I can recommend the following:

  • Set a visually appealing minimum number of major lines. This will depend on the nature of the data that you're presenting and the size of the plot you're doing, but 7 is a pretty good number
  • Choose the exponent and the multiplier based on a progression of 1, 2, 5, 10, etc. that will give you at least the minimum number of major lines. (ie. (max-min)/(scale x 10^exponent) >= minimum_tick_marks)
  • Find the minimum integer multiple of your exponent and multiplier that fits within your range. This will be the first major tick. The rest of the ticks are derived from this.

This was used for an application that allowed arbitrary scaling of data an seemed to work well.