changeset 26250:481708dbc540

movfun.m: Implement <numeric scalar> as value for "Endpoints" property for Matlab compatibility. * movfun.m: Write more documentation for "fill" value for "Endpoints" property. Delete documentation for "zero" value. Add documentation for <user_value> as a value for "Endpoints" property. Remove "zero" from list valid_bc of possible bc functions. Change InputParser validation function for Endpoints to also accept a numeric scalar in addition to valid_bc. Decode "Endpoints" property and if it is numeric then initialize the replacement value in replaceval_bc function. Replace "zero" in BIST tests with numeric value 0. * movfun.m (replaceval_bc): New subfunction that replaces values in window outside the original data with a specified value.
author Rik <rik@octave.org>
date Sun, 16 Dec 2018 23:20:48 -0800
parents 78c4aadfbfd9
children 05ec27d632da
files scripts/signal/movfun.m
diffstat 1 files changed, 53 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/signal/movfun.m	Sun Dec 16 22:24:10 2018 -0800
+++ b/scripts/signal/movfun.m	Sun Dec 16 23:20:48 2018 -0800
@@ -31,7 +31,7 @@
 ## symmetric and includes @code{(@var{wlen} - 1) / 2} elements on either side
 ## of the central element.  For example, when calculating the output at
 ## index 5 with a window length of 3, @code{movfun} uses data elements
-## @code{[4, 5, 6].  If @var{wlen} is an even number, the window is asymmetric
+## @code{[4, 5, 6]}.  If @var{wlen} is an even number, the window is asymmetric
 ## and has @code{@var{wlen}/2} elements to the left of the central element
 ## and @code{@var{wlen}/2 - 1} elements to the right of the central element.
 ## For example, when calculating the output at index 5 with a window length of
@@ -94,19 +94,27 @@
 ## example, with a window of length 3,
 ## @code{@var{y}(1) = @var{fcn} ([@var{x}(end-1:end), @var{x}(1)])},
 ##
-## @item @qcode{"zero"}
-## The array is pre-padded and post-padded with zeros to exactly contain the
-## window.  For example, with a window of length 3,
-## @code{@var{y}(1) = @var{fcn} ([0, @var{x}(1:2)])}, and
-## @code{@var{y}(end) = @var{fcn} ([@var{x}(end-1:end), 0])}.
+## @item @qcode{"fill"}
+## Any window elements outside the data array are replaced by @code{NaN}.  For
+## example, with a window of length 3,
+## @code{@var{y}(1) = @var{fcn} ([NaN, @var{x}(1:2)])}, and
+## @code{@var{y}(end) = @var{fcn} ([@var{x}(end-1:end), NaN])}.
+## This option usually results in @var{y} having @code{NaN} values at the
+## boundaries, although it is influenced by how @var{fcn} handles @code{NaN},
+## and also by the property @qcode{"nancond"}.
+##
+## @item @var{user_value}
+## Any window elements outside the data array are replaced by the specified
+## value @var{user_value} which must be a numeric scalar.  For example, with a
+## window of length 3,
+## @code{@var{y}(1) = @var{fcn} ([@var{user_value}, @var{x}(1:2)])}, and
+## @code{@var{y}(end) = @var{fcn} ([@var{x}(end-1:end), @var{user_value}])}.
+## A common choice for @var{user_value} is 0.
 ##
 ## @item @qcode{"same"}
 ## The resulting array @var{y} has the same values as @var{x} at the
 ## boundaries.
 ##
-## @item @qcode{"fill"}
-## The resulting array @var{y} has @code{NaN} at the boundaries.
-##
 ## @end table
 ##
 ## Note that for some of these values, the window size at the boundaries is not
@@ -151,7 +159,7 @@
     print_usage ();
   endif
 
-  valid_bc = {"shrink", "periodic", "same", "zero", "fill"};
+  valid_bc = {"shrink", "periodic", "same", "fill"};
 
   persistent dispatch;
   if (isempty (dispatch))
@@ -166,7 +174,7 @@
   parser = inputParser ();
   parser.FunctionName = "movfun";
   parser.addParamValue ("Endpoints", "shrink", ...
-    @(x) any (strcmpi (x, valid_bc)));
+    @(x) any (strcmpi (x, valid_bc)) || (isscalar (x) && isnumeric (x)));
   parser.addParamValue ("dim", [], ...
     @(d) isempty (d) || (isscalar (d) && isindex (d, ndims (x))));
   parser.addParamValue ("nancond", "omitnan", ...
@@ -207,7 +215,7 @@
 
   ## Check that array is longer than WLEN at dimension DIM.  At least one full
   ## window must fit.  Function max is used to include the case when WLEN is an
-  ## array. 
+  ## array.
   ## FIXME: Consider using bc to decide what to do here.
   if (max (wlen) > szx(dim))
       error ("Octave:invalid-input-arg", ...
@@ -224,7 +232,12 @@
   x     = reshape (x, N, ncols);         # reshape input
 
   ## Obtain function for boundary conditions
-  bcfunc = dispatch.(tolower (bc));
+  if (isnumeric (bc))
+    bcfunc = @replaceval_bc;
+    bcfunc (true, bc);  # initialize replaceval function with value
+  else
+    bcfunc = dispatch.(tolower (bc));
+  endif
 
   ## Obtain slicer
   [slc, C, Cpre, Cpos, win] = movslice (N, wlen);
@@ -322,24 +335,39 @@
   y            = fcn (x(idx));
 endfunction
 
-## Apply "zero" boundary conditions
-## Window is padded at beginning and end with zeros
-function y = zero_bc (fcn, x, idxp, win, wlen)
+## Apply replacement value boundary conditions
+## Window is padded at beginning and end with user-specified value.
+function y = replaceval_bc (fcn, x, idxp, win, wlen)
+
+  persistent substitute;
+
+  ## In-band method to initialize substitute value
+  if (islogical (fcn))
+    substitute = x;
+    return;
+  endif
+
+  ## Pad beginning and end of window with specified value.
   if (isscalar (wlen))
     wlen = [wlen, wlen];
   endif
   N = length (x);
   if (min (idxp) == 1)
-    x = prepad (x, N + wlen(1));
+    x = prepad (x, N + wlen(1), substitute);
     idx = idxp + win + wlen(1);
   elseif (max (idxp) == N)
-    x   = postpad (x, N + wlen(2));
+    x   = postpad (x, N + wlen(2), substitute);
     idx = idxp + win;
   endif
+
   y = fcn (x(idx));
+
 endfunction
 
 ## Apply "fill" boundary conditions
+## FIXME: This is incorrect.  This directly changes the output when it
+## must only change the values that @fcn considers.  Some functions do not
+## return NaN when there are NaN inputs such as "min (NaN, 5)".
 ## Window is padded at beginning and end with NaN
 function y = fill_bc (fcn, x, idxp, win, wlen, odim)
   y = NaN (length (idxp), odim);
@@ -354,7 +382,7 @@
 %! x_s = movfun (@mean, xn, 5, "Endpoints", "shrink");
 %! x_p = movfun (@mean, xn, 5, "Endpoints", "periodic");
 %! x_m = movfun (@mean, xn, 5, "Endpoints", "same");
-%! x_z = movfun (@mean, xn, 5, "Endpoints", "zero");
+%! x_z = movfun (@mean, xn, 5, "Endpoints", 0);
 %! x_f = movfun (@mean, xn, 5, "Endpoints", "fill");
 %!
 %! h = plot (t, xn, "o;noisy signal;",
@@ -499,7 +527,7 @@
 %!test
 %! x = (1:10).' + [-3, 0, 4];
 %! ctrfun = @(x) x(2,:);
-%! valid_bc = {"same", "periodic", "zero"};
+%! valid_bc = {"same", "periodic", 0};
 %! for bc = valid_bc
 %!   assert (movfun (ctrfun, x, 3, "Endpoints", bc{1}), x);
 %! endfor
@@ -512,7 +540,7 @@
 %! ## dim == 2, same as transpose
 %! x = randi (10, 3);
 %! ctrfun = @(x) x(2,:);
-%! valid_bc = {"same", "periodic", "zero"};
+%! valid_bc = {"same", "periodic", 0};
 %! for bc = valid_bc
 %!   assert (movfun (ctrfun, x.', 3, "Endpoints", bc{1}, "dim", 2), x.');
 %! endfor
@@ -530,7 +558,7 @@
 %! ## bad zero_bc
 %! x = ones (10, 1);
 %! y = x; y(1:2) = y([end end-1]) = [0.6;0.8];
-%! assert (movfun (@mean, x, 5, "Endpoints", "zero"), y);
+%! assert (movfun (@mean, x, 5, "Endpoints", 0), y);
 
 ## Asymmetric windows
 %!shared x,wlen,wlen0b,wlen0f,ctrfun,xd,UNO,UNOd0b,UNOd0f
@@ -548,7 +576,7 @@
 %!assert (movfun (ctrfun, x, wlen, "Endpoints", "same"), x)
 %!assert (movfun (ctrfun, x, wlen, "Endpoints", "fill"), xd)
 %!assert (movfun (ctrfun, x, wlen, "Endpoints", "periodic"), x)
-%!assert (movfun (ctrfun, x, wlen, "Endpoints", "zero"), x)
+%!assert (movfun (ctrfun, x, wlen, "Endpoints", 0), x)
 ## for shorter x, indexing fails
 %!error movfun (ctrfun, x, wlen, "Endpoints", "shrink")
 
@@ -565,8 +593,8 @@
 %!assert (movfun (@min, UNO, wlen0b, "Endpoints", "periodic"), UNO)
 %!assert (movfun (@min, UNO, wlen0f, "Endpoints", "periodic"), UNO)
 
-%!assert (movfun (@max, UNO, wlen0b, "Endpoints", "zero"), UNO)
-%!assert (movfun (@max, UNO, wlen0f, "Endpoints", "zero"), UNO)
+%!assert (movfun (@max, UNO, wlen0b, "Endpoints", 0), UNO)
+%!assert (movfun (@max, UNO, wlen0f, "Endpoints", 0), UNO)
 
 ## Multidimensional output
 %!assert(size(movfun (@(x)[min(x) max(x)], (1:10).', 3)), [10 2])