File Coverage

blib/lib/PDL/Fit/Polynomial.pm
Criterion Covered Total %
statement 40 42 95.2
branch 6 12 50.0
condition 1 3 33.3
subroutine 8 8 100.0
pod 0 1 0.0
total 55 66 83.3


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             $x = sequence(20)-10;
13             $coeff_orig = cdouble(30,-2,3,-2); # order used in this module
14             $y = 30-2*$x+3*$x**2-2*$x**3; # or: $y = polyval($coeff_orig->slice("-1:0"), $x->r2C);
15             $y += ($x->grandom - 0.5) * 100;
16             ($yfit, $coeff) = fitpoly1d($x,$y,4);
17             use PDL::Graphics::Simple;
18             $w = pgswin();
19             $xi = zeroes(100)->xlinvals(-10,9);
20             $yi = polyval($coeff->r2C->slice("-1:0"), $xi->r2C);
21             $w->plot(with=>'points',$x,$y,
22             with=>'points',$x,$yfit, with=>'line',$xi,$yi);
23              
24             $yfit = fitpoly1d $data,2; # Least-squares line fit
25             ($yfit, $coeffs) = fitpoly1d $x, $y, 4; # Fit a cubic
26              
27             =head1 FUNCTIONS
28              
29             =head2 fitpoly1d
30              
31             =for ref
32              
33             Fit 1D polynomials to data using min chi^2 (least squares)
34              
35             =for usage
36              
37             Usage: ($yfit, [$coeffs]) = fitpoly1d [$xdata], $data, $order, [Options...]
38              
39             =for sig
40              
41             Signature: (x(n); y(n); [o]yfit(n); [o]coeffs(order))
42              
43             Uses a standard matrix inversion method to do a least
44             squares/min chi^2 polynomial fit to data. Order=2 is a linear
45             fit (two parameters).
46              
47             Returns the fitted data and optionally the coefficients (in ascending
48             order of degree, unlike L).
49              
50             One can broadcast over extra dimensions to do multiple fits (except
51             the order can not be broadcasted over - i.e. it must be one fixed
52             scalar number like "4").
53              
54             The data is normalised internally to avoid overflows (using the
55             mean of the abs value) which are common in large polynomial
56             series but the returned fit, coeffs are in
57             unnormalised units.
58              
59             =for example
60              
61             $yfit = fitpoly1d $data,2; # Least-squares line fit
62             ($yfit, $coeffs) = fitpoly1d $x, $y, 4; # Fit a cubic
63            
64             $fitimage = fitpoly1d $image,3 # Fit a quadratic to each row of an image
65            
66             $myfit = fitpoly1d $line, 2, {Weights => $w}; # Weighted fit
67              
68             =for options
69              
70             Options:
71             Weights Weights to use in fit, e.g. 1/$sigma**2 (default=1)
72              
73             =cut
74              
75             package PDL::Fit::Polynomial;
76              
77 1     1   242810 use strict;
  1         2  
  1         65  
78 1     1   15 use warnings;
  1         3  
  1         76  
79 1     1   8 use PDL::Core;
  1         1  
  1         8  
80 1     1   438 use PDL::Basic;
  1         2  
  1         8  
81 1     1   271 use PDL::Exporter;
  1         2  
  1         10  
82 1     1   53 use PDL::Options ':Func';
  1         2  
  1         162  
83 1     1   8 use PDL::MatrixOps; # for inv(), using this instead of call to Slatec routine
  1         2  
  1         9  
84              
85             our @EXPORT_OK = qw( fitpoly1d );
86             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
87             our @ISA = qw( PDL::Exporter );
88              
89             sub PDL::fitpoly1d {
90 1 50   1 0 280849 my $opthash = ref($_[-1]) eq "HASH" ? pop(@_) : {} ;
91 1         6 my %opt = parse( { Weights=>ones(1) }, $opthash ) ;
92 1 50 33     515 barf "Usage: fitpoly1d [\$x,] \$y, \$order\n" if @_<2 or @_ > 3;
93 1         4 my ($x, $y, $order) = @_;
94 1 50       5 if ($#_ == 1) {
95 0         0 ($y, $order) = @_;
96 0         0 $x = $y->xvals;
97             }
98              
99 1         3 my $wt = $opt{Weights};
100              
101             # Internally normalise data
102              
103             # means for each 1D data set
104 1         6 my $xmean = (abs($x)->average)->dummy(0); # dummy for correct broadcasting
105 1         271 my $ymean = (abs($y)->average)->dummy(0);
106 1 50       105 (my $tmp = $ymean->where($ymean == 0)) .= 1 if any $ymean == 0;
107 1 50       296 ($tmp = $xmean->where($xmean == 0)) .= 1 if any $xmean == 0;
108 1         110 my $y2 = $y / $ymean;
109 1         71 my $x2 = $x / $xmean;
110              
111             # Do the fit
112              
113 1         21 my $pow = sequence($order);
114 1         81 my $M = $x2->dummy(0) ** $pow;
115 1         106 my $C = $M->transpose x ($M * $wt->dummy(0)) ;
116 1         200 my $Y = $M->transpose x ($y2->dummy(0) * $wt->dummy(0));
117              
118             # Fitted coefficients vector
119              
120             ## $a1 = matinv($C) x $Y;
121             ## print "matinv: \$C = $C, \$Y = $Y, \$a1 = $a1\n";
122 1         197 my $a1 = inv($C) x $Y; # use inv() instead of matinv() to avoid Slatec dependency
123             ## print "inv: \$C = $C, \$Y = $Y, \$a1 = $a1\n";
124            
125             # Fitted data
126              
127 1         6806 my $yfit = ($M x $a1)->clump(2) * $ymean; # Remove first dim=1, un-normalise
128 1 50       96 return wantarray ? ($yfit, $a1->clump(2) * $ymean / ($xmean ** $pow)) : $yfit;
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             =cut
150              
151             1;