File Coverage

blib/lib/PDL/Fit/Polynomial.pm
Criterion Covered Total %
statement 36 41 87.8
branch 6 12 50.0
condition 1 3 33.3
subroutine 6 6 100.0
pod 0 1 0.0
total 49 63 77.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Fit::Polynomial - routines for fitting with polynomials
4              
5             =head1 DESCRIPTION
6              
7             This module contains routines for doing simple
8             polynomial fits to data
9              
10             =head1 SYNOPSIS
11              
12             $yfit = fitpoly1d $data;
13              
14              
15             =head1 FUNCTIONS
16              
17             =head2 fitpoly1d
18              
19             =for ref
20              
21             Fit 1D polynomials to data using min chi^2 (least squares)
22              
23             =for usage
24              
25             Usage: ($yfit, [$coeffs]) = fitpoly1d [$xdata], $data, $order, [Options...]
26              
27             =for sig
28              
29             Signature: (x(n); y(n); [o]yfit(n); [o]coeffs(order))
30              
31             Uses a standard matrix inversion method to do a least
32             squares/min chi^2 polynomial fit to data. Order=2 is a linear
33             fit (two parameters).
34              
35             Returns the fitted data and optionally the coefficients.
36              
37             One can thread over extra dimensions to do multiple fits (except
38             the order can not be threaded over - i.e. it must be one fixed
39             scalar number like "4").
40              
41             The data is normalised internally to avoid overflows (using the
42             mean of the abs value) which are common in large polynomial
43             series but the returned fit, coeffs are in
44             unnormalised units.
45              
46              
47             =for example
48              
49             $yfit = fitpoly1d $data,2; # Least-squares line fit
50             ($yfit, $coeffs) = fitpoly1d $x, $y, 4; # Fit a cubic
51            
52             $fitimage = fitpoly1d $image,3 # Fit a quadratic to each row of an image
53            
54             $myfit = fitpoly1d $line, 2, {Weights => $w}; # Weighted fit
55              
56             =for options
57              
58             Options:
59             Weights Weights to use in fit, e.g. 1/$sigma**2 (default=1)
60              
61              
62             =cut
63              
64             package PDL::Fit::Polynomial;
65              
66             @EXPORT_OK = qw( fitpoly1d );
67             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
68              
69 1     1   635 use PDL::Core;
  1         2  
  1         8  
70 1     1   9 use PDL::Basic;
  1         3  
  1         6  
71 1     1   7 use PDL::Exporter;
  1         2  
  1         6  
72             @ISA = qw( PDL::Exporter );
73              
74 1     1   6 use PDL::Options ':Func';
  1         1  
  1         127  
75             # use PDL::Slatec; # For matinv()
76 1     1   7 use PDL::MatrixOps; # for inv(), using this instead of call to Slatec routine
  1         2  
  1         7  
77              
78            
79             sub PDL::fitpoly1d {
80 1 50   1 0 9 my $opthash = ref($_[-1]) eq "HASH" ? pop(@_) : {} ;
81 1         5 my %opt = parse( { Weights=>ones(1) }, $opthash ) ;
82 1 50 33     9 barf "Usage: fitpoly1d incorrect args\n" if $#_<1 or $#_ > 2;
83 1         3 my ($x, $y, $order) = @_;
84 1 50       4 if ($#_ == 1) {
85 0         0 ($y, $order) = @_;
86 0         0 $x = $y->xvals;
87             }
88            
89 1         2 my $wt = $opt{Weights};
90            
91             # Internally normalise data
92            
93             # means for each 1D data set
94 1         5 my $xmean = (abs($x)->average)->dummy(0); # dummy for correct threading
95 1         9 my $ymean = (abs($y)->average)->dummy(0);
96 1 50       10 (my $tmp = $ymean->where($ymean == 0)) .= 1 if any $ymean == 0;
97 1 50       4 ($tmp = $xmean->where($xmean == 0)) .= 1 if any $xmean == 0;
98 1         50 my $y2 = $y / $ymean;
99 1         15 my $x2 = $x / $xmean;
100            
101             # Do the fit
102            
103 1         6 my $pow = sequence($order);
104 1         5 my $M = $x2->dummy(0) ** $pow;
105 1         17 my $C = $M->xchg(0,1) x ($M * $wt->dummy(0)) ;
106 1         19 my $Y = $M->xchg(0,1) x ($y2->dummy(0) * $wt->dummy(0));
107              
108             # Fitted coefficients vector
109              
110             ## $a1 = matinv($C) x $Y;
111             ## print "matinv: \$C = $C, \$Y = $Y, \$a1 = $a1\n";
112 1         13 my $a1 = inv($C) x $Y; # use inv() instead of matinv() to avoid Slatec dependency
113             ## print "inv: \$C = $C, \$Y = $Y, \$a1 = $a1\n";
114            
115             # Fitted data
116              
117 1         6 $yfit = ($M x $a1)->clump(2); # Remove first dim=1
118            
119 1         9 $yfit *= $ymean; # Un-normalise
120 1 50       4 if (wantarray) {
121 0         0 my $coeff = $a1->clump(2);
122 0         0 $coeff *= $ymean / ($xmean ** $pow); # Un-normalise
123 0         0 return ($yfit, $coeff);
124             }
125             else{
126 1         19 return $yfit;
127             }
128            
129             }
130             *fitpoly1d = \&PDL::fitpoly1d;
131              
132             =head1 BUGS
133              
134             May not work too well for data with large dynamic range.
135              
136             =head1 SEE ALSO
137              
138             L
139              
140             =head1 AUTHOR
141              
142             This file copyright (C) 1999, Karl Glazebrook (kgb@aaoepp.aao.gov.au).
143             All rights reserved. There
144             is no warranty. You are allowed to redistribute this software
145             documentation under certain conditions. For details, see the file
146             COPYING in the PDL distribution. If this file is separated from the
147             PDL distribution, the copyright notice should be included in the file.
148              
149              
150             =cut
151              
152              
153              
154             1;
155              
156