August 10th, 2012, 02:43 PM

Linear Interpolation
I am trying to use linear interpolation on a data file that has wavelength and another value (its not important what the 2nd value is.) I am using interpolation so i can run another set of wavelengths from a diff. file (slightly different values then the other wavelengths) thru. The original wavelengths tell me what the 2nd value shud be for X wavelength. Now i need to compare another set of wavelengths from a different file and ask what the 2nd variable should be.
I have been trying to use either, Math::Interpolate, Math::Interpolation, but i cant figure out how to use the module.
Code:
#!/usr/bin/perl
#use warnings;
use Data::Dumper;
use FileHandle;
use FindBin;
#use strict;
use lib "$FindBin::Bin/../CLAPv3/";
use lib "$FindBin::Bin/../CLAPv3/Math/Interpolator/";
use Math::Interpolate;
#use PDL::Interpolate;
#use Math::Interpolate qw(derivatives constant_interpolate
# linear_interpolate robust_interpolate);
use Math::Interpolate qw(derivatives linear_interpolate);
#use Math::Interpolator;
#use Math::Interpolator::Linear;
#use Math::Interpolator::Source;
#use Math::Interpolator::Knot;
#### Done in Hashes
our %data;
our %datac;
&intro;
sub intro {
print " Welcome to C.L.A.P. 1.0\n\n";
&calcscan;
&calccsv;
}
sub calcscan {
#@files = qw($redscan $bluescan $greenscan);
$redscan = 'red_scan.txt';
$bluescan = 'blue_scan.txt';
$greenscan = 'green_scan.txt';
open (FH, $redscan) or die "Oooooh Snap ";
while (<FH>) {
chomp;
next if $. < 417;
next if $. > 3664;
($key,$value) = split (' ');
$data{$key}= $value;
#print dump(\%data);
foreach $key (sort {$a <=> $b} keys %data) {
}
}
close FH;
}
sub calccsv {
$csv = 'CLAP_sensor_sensitivity.csv';
open $FHc, $csv or die "Oh Snap";
while (<$FHc>) {
chomp;
next if $. < 2;
#next if $. > 100;
($keyc,$valuec) = split (' ');
chop($keyc);
$data{$keyc}= $valuec;
#print Dumper(\%data);
foreach $keyc (sort {$a <=> $b} keys %datac) {}
my @x = $keyc;
my @y = $valuec;
my @dy = derivatives( \@x, \@y);
my($l_y, $l_dy) = linear_interpolate($value, \@x, \@y);
print $l_y, "\n";
}
close $FHc;
}
I am trying to use Math::Interpolate here.
August 10th, 2012, 03:52 PM

Why did you comment out the strict and warnings pragmas?
Your first step is to reenable those pragmas and fix the problems that they point out.
Why are you attempting to load Math::Interpolate twice?
Since that module hasn't been maintained since 1999, I'd suggest that you find newer module which is being maintained. Possibly Math::Interpolator::Robust which is a subclass of Math::Interpolator.
August 10th, 2012, 04:03 PM

I think im going to try and take out all the modules and do the task manually. I have found a thread with code similar to what i need but im having a hard time reverse engineering it for my needs.
Code:
#!/usr/bin/perl w
use strict;
use Time::Local;
use Data::Dumper;
my @data;
my @graph;
my $one_day = 60*60*24; #one day in seconds
while (<DATA>)
{
next if /^\s*$/; #skip blank lines
my ($date, $weight) = split;
my $epoch = epoch($date);
push (@data, [$epoch, $weight]);
}
# input data is sorted already sorted
# But the algortihm depends upon sorted date information
# so I did that to make sure
#
@data = sort{$a>[0] <=> $b>[0]} @data;
# axis x will be adjusted to #days from the date of first data
# axis y is in weight
my ($x_base_epoch, $y1) = @{shift(@data)};
my $x1_day =0;
foreach my $r_xy(@data)
{
my ($x2, $y2) = @$r_xy;
$x2 = $x_base_epoch;
my $x2_day = int($x2/$one_day);
# slope is delta weight/ delta days
my $slope = ($y2$y1)/($x2_day$x1_day);
# fill in missing data points...
# by linear interpolation
for (; $x1_day< $x2_day; $x1_day++) # x interval not tested for
# other than 1
{
my $y = $slope*($x1_day$x2_day) + $y2;
push @graph, [$x1_day,$y];
}
$y1= $y2;
}
#fixup for last data point
my ($fx,$fy) = @{$data[1]};
push @graph, [($fx$x_base_epoch)/$one_day, $fy];
### data to graph is in @graph ###
### I leave that part to the OP ###
print "$_>[0] $_>[1]\n" foreach @graph;
sub epoch
{
my $date = shift;
my ($month, $day, $year) = split(m/,$date);
my $time = timelocal(0,0,0, $day, $month1, $year1900);
return $time;
}
I need to take out the time factor. I can extract the info from the files and split it appropriately. Epoc is confusing me, im not sure if or how its related to what im trying to do.
August 10th, 2012, 04:56 PM

Take out all the modules is probably a wrong idea. You have there usually very well written pieces of code, generally much better written than if you or I would write it.
There are some environments at my work where it is pretty complicated to obtain the installation of a module, I sometimes have to give up and to try to find another solution (like, sometimes, simply copy the code of the .pm module I need), but taking out the modules is a wrong idea.
August 10th, 2012, 07:50 PM

I have the same problem with modules. I wont take out all the modules i just need another solution for the interpolation module.
August 10th, 2012, 08:23 PM

I have not used or tested any of the math interpolation modules, but based on your opening question, I wonder if Math::Interpolator::Linear is the module you need. I can't say for sure because I'm not sure what you are needing to accomplish.
August 11th, 2012, 03:19 PM

I have a graph. I need the program to connect the dots(with a line.) Then I give it a random X value. The program will look at the X value and find where it intersects with the 'line' i talked about at the beginning. It will return the Y value (I already know what X is.)
To sum it up if you have not understood me this far. The program makes an educated guess (based on the data points) as to what the Y value is. The random X i am asking for lies close to the original data points of X but not exactly on any of them.
August 11th, 2012, 03:23 PM

for example i have X= 100 and Y = 10. Whats Y going to be when X is 100.1. (remember linear interpolation uses 2 data points that are close together when making the educated guess.)
August 11th, 2012, 04:47 PM

August 11th, 2012, 06:40 PM

Do you have two points? If so don't bother with a module; recall how you did it in algebra class and compute slope/intercept. Plug in your new x value and have an appropriate amount of fun.
More than two points? Push the points into an ordinary least squares regression. That will hand you a slope/intercept. Plug in your new x value and have an appropriate amount of fun.
Statistics::LineFit can be used to perform the regression:
Code:
C:\temp>cat ols.pl
use Statistics::LineFit;
my @x = ( 10, 20, 30 );
my @y = ( 10, 40, 70 );
$ols = Statistics::LineFit>new;
$ols>setData( \@x, \@y );
my ($b, $m) = $ols>coefficients;
my $x2 = 15;
printf "Interpolated f(%d) = %d\n", $x2, ($x2 * $m) + $b;
C:\temp>perl ols.pl
Interpolated f(15) = 25
EditTo clarify, I mean if you're interpolating based on two points, not that you only have two points total. (For example if you had a rough outline of a curve and were interpolating from the closest two points on either side of the value you want.) The OLS is only useful if you have many approximations of points on the line. (For example a number of measurements that could contain some discrepancy due to other factors or measurement error.)
Last edited by OmegaZero; August 11th, 2012 at 09:51 PM.
sub{*{$::{$_}}{CODE}==$_[0]&& print for(%:: )}>(\&Meh);
August 12th, 2012, 03:06 PM

Thanks OmegaZero, ur advice helps.I think I may be able to do it without Statistics module. I need to take X[1]  X[0] and same with Y (equation for slope.) I am going to do something along the lines of using a counter and put " $keyc[i]  $keyc[i+1] " i will need increase by 1 till the end of file. The problem im having is that the list of values is stored inside a scalar value (i thought it went into an array but it didnt.) I cannot access values by doing $keyc[X] because its not in an array. Should i convert it some how? I am talking about this piece of code
Code:
($keyc,$valuec) = split (' ');
chop($keyc);
$datac{$keyc}= $valuec;
foreach $keyc (sort {$a <=> $b} keys %datac) { print $keyc; }
you can igonore the chop line. I tried switching around some $ to @ to try and make it into an array but i have come up with noting but errors.
August 13th, 2012, 08:57 PM

You might do something like:
Code:
for $key (sort keys %hash) {
unless( defined $last_key ) {
$last_key = $key;
next;
}
# ... do something with $last_key and $hash{$last_key}
# ... and $key and $hash{$key}
}
to avoid using an array.
I would still stick with the array solution though. Probably something like:
Code:
push @array, [ split /\s+/ ] while <FILE>;
@array= sort { $a>[0] <=> $b>[0] } @array;
for $i ( 1 .. $#array ) {
# ... do something with $array[$i1][0] and $array[$i1][1]
# ... and $array[$i][0] and $array[$i][1]
}
sub{*{$::{$_}}{CODE}==$_[0]&& print for(%:: )}>(\&Meh);
August 14th, 2012, 05:06 PM

I followed your advice, my problem now is adding or subtracting them. This code prints the correct answer for the first value but then it repeats it 100 times instead of moving on. The value it prints is correct mathematically but it is not what i want. This subtracts key  value. $datac{$last_key} = $valuec.
Code:
sub calccsv {
$csv = 'CLAP_sensor_sensitivity.csv';
open $FHc, $csv or die "Oh Snap";
while (<$FHc>) {
chomp;
next if $. < 2;
next if $. > 100;
($keyc,$valuec) = split (' ');
chop($keyc);
$datac{$keyc}= $valuec;
for $key (sort keys %datac) {
unless ( defined $last_key) {
$last_key = $keyc;
next;
}
@newkey = $last_key + $datac{$last_key};
foreach (@newkey) {
print $newkey[1..100], "\n";
}
#return ($keyc, $valuec);
}