]> Creatis software - CreaPhase.git/blobdiff - octave_packages/optim-1.2.0/wpolyfit.m
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / optim-1.2.0 / wpolyfit.m
diff --git a/octave_packages/optim-1.2.0/wpolyfit.m b/octave_packages/optim-1.2.0/wpolyfit.m
new file mode 100644 (file)
index 0000000..a9563fa
--- /dev/null
@@ -0,0 +1,261 @@
+## Author: Paul Kienzle <pkienzle@gmail.com>
+## This program is granted to the public domain.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{p}, @var{s}] =} wpolyfit (@var{x}, @var{y}, @var{dy}, @var{n})
+## Return the coefficients of a polynomial @var{p}(@var{x}) of degree
+## @var{n} that minimizes
+## @iftex
+## @tex
+## $$
+## \sum_{i=1}^N (p(x_i) - y_i)^2
+## $$
+## @end tex
+## @end iftex
+## @ifinfo
+## @code{sumsq (p(x(i)) - y(i))},
+## @end ifinfo
+## to best fit the data in the least squares sense.  The standard error
+## on the observations @var{y} if present are given in @var{dy}.
+##
+## The returned value @var{p} contains the polynomial coefficients 
+## suitable for use in the function polyval.  The structure @var{s} returns
+## information necessary to compute uncertainty in the model.
+##
+## To compute the predicted values of y with uncertainty use
+## @example
+## [y,dy] = polyconf(p,x,s,'ci');
+## @end example
+## You can see the effects of different confidence intervals and
+## prediction intervals by calling the wpolyfit internal plot
+## function with your fit:
+## @example
+## feval('wpolyfit:plt',x,y,dy,p,s,0.05,'pi')
+## @end example
+## Use @var{dy}=[] if uncertainty is unknown.
+##
+## You can use a chi^2 test to reject the polynomial fit:
+## @example
+## p = 1-chi2cdf(s.normr^2,s.df);
+## @end example
+## p is the probability of seeing a chi^2 value higher than that which 
+## was observed assuming the data are normally distributed around the fit.
+## If p < 0.01, you can reject the fit at the 1% level.
+##
+## You can use an F test to determine if a higher order polynomial 
+## improves the fit:
+## @example
+## [poly1,S1] = wpolyfit(x,y,dy,n);
+## [poly2,S2] = wpolyfit(x,y,dy,n+1);
+## F = (S1.normr^2 - S2.normr^2)/(S1.df-S2.df)/(S2.normr^2/S2.df);
+## p = 1-f_cdf(F,S1.df-S2.df,S2.df);
+## @end example
+## p is the probability of observing the improvement in chi^2 obtained
+## by adding the extra parameter to the fit.  If p < 0.01, you can reject 
+## the lower order polynomial at the 1% level.
+##
+## You can estimate the uncertainty in the polynomial coefficients 
+## themselves using
+## @example
+## dp = sqrt(sumsq(inv(s.R'))'/s.df)*s.normr;
+## @end example
+## but the high degree of covariance amongst them makes this a questionable
+## operation.
+##
+## @deftypefnx {Function File} {[@var{p}, @var{s}, @var{mu}] =} wpolyfit (...)
+##
+## If an additional output @code{mu = [mean(x),std(x)]} is requested then 
+## the @var{x} values are centered and normalized prior to computing the fit.
+## This will give more stable numerical results.  To compute a predicted 
+## @var{y} from the returned model use
+## @code{y = polyval(p, (x-mu(1))/mu(2)}
+##
+## @deftypefnx {Function File} wpolyfit (...)
+##
+## If no output arguments are requested, then wpolyfit plots the data,
+## the fitted line and polynomials defining the standard error range.
+##
+## Example
+## @example
+## x = linspace(0,4,20);
+## dy = (1+rand(size(x)))/2;
+## y = polyval([2,3,1],x) + dy.*randn(size(x));
+## wpolyfit(x,y,dy,2);
+## @end example
+##
+## @deftypefnx {Function File} wpolyfit (..., 'origin')
+##
+## If 'origin' is specified, then the fitted polynomial will go through
+## the origin.  This is generally ill-advised.  Use with caution.
+##
+## Hocking, RR (2003). Methods and Applications of Linear Models.
+## New Jersey: John Wiley and Sons, Inc.
+##
+## @end deftypefn
+## @seealso{polyfit,polyconf}
+
+function [p_out, s, mu] = wpolyfit (varargin)
+
+  ## strip 'origin' of the end
+  args = length(varargin);
+  if args>0 && ischar(varargin{args})
+    origin = varargin{args};
+    args--;
+  else
+    origin='';
+  endif
+  ## strip polynomial order off the end
+  if args>0
+    n = varargin{args};
+    args--;
+  else
+    n = [];
+  end
+  ## interpret the remainder as x,y or x,y,dy or [x,y] or [x,y,dy]
+  if args == 3
+    x = varargin{1};
+    y = varargin{2};
+    dy = varargin{3};
+  elseif args == 2
+    x = varargin{1};
+    y = varargin{2};
+    dy = [];
+  elseif args == 1
+    A = varargin{1};
+    [nr,nc]=size(A);
+    if all(nc!=[2,3])
+      error("wpolyfit expects vectors x,y,dy or matrix [x,y,dy]");
+    endif
+    dy = [];
+    if nc == 3, dy = A(:,3); endif
+    y = A(:,2);
+    x = A(:,1);
+  else
+    usage ("wpolyfit (x, y [, dy], n [, 'origin'])");
+  end
+
+  if (length(origin) == 0)
+    through_origin = 0;
+  elseif strcmp(origin,'origin')
+    through_origin = 1;
+  else
+    error ("wpolyfit: expected 'origin' but found '%s'", origin)
+  endif
+
+  if any(size (x) != size (y))
+    error ("wpolyfit: x and y must be vectors of the same size");
+  endif
+  if length(dy)>1 && length(y) != length(dy)
+    error ("wpolyfit: dy must be a vector the same length as y");
+  endif
+
+  if (! (isscalar (n) && n >= 0 && ! isinf (n) && n == round (n)))
+    error ("wpolyfit: n must be a nonnegative integer");
+  endif
+
+  if nargout == 3
+    mu = [mean(x), std(x)];
+    x = (x - mu(1))/mu(2);
+  endif
+
+  k = length (x);
+
+  ## observation matrix
+  if through_origin
+    ## polynomial through the origin y = ax + bx^2 + cx^3 + ...
+    A = (x(:) * ones(1,n)) .^ (ones(k,1) * (n:-1:1));
+  else
+    ## polynomial least squares y = a + bx + cx^2 + dx^3 + ...
+    A = (x(:) * ones (1, n+1)) .^ (ones (k, 1) * (n:-1:0));
+  endif
+
+  [p,s] = wsolve(A,y(:),dy(:));
+
+  if through_origin
+    p(n+1) = 0;
+  endif
+
+  if nargout == 0
+    good_fit = 1-chi2cdf(s.normr^2,s.df);
+    printf("Polynomial: %s  [ p(chi^2>observed)=%.2f%% ]\n", polyout(p,'x'), good_fit*100);
+    plt(x,y,dy,p,s,'ci');
+  else
+    p_out = p';
+  endif
+
+function plt(x,y,dy,p,s,varargin)
+
+  if iscomplex(p)
+    # XXX FIXME XXX how to plot complex valued functions?
+    # Maybe using hue for phase and saturation for magnitude
+    # e.g., Frank Farris (Santa Cruz University) has this:
+    # http://www.maa.org/pubs/amm_complements/complex.html
+    # Could also look at the book
+    #   Visual Complex Analysis by Tristan Needham, Oxford Univ. Press
+    # but for now we punt
+    return
+  end
+
+  ## decorate the graph
+  grid('on');
+  xlabel('abscissa X'); ylabel('data Y');
+  title('Least-squares Polynomial Fit with Error Bounds');
+
+  ## draw fit with estimated error bounds
+  xf = linspace(min(x),max(x),150)';
+  [yf,dyf] = polyconf(p,xf,s,varargin{:});
+  plot(xf,yf+dyf,"g.;;", xf,yf-dyf,"g.;;", xf,yf,"g-;fit;");
+
+  ## plot the data
+  hold on;
+  if (isempty(dy))
+    plot(x,y,"x;data;");
+  else
+    if isscalar(dy), dy = ones(size(y))*dy; end
+    errorbar (x, y, dy, "~;data;");
+  endif
+  hold off;
+
+  if strcmp(deblank(input('See residuals? [y,n] ','s')),'y')
+    clf;
+    if (isempty(dy))
+      plot(x,y-polyval(p,x),"x;data;");
+    else
+      errorbar(x,y-polyval(p,x),dy, '~;data;');
+    endif
+    hold on;
+    grid on;
+    ylabel('Residuals');
+    xlabel('abscissa X'); 
+    plot(xf,dyf,'g.;;',xf,-dyf,'g.;;');
+    hold off;
+  endif
+
+%!demo % #1  
+%! x = linspace(0,4,20);
+%! dy = (1+rand(size(x)))/2;
+%! y = polyval([2,3,1],x) + dy.*randn(size(x));
+%! wpolyfit(x,y,dy,2);
+  
+%!demo % #2
+%! x = linspace(-i,+2i,20);
+%! noise = ( randn(size(x)) + i*randn(size(x)) )/10;
+%! P = [2-i,3,1+i];
+%! y = polyval(P,x) + noise;
+%! wpolyfit(x,y,2)
+
+%!demo
+%! pin = [3; -1; 2];
+%! x = -3:0.1:3;
+%! y = polyval (pin, x);
+%!
+%! ## Poisson weights
+%! # dy = sqrt (abs (y));
+%! ## Uniform weights in [0.5,1]
+%! dy = 0.5 + 0.5 * rand (size (y));
+%!
+%! y = y + randn (size (y)) .* dy;
+%! printf ("Original polynomial: %s\n", polyout (pin, 'x'));
+%! wpolyfit (x, y, dy, length (pin)-1);
+
+