1 ## Copyright (C) 2011 Nir Krakauer <nkrakauer@ccny.cuny.edu>
2 ## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
4 ## This program is free software; you can redistribute it and/or modify it under
5 ## the terms of the GNU General Public License as published by the Free Software
6 ## Foundation; either version 3 of the License, or (at your option) any later
9 ## This program is distributed in the hope that it will be useful, but WITHOUT
10 ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 ## You should have received a copy of the GNU General Public License along with
15 ## this program; if not, see <http://www.gnu.org/licenses/>.
18 ## @deftypefn {Function File} {@var{yy} =} monotone_smooth (@var{x}, @var{y}, @var{h})
19 ## Produce a smooth monotone increasing approximation to a sampled functional
20 ## dependence y(x) using a kernel method (an Epanechnikov smoothing kernel is
21 ## applied to y(x); this is integrated to yield the monotone increasing form.
22 ## See Reference 1 for details.)
24 ## @subheading Arguments
28 ## @var{x} is a vector of values of the independent variable.
31 ## @var{y} is a vector of values of the dependent variable, of the same size as
32 ## @var{x}. For best performance, it is recommended that the @var{y} already be
33 ## fairly smooth, e.g. by applying a kernel smoothing to the original values if
37 ## @var{h} is the kernel bandwidth to use. If @var{h} is not given, a "reasonable"
42 ## @subheading Return values
46 ## @var{yy} is the vector of smooth monotone increasing function values at @var{x}.
50 ## @subheading Examples
55 ## y = (x .^ 2) + 3 * randn(size(x)); %typically non-monotonic from the added noise
56 ## ys = ([y(1) y(1:(end-1))] + y + [y(2:end) y(end)])/3; %crudely smoothed via
57 ## moving average, but still typically non-monotonic
58 ## yy = monotone_smooth(x, ys); %yy is monotone increasing in x
59 ## plot(x, y, '+', x, ys, x, yy)
63 ## @subheading References
67 ## Holger Dette, Natalie Neumeyer and Kay F. Pilz (2006), A simple nonparametric
68 ## estimator of a strictly monotone regression function, @cite{Bernoulli}, 12:469-490
70 ## Regine Scheder (2007), R Package 'monoProc', Version 1.0-6,
71 ## @url{http://cran.r-project.org/web/packages/monoProc/monoProc.pdf} (The
72 ## implementation here is based on the monoProc function mono.1d)
76 ## Author: Nir Krakauer <nkrakauer@ccny.cuny.edu>
77 ## Description: Nonparametric monotone increasing regression
79 function yy = monotone_smooth (x, y, h)
81 if (nargin < 2 || nargin > 3)
83 elseif (!isnumeric (x) || !isvector (x))
84 error ("first argument x must be a numeric vector")
85 elseif (!isnumeric (y) || !isvector (y))
86 error ("second argument y must be a numeric vector")
87 elseif (numel (x) != numel (y))
88 error ("x and y must have the same number of elements")
89 elseif (nargin == 3 && (!isscalar (h) || !isnumeric (h)))
90 error ("third argument 'h' (kernel bandwith) must a numeric scalar")
95 %set filter bandwidth at a reasonable default value, if not specified
107 %transform range of x to [0, 1]
108 xl = (x - x_min) / (x_max - x_min);
112 %Epanechnikov smoothing kernel (with finite support)
113 %K_epanech_kernel = @(z) (3/4) * ((1 - z).^2) .* (abs(z) < 1);
115 K_epanech_int = @(z) mean(((abs(z) < 1)/2) - (3/4) * (z .* (abs(z) < 1) - (1/3) * (z.^3) .* (abs(z) < 1)) + (z < -1));
117 %integral of kernels up to t
118 monotone_inverse = @(t) K_epanech_int((y - t) / h);
120 %find the value of the monotone smooth function at each point in x
121 niter_max = 150; %maximum number of iterations for estimating each value (should not be reached in most cases)
126 wmin = monotone_inverse(tmin);
127 wmax = monotone_inverse(tmax);
132 iter_max_reached = 1;
134 wt_scaled = (wt - wmin) / (wmax - wmin);
135 tn = tmin + wt_scaled * (tmax - tmin) ;
136 wn = monotone_inverse(tn);
137 wn_scaled = (wn - wmin) / (wmax - wmin);
139 %if (abs(wt-wn) < 1E-4) || (tn < (y_min-0.1)) || (tn > (y_max+0.1))
140 %% criterion for break in the R code -- replaced by the following line to
141 %% hopefully be less dependent on the scale of y
142 if (abs(wt_scaled-wn_scaled) < 1E-4) || (wt_scaled < -0.1) || (wt_scaled > 1.1)
143 iter_max_reached = 0;
155 warning("at x = %g, maximum number of iterations %d reached without convergence; approximation may not be optimal", x(l), niter_max)
157 yy(l) = tmin + (wt - wmin) * (tmax - tmin) / (wmax - wmin);