4

I have created a graph using Jpgraph (http://www.jpgraph.com).

Jpgraph has a spline class which smooths out the lines on the line graph. However it does not work as I expected. As you can see, the highest peak of the line goes above the actual markings (the red squares). Friday 10pm is 2.0 however it appears to be higher than that around 6am friday.

enter image description here

A poorly laptop touchpad drawn example of what I would have expected is below :) enter image description here

I have managed to track down the class to jpgraph_regstat.php. Any idea how to make the center of the peak the actual markings?

//------------------------------------------------------------------------
// CLASS Spline
// Create a new data array from an existing data array but with more points.
// The new points are interpolated using a cubic spline algorithm
//------------------------------------------------------------------------
class Spline {
    // 3:rd degree polynom approximation

    private $xdata,$ydata;   // Data vectors
    private $y2;   // 2:nd derivate of ydata
    private $n=0;

    function __construct($xdata,$ydata) {
        $this->y2 = array();
        $this->xdata = $xdata;
        $this->ydata = $ydata;

        $n = count($ydata);
        $this->n = $n;
        if( $this->n !== count($xdata) ) {
            JpGraphError::RaiseL(19001);
            //('Spline: Number of X and Y coordinates must be the same');
        }

        // Natural spline 2:derivate == 0 at endpoints
        $this->y2[0]    = 0.0;
        $this->y2[$n-1] = 0.0;
        $delta[0] = 0.0;

        // Calculate 2:nd derivate
        for($i=1; $i < $n-1; ++$i) {
            $d = ($xdata[$i+1]-$xdata[$i-1]);
            if( $d == 0  ) {
                JpGraphError::RaiseL(19002);
                //('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
            }
            $s = ($xdata[$i]-$xdata[$i-1])/$d;
            $p = $s*$this->y2[$i-1]+2.0;
            $this->y2[$i] = ($s-1.0)/$p;
            $delta[$i] = ($ydata[$i+1]-$ydata[$i])/($xdata[$i+1]-$xdata[$i]) -
            ($ydata[$i]-$ydata[$i-1])/($xdata[$i]-$xdata[$i-1]);
            $delta[$i] = (6.0*$delta[$i]/($xdata[$i+1]-$xdata[$i-1])-$s*$delta[$i-1])/$p;
        }

        // Backward substitution
        for( $j=$n-2; $j >= 0; --$j ) {
            $this->y2[$j] = $this->y2[$j]*$this->y2[$j+1] + $delta[$j];
        }
    }

    // Return the two new data vectors
    function Get($num=50) {
        $n = $this->n ;
        $step = ($this->xdata[$n-1]-$this->xdata[0]) / ($num-1);
        $xnew=array();
        $ynew=array();
        $xnew[0] = $this->xdata[0];
        $ynew[0] = $this->ydata[0];
        for( $j=1; $j < $num; ++$j ) {
            $xnew[$j] = $xnew[0]+$j*$step;
            $ynew[$j] = $this->Interpolate($xnew[$j]);
        }
        return array($xnew,$ynew);
    }

    // Return a single interpolated Y-value from an x value
    function Interpolate($xpoint) {

        $max = $this->n-1;
        $min = 0;

        // Binary search to find interval
        while( $max-$min > 1 ) {
            $k = ($max+$min) / 2;
            if( $this->xdata[$k] > $xpoint )
            $max=$k;
            else
            $min=$k;
        }

        // Each interval is interpolated by a 3:degree polynom function
        $h = $this->xdata[$max]-$this->xdata[$min];

        if( $h == 0  ) {
            JpGraphError::RaiseL(19002);
            //('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
        }


        $a = ($this->xdata[$max]-$xpoint)/$h;
        $b = ($xpoint-$this->xdata[$min])/$h;
        return $a*$this->ydata[$min]+$b*$this->ydata[$max]+
        (($a*$a*$a-$a)*$this->y2[$min]+($b*$b*$b-$b)*$this->y2[$max])*($h*$h)/6.0;
    }
}

Code example using spline can be found here (http://jpgraph.net/download/manuals/chunkhtml/example_src/splineex1.html)

Ryan NZ
  • 616
  • 1
  • 9
  • 25

1 Answers1

2

If you really want to use a spline to interpolate your data values, and you want to minimize deviations above and below your actual samples (data points), then you're going to need more samples. You could get them by generating fake/artificial data points via linear interpolation between samples and then regenerating the spline (in which case you'll end up with something more like your touchpad drawing). But if you're trying to generate a "smooth" graph which minimizes some sort of average deviation from reality, then you may need to use something other than a spline.

Peter
  • 2,526
  • 1
  • 23
  • 32