7

What is the best way to construct a matrix whose elements are exactly their indices in Matlab?


EDIT: The existing answers to this question is applicable to how to construct a matrix whose elements are functions of their indices. So I added that to the question title.


The format can be either a matrix with vectors as elements, or as two matrices each storing one index.

At the end, I would like to create a matrix whose elements are functions of their indices. So an efficient method for that (but possibly different) is very much appreciated. Any comment on efficiency is welcomed.

The size of matrices tends to be large (dimension of hundreds squared at the minimum) for my applications. As a result, methods that take advantage of native Matlab functions are probably much better than for/while loops.

For example, for a matrix of size [2 2], I would like to make either

IND = 
[1 1]  [1 2]
[2 1]  [2 2]

or

X = 
1 1
2 2
Y =
1 2
1 2

At the end, I am hoping to do something like

matrixIneed = arrayfun(@(s)..., IND)

where s is a vector of size 2, or

matrixIneed = arrayfun(@(i,j)..., X,Y)

The latter is preferred.


EDIT: A note about accepted answer.

I have accepted Andrew's answer because it is intuitive to me and the code seems quick (at least to me).

If you ever Google an answer to this question, you are likely concerned about performance like I do. (Otherwise if not for best practice, anyone can think of a double loop to accomplish the task.)

If so, you are encouraged to examine Andrew's comment on the reshape() function and Rody's answer about the performance of meshgrid() and loops.

Nevertheless, thewaywewalk's solution with meshgrid() is a helpful example to learn the meshgrid() function. And it is useful in many other Matlab functions.

Jigg's repmat() solution may help you too.

Argyll
  • 8,591
  • 4
  • 25
  • 46

5 Answers5

7

use meshgrid or ndgrid:

% example matrix
Matrix = magic(5)

[n m] = size(Matrix)
% or define the dimensions directly
n = 5;
m = 5;

[X,Y] = meshgrid(1:n,1:m)     %\\ [Y,X] = ndgrid(1:n,1:m) 

(the difference for your 2D case is that, Y and X are swapped. - use it according to your needs.)

From the documentation:

[X,Y] = meshgrid(xgv,ygv) replicates the grid vectors xgv and ygv to produce a full grid. This grid is represented by the output coordinate arrays X and Y. The output coordinate arrays X and Y contain copies of the grid vectors xgv and ygv respectively. The sizes of the output arrays are determined by the length of the grid vectors. For grid vectors xgv and ygv of length M and N respectively, X and Y will have N rows and M columns.

Well there is not much more to explain, meshgrid is used to create a regular grid from two vectors, usually "x" and "y" values in order to obtain suitable input data for a 3D/color-coded plot of z-data. If you assume your x and y to be the vectors [1 2 3 ... n] it does exactly what you need.

returns:

X =

     1     2     3     4     5
     1     2     3     4     5
     1     2     3     4     5
     1     2     3     4     5
     1     2     3     4     5


Y =

     1     1     1     1     1
     2     2     2     2     2
     3     3     3     3     3
     4     4     4     4     4
     5     5     5     5     5
Robert Seifert
  • 25,078
  • 11
  • 68
  • 113
4

Get used to ndgrid. Avoid meshgrid, except in support of plotting and graphics operations.

The output of ndgrid, expressed in terms of rows, columns, etc., has more natural semenatics for MATLAB matrix operations than x,y coordinates, as returned by meshgrid.

>> nrows = 3;
>> ncols = 4;
>> [II,JJ] = ndgrid(1:nrows,1:ncols)
II =
     1     1     1     1
     2     2     2     2
     3     3     3     3
JJ =
     1     2     3     4
     1     2     3     4
     1     2     3     4

MATLAB's dimension ordering is rows as first dimension, columns as second, then higher dimensions. ndgrid follows this convention with the ordering of its inputs and outputs. For reference, the equivalent meshgrid command is [JJ,II] = meshgrid(1:ncols,1:nrows);.

An excellent illustration of why you should stick to the rows,columns frame of mind is converting to linear indexes: sub2ind. The function sub2ind expects subscripts ordered in the same way as the outputs of ndgrid:

>> inds = sub2ind([nrows ncols],II,JJ)
inds =
     1     4     7    10
     2     5     8    11
     3     6     9    12

Formulating this command requires little thought if you are always thinking in terms of rows,columns (subscripts) rather than x,y.

Again, use ndgrid.

chappjc
  • 30,359
  • 6
  • 75
  • 132
3

Have a look at the ind2sub, sub2ind, and reshape functions. They're useful for transforming subscripts and indexes on multidimensional arrays.

In your case, it looks like you want this. (I think you want "subscripts" instead of "indices". Matlab uses "index" to mean the "linear" index of an element when viewing the array as a one-dimensional vector, and "subscripts" to mean the position along each dimension of a multidimensional array.)

sz = [3 3];  % Size of your matrix
n = prod(sz); % Total number of elements in matrix
[X, Y] = ind2sub(sz, 1:n);  % Subscripts, returned as vectors
X = reshape(X, sz);  % Reshape the subscript vectors to match your matrix
Y = reshape(Y, sz);

The meshgrid approach that @thewaywewalk gave will produce the same output, but I think the ind2sub approach is a bit more readable in this case, since it's worded in terms of array indexing, which is your problem domain. And it will generalize to working on slices or arbitrary subsets of arrays, which meshgrid will not, and corresponds nicely to ind2sub for efficient operations going the other way. (It's worth learning meshgrid anyway though; it pops up in other locations.)

Andrew Janke
  • 23,508
  • 5
  • 56
  • 85
  • Hi Andrew, this is actually my first attempt too! (meshgrid was and still is confusing me..) Do you also happen to know how reshape() performs? – Argyll Mar 17 '14 at 19:50
  • 1
    Reshape is very fast. It does not change the underlying primitive data array at all; it just rewrites the dimensions in the header of the "mxArray" C structure that implements the array in the Matlab interpreter, possibly rewriting the array if it's shared by copy-on-write. That's about as fast as Matlab functions get. – Andrew Janke Mar 18 '14 at 18:49
1

Since you have indicated you want to run a function on the indices, it is by far the easiest and probably fastest to just make a double loop:

matrixIneed = zeros(n,m);
for ii = 1:n
    for jj = 1:m
        matrixIneed(ii,jj) = myFunction(ii,jj);
    end
end

Depending a bit on myFunction, this is likely to be faster than arrayfun, and certainly a lot less memory intensive than meshgrid when n or m are relatively large; see also this related question.

If you're running MATLAB R2009 or later, and your function is a built-in or can be constructed using built-ins only, the execution time of these loops vs. meshgrid will be comparable, but meshgrid will consume much more memory (O(N²) memory as opposed to O(1) for the loop). As always, profiling is key, but the double loop is probably the best all-round solution.

Sometimes, this also helps:

matrixIneed = zeros(n*m,1);
for ij = 1:n*m

    %// use this when possible
    matrixIneed(ij) = myFunction(ij); 

    %// otherwise, use this
    matrixIneed(ij) = myFunction(mod(ij-1,n)+1, floor((ij-1)/n)+1);  
end
reshape(matrixIneed,n,m);
Community
  • 1
  • 1
Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
  • I have been using R2011a. And I have found loops being very slow. For example, with a matrix of a million elements, the built-in `mean()` function is about 50 times faster then a loop, even though a loop saves memory. Likely, `rand()` about 50 times faster when directly producing an array of random numbers compared to a loop. I had a custom 1-parameter function (which is a composition of elementary arithmetic operations) that performs about 50 times faster if the input is a matrix (no `arrayfun` is needed in this case) compared to a loop. – Argyll Mar 23 '14 at 18:03
  • Three 50s add up to make the difference between a simulation being precise enough or not, or a simulation being doable or not. A million was still too small to me back then. So memory does matter. And I had to instead loop over matrices to accomplish the task without crashing my computer. So your answer comes as a surprise to me. Have you not encountered similar problems with Matlab loops? – Argyll Mar 23 '14 at 18:09
0

Another option using repmat (since the content of these matrices is redundant):

X=repmat((1:size(YourMatrix, 2))', 1, size(YourMatrix, 1));
Y=repmat(1:size(YourMatrix, 1), size(YourMatrix, 2), 1);
Cape Code
  • 3,584
  • 3
  • 24
  • 45