Loren on the Art of MATLAB

All about the Colon Operator 19

Posted by Loren Shure,

I have written several blog articles that cover some aspects of the : operator. Based on some recent posts to the MATLAB newsgroup, it seems worthwhile for me to compile the information.

Contents

Uses of :

There are several ways that you can use the : operator in MATLAB, and they serve different purposes. Most, but not all of them, have to do with indexing into an array.

  • Creating a list of numbers
  • Collapsing trailing dimensions (right- or left-hand side)
  • Creating a column vector (right-hand side behavior related to reshape)
  • Retaining an array shape during assignment (left-hand side behavior)
  • Working with all the entries in specified dimensions

Creating a List of Numbers

You can use the : operator to create a vector of evenly-spaced numbers. Here are the integers from -3 to 3.

list1 = -3:3
list1 =
    -3    -2    -1     0     1     2     3

Here are the first few odd positive integers.

list2 = 1:2:10
list2 =
     1     3     5     7     9

Here's how to divide the interval between 0 and pi into equally spaced samples.

nsamp = 5;
sliceOfPi = (0:1/(nsamp-1):1)*pi
sliceOfPi =
         0    0.7854    1.5708    2.3562    3.1416

Note that even though only a few digits of each value in sliceOfPi are printed out, they are double precision numbers. To see more digits, check out the function format.

Collapsing Trailing Dimensions

I have a 4-dimensional array and would like to find the sum of all the elements that are in the final row and column.

b = rand(3,2,6,4);
b32sum = sum(b(3,2,:))
b32sum =
   11.4622

If instead, I sum using all the dimensions, in this case, I get a 1x1x1x4 array instead of scalar output. If I sum these output values, I get my overall sum.

b32sumAll = sum(b(3,2,:,:))
overall = sum(b32sumAll)
b32sumAll(:,:,1,1) =
    2.8279
b32sumAll(:,:,1,2) =
    3.4177
b32sumAll(:,:,1,3) =
    1.9352
b32sumAll(:,:,1,4) =
    3.2815
overall =
   11.4622

Creating a Column Vector

Here's the size of the array b32sumAll.

size(b32sumAll)
ans =
     1     1     1     4

If I want to transform this into a column vector, I can use reshape, permute or : on the right-hand side.

b32vecR = reshape(b32sumAll,[],1);
b32vecP = permute(b32sumAll, [4 1:3]);
b32vec = b32sumAll(:)
allthesame = isequal(b32vec, b32vecP, b32vecR)
b32vec =
    2.8279
    3.4177
    1.9352
    3.2815
allthesame =
     1

Retaining Array Shape During Assignment

Now let me pour some new values into b32sumAll, the array that looks almost like a vector.

b32sumAll(:) = [1 2; 3 5]
b32sumAll(:,:,1,1) =
     1
b32sumAll(:,:,1,2) =
     3
b32sumAll(:,:,1,3) =
     2
b32sumAll(:,:,1,4) =
     5

Notice that I only have to have the same number of elements on both the left- and right-hand sides. The values are poured in from the right-hand side ordered as if that array had been turned into a column vector. Note that a scalar for the right-hand side is also acceptable and then MATLAB performs a scalar expansion to fill the left-hand side.

Working with All the Entries in Specified Dimensions

If I want to manipulate values in some specific dimensions, I can also use the : operator to specify the dimensions I'd like to leave alone. For example, suppose I want to perform a left shift on the values in the second dimension of my 3-D array. Let me first create an array for illustration.

a3 = zeros(2,3,2);
a3(:) = 1:numel(a3)
a3(:,:,1) =
     1     3     5
     2     4     6
a3(:,:,2) =
     7     9    11
     8    10    12

Now let's shift the column values all over to the left, and have the right-most one become the last column. Note that columns are dimension 2. Here's a way to do this.

a3r1 = a3(:,[2:size(a3,2) 1],:)
a3r1(:,:,1) =
     3     5     1
     4     6     2
a3r1(:,:,2) =
     9    11     7
    10    12     8

How do I do this if the array could be any number of dimensions, not just 3? I can't index with :, because I don't know how many : to use, since I don't knwo the dimension. In this case, use the string value ':', create the indices in a cell array, and use the notion of the comma-separated list to perform the actual indexing. For a3, here's what this looks like.

indices = {':', [2:size(a3,2) 1],':'}
a3rnew = a3(indices{:})
sameshifts = isequal(a3r1, a3rnew)
indices = 
    ':'    [1x3 double]    ':'
a3rnew(:,:,1) =
     3     5     1
     4     6     2
a3rnew(:,:,2) =
     9    11     7
    10    12     8
sameshifts =
     1

Now, more generally, when the number of dimensions is not known, I simply create a cell array of all ':' string values, replace the specific dimensions I care about with some other indexing expression, and then I can use the index cell array to do the operation.

a5 = zeros(2,5,2,3,4);
a5(:) = 1:numel(a5);
indices = repmat({':'}, 1, ndims(a5));
myshift = [2:size(a5,2) 1]
indices{2} = myshift
a5new = a5(indices{:});
myshift =
     2     3     4     5     1
indices = 
    ':'    [1x5 double]    ':'    ':'    ':'

Let's compare two "rows" and see that the values have shifted left by 1 column.

a5(1,:,2,2,3)
a5new(1,:,2,2,3)
ans =
   151   153   155   157   159
ans =
   153   155   157   159   151

Related Reference Material

For more in depth understanding of the various ways in which the : operator can be used, look in the MATLAB documention

and in related blog articles

: is Complicated

The : operator behaves differently in MATLAB depending on its usage. This can lead to confusion. Have I missed anything major on this topic? Let me know.


Get the MATLAB code

Published with MATLAB® 7.3

19 CommentsOldest to Newest

It might be nice to provide some information about how : is ordered with respect to other math operations. i.e. 1:N+1 vs (1:N)+1. This doesn’t appear to be covered in the documentation.

Mark-

Good question. Precedence is documented here.

And here’s the list for good measure:

Parentheses ()

Transpose (.’), power (.^), complex conjugate transpose (‘), matrix power (^)

Unary plus (+), unary minus (-), logical negation (~)

Multiplication (.*), right division (./), left division (.\), matrix multiplication (*), matrix right division (/), matrix left division (\)

Addition (+), subtraction (-)

Colon operator (:)

Less than (< ), less than or equal to (<=), greater than (>), greater than or equal to (>=), equal to (==), not equal to (~=)

Element-wise AND (&)

Element-wise OR (|)

Short-circuit AND (&&)

Short-circuit OR (||)

Loren,

why in the reply
b32sumAll(:,:,1,1) =
2.8279
the first two singleton dimensions are represented by “:” and the third one by “1″? Is there any real difference between the first two dimensions and the third?

Brook-

b32sumAll is a 1x1x1x4 array and b32sumAll(:,:,1,1) is then the value of the first element in that array. Since it’s leading dimensions are 1s, you are right that I could have achieved the same result with b32sumAll(1,1,1,1).

–Loren

My interest is the use of the colon operator in for loops.

for i = 1:100
disp(i)
end

I assume that the array is not pre-generated and then iterated through. In this case 1:100 does not define a range but an iterator object. This then begs the question of the ability of user defined iterator objects.

for i = fib(10)
…disp(i)
end

where fib(10) does not pre-generate the list of numbers but generates the next on demand. Addition of the python/ruby yield keyword would do the trick

function fib( N )
…a = 0
…b = 1
…for i:N
…..t = a + b
…..a = b
…..b = t
…..yield t
…end
end

Using python like philosophy this would be equivalent to

function fib( N )
…a = 0
…b = 1
…t = 0
…i = 0
…function next()
…..if i == N
…….throw “finished”
…..end
…..t = a + b
…..a = b
…..b = t
…..return t
…end
…return @next
end

As you can see the yield keyword simplifies a common nested function paradigm and makes writing iterators that behave like the colon operator much easier.

hi all:

my post follows the theme raised by the previous post…

for now, withdin a for loop we can change the value of the index and still use it to do calculations. when the cicle iterations are over, the index returns to its original value and gets incremented (or decremented) before the first code line of the next iteration of the for loop.

this as the ability to make for loops much robust to errors. on the other hand, it leads to a great disadvantage of flexibility, i.e. if we have to jump a certain iteration (if , continue; end) based on a value calculated on the previous we can’t do it has suggested by brad. we have to insert a if condition leading to a slower code….

The problem I keep running into is where I use a function to generate a matrix that I wish to take a subsample from. While it’s straightforward to use the colon notation to subsample a stored matrix, it’s not possible to use the same method for a matrix created on the fly.

#########

Example
——-

% V is a 3157 element vector, i.e. size(V) = 3157 3

A = reshape(R, 41, 77);
B = A(26:41, 52:77);

% But this creates the unnecessary matrix “A”.
%
% What would be more efficient, is the more direct:-

B = [reshape(R, 41, 77)](26:41, 52:77);

% …or similar syntax, but this type of scenario is never
% covered in the numerous help pages devoted to colon “:”
% notation.

########

I have a dim memory that at one point I did find a half way
elegant way to do this (I’ve had a hiatus in my matlab
usage and am in the process of relearning…not quite the
same as bike riding). But considering how commonly I’m
trying to do this type of subsampling operation (and by
extension, I guess other people are too), it seems
surprising that I can find no easy reference to such
operations in the literature (couple of hours checking
reference materials, googling, etc…).

Of course, it wouldn’t be the first time that I’ve missed
the obvious, so feel free to berate me if this is explained
in bold type on page 1 of the ‘Introduction to Matlab’.

Regarding the above question about the precedence of the colon operator:

Loren, do you remember something Damian said way back when I was running around challenging colon’s precedence?

“All in all, I think it’d be better to keep a tight colon.”

Or something to that effect.

Glad I could drive by and lower the signal-to-noise ratio :)

–JMike

Tony-

There is no direct syntax to do what you are asking:

first = eig(magic(3))(1)

but it is in our enhancement database.

You could sort of do this via an anonymous function. Here’s the idea:

f = @(x,ind) x(ind);
first = f(magic(3),1)

–Loren

It’s easy to create a vector of numbers using a colon operator, but do you know how to create a sequence of numbers using only a loop and no colons?

Tim-

You could use a while loop and and index that I bump by some increment.

maxind = 10;
nums = zeros(1,maxind);
ind = 0;
while ind < maxind
   ind = ind+1;
   nums(ind) = ind;
end

and obviously you can get more elaborate with starting, ending values and increment not being 1.

BUT, is there a reason to not use : ?

--Loren

Hi Loren,

greetings..

I need to subtarct 2 matrices..


a =

     1     2     3
     4     5     6

and

mu =

     2
     5

how can i do in in 1 line..( i.e i do not want to use loops and subtract row by row..i.e subtarct each element of a row wise by element in mu)

i tried using ” a. – mu ” ,but it doesnt work ( not surprisingly!)

i answer i expect is

-1 0 1
-1 0 1

Could u pls reply?

Thanks and Regards,
Kishore.

I deal with a lot of large arrays and matrices with Matlab. Sometimes I want to use all the elements from some offset onwards or all of the elements up to a point. At the moment my code looks something like this:

array = ones(10,1);
%assume array length is unknown
length_of_array = length(array);
offset = 5;
second_array = array(offset:length_of_array);

Is there a better solution? I would have thought it would look something like this – but it comes up with an error:

array = ones(10,1);
offset = 5;
second_array = array(offset:);

Any help?

Maybe there is a way of referring to the last element of an array without actually knowing how long the array is. Something like:

second_array = array(offset:'last')

Olbert-

The term end is used for the last element. So you can write:

array = ones(10,1);
%assume array length is unknown
offset = 5;
second_array = array(offset:end);

It’s been available in MATLAB since version 5, I believe.
–Loren

[0:0.1:(5-11*eps)]

include 5 but

[0:0.1:(5-10*eps)]

not include 5
the number for change here is a mystery!

[0:0.1:(5-eps)]

must not include 5 floting point number, because eps is machine epsilon and 5 != (5-eps) is true
Very annoying.

Using two colons to create a vector with arbitrary real increments between the elements,
E = 0:.1:.5
results in
E =
0 0.1000 0.2000 0.3000 0.4000 0.5000
This primer in help documentation about : operator must comment on this.
version 7 Matlab

These postings are the author's and don't necessarily represent the opinions of MathWorks.