Linear interpolation with PHP

I’ve always loved working with Excel. Yes, it can be a bit of a struggle, but in the end it always works, right? Lately I’ve been working on a couple of statistical tools, developed in PHP. Before development (okay, sometimes during) development I find it always helpful to put all calculations and formulas in Excel to check if everything that it returns is as expected. Plus this is always a good reference for later use.

One project used the FORECAST-function of Excel, which is basically a linear interpolation and extrapolation calculation. But now, how do you port a statistical function like that to PHP, which is obviously not made for math- and statistics lovers. And sadly enough I’m not that good in math to be able to build the function myself.

After a while I finally found THIS example. It’s a nice example that calculates the slope between two points in an array of X and Y’s. I’ve made some changes to fit my needs:

  • Removed float stuff, which makes more sense
  • Added support for different steps than just 1
  • Added support for Y input and X output (just like Excel)

The output is not the same as Excel’s FORECAST, but in my opinion it’s even better (dataset is on top, 25 is the value we want to interpolate):

Excel LInterpolation

X

Y

X

Y

17

22.6

17

22.6

19

26.6

19

26.6

21

30.3

21

30.3

26

38.9

26

38.9

36

47.3

36

47.3

60

96.9

60

96.9

121

220

121

220

19.746

25

18.2

25

So it’s pretty accurate.

Here’s the code. Maybe not perfect yet, but almost :)

function LInterpolation($arr,$step=1,$returnval) {
	global $smarty;
	
	if(!is_array($arr)){
		$smarty->assign('error', array('str' => '<b>LInterpolation:</b> expects an array'));
		return false;
	}
	
	$narr = array();
	foreach($arr AS $key => $value) {
		$nval = $value;
		$nkey = $key;
		$narr[(string) $nkey] = (string) $nval;
	}
	$arr = $narr;
	
	$array_keys = array_keys($arr);
	$first_key  = current($array_keys);
	$last_key   = end($array_keys);
	
	$arr_calc = array();
	foreach($arr AS $key => $value){
		if(is_numeric($key) == false || is_numeric($value) == false){
			$smarty->assign('error', array('str' => '<b>LInterpolation:</b> array keys & values must be numeric'));
			return false;
		}
		if($key !== $first_key){
			array_push($arr_calc,((($value-$arr["$before_key"]) / ($key-$before_key)) * $step));
		}
		$before_key = $key;
	}
	
   $x = -1;
   $arr_new = array();
   for($i = $first_key; (string) $i <= (string) $last_key; $i=$i+$step){
	  if(!isset($arr["$i"])){
		 $arr_new["$i"] = ($arr_new["$before_key"] + $arr_calc[$x]);
	  } else {
		 $arr_new["$i"] = $arr["$i"];
		 $x++;
	  }
	  if (is_numeric($returnval) && $arr_new["$i"] >= $returnval){
	    if($arr_new["$before_key"] == '' && (float) $returnval !== (float) $arr_new["$i"]){
			$smarty->assign('error', array('str' => 'outofbounds', 'vars' => array($returnval, $i, $arr_new["$i"])));
			return false;
		}
		$diff_key = $i - $before_key;
		$diff_val = $arr_new["$i"] - $arr_new["$before_key"];
		$growth = $returnval - $arr_new["$before_key"];
		$growthperunit = $diff_key / $diff_val;
		$ttlgrowth = $growth * $growthperunit;
		$est = $before_key + $ttlgrowth;
		return $est;
	  }
	  $before_key = $i;
   }
   
   if(is_numeric($returnval)){
		return $before_key;
   }
   return $arr_new;
}

Call the function as followed:

LInterpolation(
   array(
      "17"=>"22.6",
      "19"=>"26.6",
      "21"=>"30.3",
	  "26"=>"38.9",
	  "36"=>"47.3",
	  "60"=>"96.9",
	  "121"=>"220",
   ),1,null)

This will return an array with all predicted values between the first (17) and last element (121). Want to predict a specific X? Use the third element in the function for the Y:

LInterpolation(
   array(
      "17"=>"22.6",
      "19"=>"26.6",
      "21"=>"30.3",
	  "26"=>"38.9",
	  "36"=>"47.3",
	  "60"=>"96.9",
	  "121"=>"220",
   ),1,1.20)

This will return only the exact value of X.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>