Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graph (Chart) Algorithm

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 ;)

like image 465
Unsliced Avatar asked Nov 06 '22 23:11

Unsliced


1 Answers

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;
    }
like image 116
Bart Read Avatar answered Nov 15 '22 07:11

Bart Read