Code covered by the BSD License  

Highlights from
der.m v1.0 (Nov, 2009)

image thumbnail
from der.m v1.0 (Nov, 2009) by Carlos Adrian Vargas Aguilera
Estimates first derivative of an array by using POLYFIT locally on each point ignoring NaNs!

der(X,DT,W,DEG,PAD,DIM)
function [DXDT,DXDTstd] = der(X,DT,W,DEG,PAD,DIM)
%DER   Estimates central first derivative from local slope ignoring NaNs.
%
%   SYNTAX:
%               DXDT = der(X);
%               DXDT = der(X,DT);
%               DXDT = der(X,DT,W);
%               DXDT = der(X,DT,W,DEG);
%               DXDT = der(X,DT,W,DEG,PAD);
%               DXDT = der(X,DT,W,DEG,PAD,DIM);
%               DXDT = der(X,T,...);            % Instead of DT
%     [DXDT,DXDTstd] = der(...);
%
%   INPUT:
%     X   - Data array with/without NaNs. Must have length N in the
%           specified DIM. For example N-by-M.
%     DT  - * An scalar with the sampling interval (argument is then
%             T=(0:N-1)*DT).
%           * A vector T of length N with the X argument: X(T).
%           DEFAULT: 1 (then T = 0:N-1)
%     W   - * An scalar with the window width to be used at estimating the
%             linear regression slope in units of DT. W/DT is forced to be
%             odd. If T was given, W is the number of elements in the
%             window. 
%           * A vector with the M values for each column on X (if DIM=1).
%           DEFAULT: 3*DT (or 3 if T was given)
%     DEG - Polynomial degree to be used on the linear regression.
%           DEFAULT: 1 (fits a line on the small window)
%     PAD - Padding edge option. One of (see NOTE below):
%              'circular'        - Treats series as periodic.
%              'replicate'       - Repeats border elements.
%              'symmetric'       - Pads with mirror reflection of X.
%              'mean'            - Pads with mean series value.
%              'zero'            - Pads with zeros.
%              'nan'             - Pads with NaNs (do not pads!)
%              An scalar         - Padding value.
%              A INTERP1 method  - Extrapolates by using 'linear', 'cubic'
%                                  'spline', 'nearest' or 'pchip' method.
%              A function handle - Extrapolation function with same syntax
%                                  as x0=INTERP1(x,t,t0).
%           DEFAULT: NaN (do not pads at all)
%     DIM - Specifies the location dimension of the time series in the X
%           array.
%           DEFAULT: first non-singleton (generally in columns, DIM=1)
%
%   OUTPUT:
%     DXDT    - First central derivative of X with respect to T at T. Its
%               has the same size as X.
%     DXDTstd - Estimation of the error on the DXDT estimation. Same size
%               as DXDT.
%
%   DESCRIPTION:
%     This program estimate the first central derivative of an array by
%     fitting locally a polynomial (linear regression) and then estimate
%     its slope at T.
%
%     The locality is achieved by using only the surrounding elements of
%     each T element no further than half of the specified rectangular
%     window width. 
%
%     The result is an smoothed derivative where the degree of smoothing
%     increases when the window width is increase. The degree of the
%     polynomial may be specified too, greater than 3 is not recommended,
%     nut is cannot be lower than the number of elements in the window. The
%     latter must be less than the length of the array.
%
%     The error estimation of the each estimated derivative is obtained by
%     assuming X is a random variable. See POLYFIT for details.
%
%   NOTE:
%     * Optional inputs use its DEFAULT value when not given or [].
%     * Optional outputs may or not be called.
%     * Property names may be shortened, as long as they are unambiguous.
%       Similarly for string values. Case is ignored.
%     * Padding options is important for the derivatives at edges values.
%       If your data has a constant derivative on the edges 'replicate' or
%       'symmetric' are recommended. If your data is periodic then use
%       'circular'. The best way is to try different options and keep the
%       "best" for your data.
%
%   EXAMPLE:
%     % Compares DEG 1 and 3 for window width (in number of elements):
%     w = 11;
%     % Data.
%     pnoise = 30;
%     n = 100;
%     t = linspace(0,4*pi,n)';
%     x = sin(t) + (pnoise/100)*(rand(n,1)*2-1); x(end)=[]; t(end)=[];
%     dt = t(2)-t(1);
%     % Linear derivative.
%     [y1,y1std] = der(x,dt,dt*w,1,'re');
%     % Cubic derivative.
%     [y3,y3std] = der(x,dt,dt*w,3,'re');
%     % Draws.
%     figure(1),clf
%     subplot(311)
%      plot(t,sin(t),t,x,'.:'), legend('sin(t)','x')
%      title 'Data'
%     subplot(312)
%      plot(t,cos(t),t,y1,'r.:',t,y3,'g--.',...
%       t,[y1+y1std y1-y1std],'r',t,[y3+y3std y3-y3std],'g')
%      legend('dsin(t)/dt','der(x), DEG=1','der(x), DEG=3')
%      title(sprintf(['Derivatives from %.0f surrounding elements, '...
%       'plus error intervals'],w))
%     subplot(313)
%      stem(t,y1-cos(t),'r')
%      hold on
%       stem(t,y3-cos(t),'g')
%      hold off
%      legend('DEG=1','DEG=3')
%      title 'Derivative error with respect to cos(t)'
%
%   SEE ALSO:
%     DIFF, GRADIENT
%     and
%     MOVINGSLOPE by John D'Errico and NANDERIVATIVE by Carlos Vargas 
%     at http://www.mathworks.com/matlabcentral/fileexchange
%
%
%   ---
%   MFILE:   der.m
%   VERSION: 1.0 (Nov 12, 2009) (<a href="matlab:web('http://www.mathworks.com/matlabcentral/fileexchange/authors/11258')">download</a>) 
%   MATLAB:  7.7.0.471 (R2008b)
%   AUTHOR:  Carlos Adrian Vargas Aguilera (MEXICO)
%   CONTACT: nubeobscura@hotmail.com

%   REVISIONS:
%   1.0      Released. (Nov 12, 2009)

%   DISCLAIMER:
%   der.m is provided "as is" without warranty of any kind, under the
%   revised BSD license.

%   Copyright (c) 2009 Carlos Adrian Vargas Aguilera


% INPUTS CHECK-IN
% -------------------------------------------------------------------------

% Defaults.
DXDT    = [];
DXDTstd = [];
dDT     = 1;
dW      = 3;   % *DT
dDEG    = 1;
dPAD    = NaN;
dDIM    = 1;   % or 2 if X is row vector.

% Checks arguments.
if (nargin<1) 
 error('CVARGAS:der:notEnoughInputs',...
  'At least 1 input is required')
end
if (nargin>6) 
 error('CVARGAS:der:tooManyInputs',...
  'At most 6 inputs are allowed')
end
if (nargout>2) 
 error('CVARGAS:der:tooManyOutputs',...
  'At most 2 outputs are allowed')
end

% Checks X.
if isempty(X), return, end
Xsize = size(X);

% Checks PAD.
if (nargin<5) || isempty(PAD)
 PAD = dPAD;
elseif ischar(PAD)
 l = length(PAD);
 if     strncmpi(PAD,'circular',max(l,2))
  PAD = 'circular';
 elseif strncmpi(PAD,'replicate',l)
  PAD = 'replicate';
 elseif strncmpi(PAD,'symmetric',l)
  PAD = 'symmetric';
 elseif strncmpi(PAD,'mean',l)
  PAD = 'mean';
 elseif strncmpi(PAD,'zero',l)
  PAD = 0;
 elseif strncmpi(PAD,'nan',max(l,2))
  PAD = NaN;
 elseif strncmpi(PAD,'linear',l)
  PAD = @(x,y,xo) interp1(x,y,xo,'linear');
 elseif strncmpi(PAD,'cubic',max(l,2))
  PAD = @(x,y,xo) interp1(x,y,xo,'cubic'); 
 elseif strncmpi(PAD,'spline',l)
  PAD = @(x,y,xo) interp1(x,y,xo,'spline'); 
 elseif strncmpi(PAD,'nearest',max(l,2))
  PAD = @(x,y,xo) interp1(x,y,xo,'nearest');
 elseif strncmpi(PAD,'pchip',l)
  PAD = @(x,y,xo) interp1(x,y,xo,'pchip');
 elseif (exist(PAD,'file')==2)
  PAD = str2func(PAD);
 else
  warning('CVARGAS:der:incorrectPadString',...
   'Unrecognize %s padding. Default used.',PAD)
 end
elseif (isnumeric(PAD) || islogical(PAD)) && ...
  (length(PAD)==1)
 % Continue
elseif (length(PAD)==1) && isa(PAD,'function_handle')
 % Continue
else
 warning('CVARGAS:der:incorrectPadType',...
  'Incorrect padding type. Default used.')
 PAD = dPAD;
end

% Checks DEG.
if (nargin<4) || isempty(DEG)
 DEG = dDEG;
end

% Checks DIM.
if (nargin<6) || isempty(DIM)
 DIM = find(Xsize~=1,1);   %  DIM = min(find(S~=1));
 if isempty(DIM), DIM = dDIM; end
end

% Series length and dimension number.
m    = Xsize(DIM);
Ndim = length(Xsize);

% Reshape series in columns.
n = prod(Xsize)/m;   % Number of series:
X = reshape(permute(X,[DIM 1:DIM-1 DIM+1:Ndim]),m,n);

% Checks DT and T.
if (nargin<2) || isempty(DT)
 DT = dDT;
 T  = repmat((0:m-1)'*DT,1,n);
else
 nDT = numel(DT);
 if nDT==1
  T  = repmat((0:m-1)'*DT,1,n);
 elseif nDT==m
  T  = repmat(DT(:),1,n);
  DT = [];
 elseif nDT==n
  DT = DT(:).';
  T  = repmat((0:m-1)',1,n).*repmat(DT,m,1);
 else
  T  = reshape(permute(DT,[DIM 1:DIM-1 DIM+1:Ndim]),m,n);
  DT = [];
 end
end

% Checks W.
if (nargin<3) || isempty(W)
 W  = repmat(dW,1,n);
else
 W = W(:).';
 if ~isempty(DT)
  W = W./DT;
  if any(abs(W-round(W))>eps*1e4)
   warning('CVARGAS:der:incorrectWMultiple',...
    'W must be a multiple of sampling time DT. ROUND used.')
  end
  W = round(W);
 end
 ind = (W<3);
 if any(ind)
  warning('CVARGAS:der:incorrectWValue',...
   'W/DT too low. Used default.')
  W(ind) = dW;
 end
 ind = ~rem(W,2);
 if any(ind)
  warning('CVARGAS:der:incorrectWOdd',...
   'W/DT forced to be odd.')
  W(ind) = W(ind)+ind;
 end
 if length(W)==1
  W = repmat(W,1,n);
 end
end

% Checks DEG and W.
if DEG>=W
  warning('CVARGAS:der:incorrectDegW',...
   'DEG must be lower than W. NaNs output.')
  DXDT = NaN(Xsize);
  if nargout>1, DXDTstd = DXDT; end
  return
end

% Checks W and X.
if W>=m
  warning('CVARGAS:der:tooLargeW',...
   'W must be less than the array length. Continue anyway.')
end


% -------------------------------------------------------------------------
% MAIN
% -------------------------------------------------------------------------

% Window semi width.
W2 = floor(W/2);

% Pads W2 elements at each edges.
T = [interp1((1:2)',T(1:2,:)      ,repmat((-W2+1:0)' ,1,n),[],'extrap'); T;
     interp1((1:2)',T(m-1:m,:),repmat((0:W2-1)'+3,1,n),[],'extrap')];
if ischar(PAD)
 switch PAD(1)
  case 'r'
   % 'replicates'
   X = [repmat(X(1,:),W2,1); X; repmat(X(m,:),W2,1)];
  case 'c'
   % 'circular'
   X = [X(mod((m-W2+1:m)-1,m)+1,:); X; X(mod((1:W2)-1,m)+1,:)];
  case 's'
   % 'symmetric'
   temp = [1:m m-1:1];
   X = [X(temp(mod((W2:-1:     1)-1,2*m)+1),:); X; ...
        X(temp(mod((m :-1:m-W2+1)-1,2*m)+1),:)];
   clear temp
  case 'm'
   % 'mean'
   X = [repmat(nanmean(X,1),W2,1); X; repmat(nanmean(X,1),W2,1)];
 end
elseif isa(PAD,'function_handle')
 try
  X = [PAD(T(W2+(1:m),:),X,T(      1:W2 ,:)); X; ...
       PAD(T(W2+(1:m),:),X,T(W2+m+(1:W2),:))];
 catch
  warning('CVARGAS:der:errorDuringPadFunctionEvaluation',...
   'An error ocurred during "%s" evaluation. Returned NaNs.',func2str(PAD))
  DXDT = NaN(Xsize);
  if nargout>1, DXDTstd = DXDT; end
  return
 end
else
 % An scalar
 X = [repmat(PAD,W2,n); X; repmat(PAD,W2,n)];
end

% Gets derivatives..
DXDT = NaN(m,n);
inan = isnan(X);
if nargout==1
 % Do not estimate slope error.
 for col = 1:n
  for row = 1:m
   r   = (-W2(col):W2(col))' + row + W2;
   bad = inan(r,col);
   if sum(~bad)>DEG
    slope = polyfit(T(r(~bad),col)-T(row+W2,col),X(r(~bad),col),DEG);
    DXDT(row,col) = slope(DEG);
   end
  end
 end
else
 % Estimate slope error.
 DXDTstd = DXDT;
 for col = 1:n
  for row = 1:m
   r   = (-W2(col):W2(col))' + row + W2;
   bad = inan(r,col);
   if sum(~bad)>DEG
    [slope,E] = polyfit(T(r(~bad),col)-T(row+W2,col),X(r(~bad),col),DEG);
    DXDT(row,col) = slope(DEG);
    E = sqrt(diag(abs((inv(E.R)*inv(E.R')))*E.normr^2/E.df));
    DXDTstd(row,col) = E(DEG);
   end
  end
 end
end
 
% OUTPUTS CHECK-OUT
% -------------------------------------------------------------------------

% Returns to original shape.
DXDT = ipermute(reshape(DXDT,...
 [size(DXDT,1) Xsize([DIM+1:Ndim 1:DIM-1])]),[DIM 1:DIM-1 DIM+1:Ndim]);
if nargout>1
 DXDTstd = ipermute(reshape(DXDTstd,...
 [size(DXDTstd,1) Xsize([DIM+1:Ndim 1:DIM-1])]),[DIM 1:DIM-1 DIM+1:Ndim]);
end

                                     
% [EOF]   der.m 

Contact us at files@mathworks.com