Logical Indexing, NaN, Basic Plotting

Contents

Logical Operations

In addition to math operations, Matlab can also do logical comparisons:

2>1
2>7
1==2 %test equality
1==1
7<=9
9<=9
ans =

     1


ans =

     0


ans =

     0


ans =

     1


ans =

     1


ans =

     1

In Matlab, as in most programming languages 1 is true and 0 is false. (Technically, the commands true and false create logical variables, but Matlab is usually smart enough to convert back and forth when you need it to. If you ever get in a pinch, you can use the functions logical to convert numbers to True/False logicals and double to convert from logical to numerical.)

Just as importantly, we can also do operations like AND and OR:

1&1 %AND
1&0
1|0 %OR
0|0 %OR
~1 %NOT
ans =

     1


ans =

     0


ans =

     1


ans =

     0


ans =

     0

And these work just as easily for vectors:

aa=rand(10,1); %create a 10x1 column vector of random numbers between 0 and 1
aa>0.5
bb=aa>0.5; %we can save this output
cc=rand(10,1)>0.5; %cut out the middle man
ee=(bb==cc) %tests equality element-wise (why isn't every entry 1?)
bb|cc %short circuit OR
ans =

     0
     1
     0
     0
     0
     0
     0
     0
     1
     0


ee =

     1
     0
     0
     0
     1
     0
     1
     1
     1
     1


ans =

     0
     1
     1
     1
     0
     1
     0
     0
     1
     0

Working with logicals

Some other tests we might find useful:

any(ee) %test if any of the elements in ee is nonzero
all(ee) %test if all of the element in ee is nonzero
ans =

     1


ans =

     0

When dealing with logicals, we often want to get all the entries of an array where some condition is nonzero. To do this, we use the find command, which takes a logical array as an input and returns all the indices which are true (nonzero):

find(ee)
ans =

     1
     5
     7
     8
     9
    10

In fact, it's worth the time to look up find. It has many more useful options for more complicated problems, and using it well will become important to our programming later on.

Logical Indexing

The most frequent use of logicals for us, however, will be in indexing. Logical indexing will allow us to select and manipulate only those subsets of data that satisfy particular logical conditions.

When we index an array with a logical variable, Matlab is using find to locate all the true elements and convert them to indices.

So for instance, if we create a 5x5 array of random integers from 1 to 50:

A=randi(50,5,5); %look this command up

we could grab a sub-block of it by using indices...

A(1:2,3:4) %remember this?
ans =

    22    24
     8    15

... or we could use indices stored in a variable.

submat=[1 2; 8 16];
A(submat)
ans =

     3     7
    26    24

But we could also use a logical array:

Aupper=A>=25 %true (1) if the entry in A is greater than or equal to 25, 0 otherwise
A(Aupper)
Aupper =

     0     0     0     0     1
     0     0     0     0     0
     0     1     1     0     0
     0     0     1     0     0
     0     1     0     1     1


ans =

    26
    50
    39
    38
    44
    36
    30

Note that Matlab gave us back every entry of A for which Aupper was true, but it did not give us back a result in matrix form. This happend for two reasons:

  1. There is no real way to arrange the returned values back into a matrix, since that matrix would have missing elements, and Matlab doesn't know what to do about that.
  2. Matlab is doing exactly what we asked it to do: return it the values of A at certain indices (given by using find on Aupper). We get a vector back for the same reason that A(1:20) would have returned the first 20 elements in a vector: indexing this way ignores the matrix structure.

But if we only want to use logical indexing to change values inside a matrix, a really nice thing happens:

A(Aupper)=100
A =

     3     2    22    24   100
     7    16     8    15     1
     1   100   100    21     2
    10    20   100    22    12
     4   100     5   100   100

returns a matrix!

Why? Because Matlab is assigning the value 100 to several elements of A (all those for which Aupper is true), but this assignment does not need to change the matrix structure. First (corresponding to the first reason above) because A still has all its elements. Second (as in the second reason) because we're not extracting data, just modifying it in place.

This type of manipulation becomes extremely powerful once you learn to use it well. Very, very often, we want to extract or change only those parts of an array that satisfy certain conditions, and logical indexing will be the method we choose to use. For more examples and elaboration, see the Indexing Cheatsheet

The special values: Inf and NaN

In addition to numbers, Matlab arrays can store a couple of additional special values. One arises through some pathological arithmetic operations:

1/0
-1/0
ans =

   Inf


ans =

  -Inf

This symbol behaves much as you'd think:

Inf + 2
Inf * 3
Inf / -1
ans =

   Inf


ans =

   Inf


ans =

  -Inf

Matlab also has special functions that allow us to test whether a variable or array element is infinite (and thus to find it and perhaps change it):

isinf(1/0)
isfinite(1/0)
ans =

     1


ans =

     0

The other special symbol arises in cases where arithmetic operations not only aren't defined, they can't be extended using Inf:

0/0
Inf/Inf
ans =

   NaN


ans =

   NaN

There's no real predicting how this symbol will act, so here are the rules:

NaN*1
NaN-1
NaN+NaN
NaN+Inf
ans =

   NaN


ans =

   NaN


ans =

   NaN


ans =

   NaN

In other words, you can't "undo" a NaN. On the other hand, |NaN|s have two big advantages:

  1. They serve as placeholders for missing or undefined data
  2. They don't show up on plots

The first property is really important when you may have different numbers of data points in different trials, but you need to store everything in a single array:

B=[5 7 9; 10 36 NaN; NaN NaN 4];

Typically, you'd code missing data as 0 or -1 or 999, but you don't want to use these values in an average. And Matlab has a shortcut for this:

mean(B) %mean down columns
ans =

   NaN   NaN   NaN

Ugh... but

nanmean(B) %works just fine
ans =

    7.5000   21.5000    6.5000

The second property can be illustrated with a simple plot:

t_axis=0:0.01:10; %define a time axis from 0 to 10 with points every 0.01
y=t_axis+2*sin(2*pi*t_axis)+0.5*randn(size(t_axis)); %look up the commands you don't recognize

And we can plot the data in y as a function of t_axis:

plot(t_axis,y)

But maybe we believe some data are corrupted, in which case we can replace them with |NaN|s.

bad=(t_axis>=4)&(t_axis<=5);
y(bad)=NaN;

When we plot the modified version of y the NaN data points don't plot.

plot(t_axis,y)