File Coverage

blib/lib/PDL/Fit/Linfit.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Fit::Linfit - routines for fitting data with linear combinations of functions.
4              
5             =head1 DESCRIPTION
6              
7             This module contains routines to perform general curve-fits to a set (linear combination)
8             of specified functions.
9              
10             Given a set of Data:
11              
12             (y0, y1, y2, y3, y4, y5, ...ynoPoints-1)
13              
14             The fit routine tries to model y as:
15              
16             y' = beta0*x0 + beta1*x1 + ... beta_noCoefs*x_noCoefs
17              
18             Where x0, x1, ... x_noCoefs, is a set of functions (curves) that
19             the are combined linearly using the beta coefs to yield an approximation
20             of the input data.
21              
22             The Sum-Sq error is reduced to a minimum in this curve fit.
23              
24             B
25              
26             =over 1
27              
28             =item $data
29              
30             This is your data you are trying to fit. Size=n
31              
32             =item $functions
33              
34             2D array. size (n, noCoefs). Row 0 is the evaluation
35             of function x0 at all the points in y. Row 1 is the evaluation of
36             of function x1 at all the points in y, ... etc.
37              
38             Example of $functions array Structure:
39              
40             $data is a set of 10 points that we are trying to model using
41             the linear combination of 3 functions.
42              
43             $functions = ( [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], # Constant Term
44             [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], # Linear Slope Term
45             [ 0, 2, 4, 9, 16, 25, 36, 49, 64, 81] # quadradic term
46             )
47              
48             =back
49              
50             =head1 SYNOPSIS
51              
52             $yfit = linfit1d $data, $funcs
53              
54              
55             =head1 FUNCTIONS
56              
57             =head2 linfit1d
58              
59             =for ref
60              
61             1D Fit linear combination of supplied functions to data using min chi^2 (least squares).
62              
63             =for usage
64              
65             Usage: ($yfit, [$coeffs]) = linfit1d [$xdata], $data, $fitFuncs, [Options...]
66              
67             =for sig
68              
69             Signature: (xdata(n); ydata(n); $fitFuncs(n,order); [o]yfit(n); [o]coeffs(order))
70              
71             Uses a standard matrix inversion method to do a least
72             squares/min chi^2 fit to data.
73              
74             Returns the fitted data and optionally the coefficients.
75              
76             One can thread over extra dimensions to do multiple fits (except
77             the order can not be threaded over - i.e. it must be one fixed
78             set of fit functions C.
79              
80             The data is normalised internally to avoid overflows (using the
81             mean of the abs value) which are common in large polynomial
82             series but the returned fit, coeffs are in
83             unnormalised units.
84              
85              
86             =for example
87              
88             # Generate data from a set of functions
89             $xvalues = sequence(100);
90             $data = 3*$xvalues + 2*cos($xvalues) + 3*sin($xvalues*2);
91            
92             # Make the fit Functions
93             $fitFuncs = cat $xvalues, cos($xvalues), sin($xvalues*2);
94            
95             # Now fit the data, Coefs should be the coefs in the linear combination
96             # above: 3,2,3
97             ($yfit, $coeffs) = linfit1d $data,$fitFuncs;
98            
99              
100             =for options
101              
102             Options:
103             Weights Weights to use in fit, e.g. 1/$sigma**2 (default=1)
104              
105              
106             =cut
107              
108             package PDL::Fit::Linfit;
109              
110             @EXPORT_OK = qw( linfit1d );
111             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
112              
113 1     1   587 use PDL::Core;
  1         2  
  1         6  
114 1     1   26 use PDL::Basic;
  1         33  
  1         8  
115 1     1   7 use PDL::Exporter;
  1         4  
  1         5  
116             @ISA = qw( PDL::Exporter );
117 1     1   7 use PDL::Options ':Func';
  1         1  
  1         96  
118 1     1   232 use PDL::Slatec; # For matinv()
  0            
  0            
119              
120             sub PDL::linfit1d {
121             my $opthash = ref($_[-1]) eq "HASH" ? pop(@_) : {} ;
122             my %opt = parse( { Weights=>ones(1) }, $opthash ) ;
123             barf "Usage: linfit1d incorrect args\n" if $#_<1 or $#_ > 3;
124             my ($x, $y, $fitfuncs) = @_;
125             if ($#_ == 1) {
126             ($y, $fitfuncs) = @_;
127             $x = $y->xvals;
128             }
129            
130             my $wt = $opt{Weights};
131            
132             # Internally normalise data
133            
134             my $ymean = (abs($y)->sum)/($y->nelem);
135             $ymean = 1 if $ymean == 0;
136             my $y2 = $y / $ymean;
137            
138             # Do the fit
139            
140             my $M = $fitfuncs->xchg(0,1);
141             my $C = $M->xchg(0,1) x ($M * $wt->dummy(0)) ;
142             my $Y = $M->xchg(0,1) x ($y2->dummy(0) * $wt->dummy(0));
143              
144             # Fitted coefficients vector
145              
146             $c = matinv($C) x $Y;
147            
148             # Fitted data
149              
150             $yfit = ($M x $c)->clump(2); # Remove first dim=1
151            
152             $yfit *= $ymean; # Un-normalise
153             if (wantarray) {
154             my $coeff = $c->clump(2);
155             $coeff *= $ymean; # Un-normalise
156             return ($yfit, $coeff);
157             }
158             else{
159             return $yfit;
160             }
161            
162             }
163             *linfit1d = \&PDL::linfit1d;
164              
165              
166             1;