view scripts/general/accumarray.m @ 9141:c1fff751b5a8

Update section 17.1 (Utility Functions) of arith.txi Split section into "Exponents and Logarithms" and "Utility Functions" Use Tex in many more of the doc strings for pretty printing in pdf format.
author Rik <rdrider0-list@yahoo.com>
date Mon, 20 Apr 2009 17:16:09 -0700
parents 1bf0ce0930be
children 9cb0c21e97f7
line wrap: on
line source

## Copyright (C) 2007, 2008, 2009 David Bateman
## Copyright (C) 2009 VZLU Prague
##
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or (at
## your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} {} accumarray (@var{subs}, @var{vals}, @var{sz}, @var{fun}, @var{fillval}, @var{issparse})
## @deftypefnx {Function File} {} accumarray (@var{csubs}, @var{vals}, @dots{})
##
## Create an array by accumulating the elements of a vector into the
## positions defined by their subscripts.  The subscripts are defined by
## the rows of the matrix @var{subs} and the values by @var{vals}.  Each row
## of @var{subs} corresponds to one of the values in @var{vals}.
##
## The size of the matrix will be determined by the subscripts themselves.
## However, if @var{sz} is defined it determines the matrix size.  The length
## of @var{sz} must correspond to the number of columns in @var{subs}.
##
## The default action of @code{accumarray} is to sum the elements with the
## same subscripts.  This behavior can be modified by defining the @var{fun}
## function.  This should be a function or function handle that accepts a 
## column vector and returns a scalar.  The result of the function should not
## depend on the order of the subscripts.
##
## The elements of the returned array that have no subscripts associated with
## them are set to zero.  Defining @var{fillval} to some other value allows
## these values to be defined.
##
## By default @code{accumarray} returns a full matrix.  If @var{issparse} is
## logically true, then a sparse matrix is returned instead.
##
## An example of the use of @code{accumarray} is:
##
## @example
## @group
## accumarray ([1,1,1;2,1,2;2,3,2;2,1,2;2,3,2], 101:105)
## @result{} ans(:,:,1) = [101, 0, 0; 0, 0, 0]
##    ans(:,:,2) = [0, 0, 0; 206, 0, 208]
## @end group
## @end example
## @end deftypefn

function A = accumarray (subs, val, sz, fun, fillval, isspar)  

  if (nargin < 2 || nargin > 6)
    print_usage ();
  endif

  if (iscell (subs))
    subs = cell2mat (cellfun (@(x) x(:), subs, "UniformOutput", false));
  endif
  ndims = size (subs, 2);

  if (nargin < 5 || isempty (fillval))
    fillval = 0;
  endif

  if (nargin < 6 || isempty (isspar))
    isspar = false;
  endif

  if (isspar && ndims > 2)
    error ("accumarray: sparse matrices limited to 2 dimensions");
  endif

  if (nargin < 4 || isempty (fun))
    fun = @sum;
    ## This is the fast summation case. Unlike the general case,
    ## this case will be handled using an O(N) algorithm.

    if (isspar && fillval == 0)
      ## The "sparse" function can handle this case.

      if ((nargin < 3 || isempty (sz)))
        A = sparse (subs(:,1), subs(:,2), val);
      elseif (length (sz) == 2)
        A = sparse (subs(:,1), subs(:,2), val, sz(1), sz(2));
      else
        error ("accumarray: dimensions mismatch")
      endif
    else
      ## This case is handled by an internal function.

      if (ndims > 1)
        if ((nargin < 3 || isempty (sz)))
          sz = max (subs);
        elseif (ndims != length (sz))
          error ("accumarray: dimensions mismatch")
        elseif (any (max (subs) > sz))
          error ("accumarray: index out of range")
        endif

        ## Convert multidimensional subscripts.
        subs = sub2ind (sz, mat2cell (subs, rows (subs), ones (1, ndims)){:});
      elseif (nargin < 3)
        ## In case of linear indexing, the fast built-in accumulator
        ## will determine the extent for us.
        sz = [];
      endif

      ## Call the built-in accumulator.
      if (isempty (sz))
        A = __accumarray_sum__ (subs, val);
      else
        A = __accumarray_sum__ (subs, val, prod (sz));
        ## set proper shape.
        A = reshape (A, sz);
      endif

      ## we fill in nonzero fill value.
      if (fillval != 0)
        mask = true (size (A));
        mask(subs) = false;
        A(mask) = fillval;
      endif
    endif

    return
  endif

  if (nargin < 3 || isempty (sz))
    sz = max (subs);
    if (isscalar(sz))
      sz = [sz, 1];
    endif
  elseif (length (sz) != ndims
	  && (ndims != 1 || length (sz) != 2 || sz(2) != 1))
    error ("accumarray: inconsistent dimensions");
  endif
  
  [subs, idx] = sortrows (subs);

  if (isscalar (val))
    val = val * ones (size (idx));
  else
    val = val(idx);
  endif
  cidx = find ([true; (sum (abs (diff (subs)), 2) != 0)]);
  idx = cell (1, ndims);
  for i = 1:ndims
    idx{i} = subs (cidx, i);
  endfor
  x = cellfun (fun, mat2cell (val(:), diff ([cidx; length(val) + 1])));
  if (isspar && fillval == 0)
    A = sparse (idx{1}, idx{2}, x, sz(1), sz(2));
  else
    if (iscell (x))
      ## Why did matlab choose to reverse the order of the elements
      x = cellfun (@(x) flipud (x(:)), x, "UniformOutput", false);
      A = cell (sz);
    elseif (fillval == 0)
      A = zeros (sz, class (x));
    else 
      A = fillval .* ones (sz);
    endif
    A(sub2ind (sz, idx{:})) = x;
  endif
endfunction

%!error (accumarray (1:5))
%!error (accumarray ([1,2,3],1:2))
%!assert (accumarray ([1;2;4;2;4],101:105), [101;206;0;208])
%!assert (accumarray ([1,1,1;2,1,2;2,3,2;2,1,2;2,3,2],101:105),cat(3, [101,0,0;0,0,0],[0,0,0;206,0,208]))
%!assert (accumarray ([1,1,1;2,1,2;2,3,2;2,1,2;2,3,2],101:105,[],@(x)sin(sum(x))),sin(cat(3, [101,0,0;0,0,0],[0,0,0;206,0,208])))
%!assert (accumarray ({[1 3 3 2 3 1 2 2 3 3 1 2],[3 4 2 1 4 3 4 2 2 4 3 4],[1 1 2 2 1 1 2 1 1 1 2 2]},101:112),cat(3,[0,0,207,0;0,108,0,0;0,109,0,317],[0,0,111,0;104,0,0,219;0,103,0,0]))
%!assert (accumarray ([1,1;2,1;2,3;2,1;2,3],101:105,[2,4],@max,NaN),[101,NaN,NaN,NaN;104,NaN,105,NaN])
%!assert (accumarray ([1 1; 2 1; 2 3; 2 1; 2 3],101:105,[2 4],@prod,0,true),sparse([1,2,2],[1,1,3],[101,10608,10815],2,4))
%!assert (accumarray ([1 1; 2 1; 2 3; 2 1; 2 3],1,[2,4]), [1,0,0,0;2,0,2,0])
%!assert (accumarray ([1 1; 2 1; 2 3; 2 1; 2 3],101:105,[2,4],@(x)length(x)>1),[false,false,false,false;true,false,true,false])
%!test
%! A = accumarray ([1 1; 2 1; 2 3; 2 1; 2 3],101:105,[2,4],@(x){x});
%! assert (A{2},[104;102])