Loren on the Art of MATLAB

Turn ideas into MATLAB

Making Functions Suitable for ND Arrays

When we changed MATLAB to handle more than just 2-dimensional arrays (MATLAB version 5), we wanted to extend existing M-files to also handle N dimensions, when sensible. We found certain ways to do that well and I plan to show a couple of the techniques here.


Reshape into a 2-Dimensional Array

The formula for this technique is: * Reshape input into a 2-D array * Set up output array * Work on the columns * Reset the output shape if shape wasn't set up earlier

For an example of this technique, let's looks at the function mode and a few lines in particular. You will see similar code in the function trapz. The first thing you see in the code is error checking inputs and validating the dimension to operate on and setting up information to collect the correct output (i.e., how many outputs is the user asking for and should any of them be sparse?). We then reach lines 114:117

dbtype mode 114:117
114   % Convert data to operate along columns of a 2-d array
115   x = permute(x,[dim, (1:dim-1), (dim+1:length(sizex))]);
116   x = reshape(x,[sizex(dim),prod(sizem)]);
117   [nrows,ncols] = size(x);

where we reshape the data into a 2-D matrix. We are now ready to calculate the mode. In this case, we don't need to reshape the output, because it was set up initially to be the right dimensions. And the mode calculation itself places the output value into the array using linear indexing. Linear indexing does not alter the shape of the array being operated on, so the output shape, which was preset, is preserved in this case.

Use a List of N-Dimensional Indices

Instead of reshaping the array, another technique is to perform N-dimensional indexing. Here I'll show some of the code in the function fftshift where we want to work on all the elements unchanged in all dimensions except one. And in that dimension, we do something specific. Let's look at some of the code:

dbtype fftshift 23:31
23    if nargin > 1
24        if (~isscalar(dim)) || floor(dim) ~= dim || dim < 1
25            error('MATLAB:fftshift:DimNotPosInt', 'DIM must be a positive integer.')
26        end
27        idx = repmat({':'}, 1, max(ndims(x),dim));
28        m = size(x, dim);
29        p = ceil(m/2);
30        idx{dim} = [p+1:m 1:p];
31    else

First we find out what dimension is of interest. We then create a cell array , to be used later for indexing, and we fill it with ':' through the maximum of the dimension of interest and the dimension of the input array (in this case, to allow for possible extra dimensions with size 1). We now have a cell array filled with ':' and we replace the single entry for the dimension of interest with some other values, in this case, we swap the first half of the existing values in 1:m with the second half.

Suppose A is 4-dimensional where the 3rd dimension is the one of interest and is length 4. Our cell array, indices, would now look like this:

swapind = [3:4 1:2]
indices = {':',':',swapind,':'}
swapind =

     3     4     1     2

indices = 

    ':'    ':'    [1x4 double]    ':'

And now we are ready to rearrange the contents of A, but taking advantage of ability to turn a cell array into comma-separated list.

A = rand(2,1,4,2);
B = A(indices{:});

You might want to compare A and B here. I'll just compare the first page of A and the relevant page of B.

[A(:,:,1,2)  B(:,:,3,2)]
ans =

    0.8983    0.8983
    0.7546    0.7546

Two Techniques for Handling N-D Arrays

I've illustrated two techniques for handling N-dimensional arrays. * Reshape arrays into 2-D arrays and reshape the output appropriately * Use indexing, perhaps with ':', and comma-separated lists to get the desired results.

You can also work some indexing magic using anonymous functions. Do you know of other techniques? If so, add them here.



To leave a comment, please click here to sign in to your MathWorks Account or create a new one.