[MUSIC] We've seen the power and flexibility of loops. Now, we're going to see another powerful feature, one that's unique to MATLAB. It's called logical indexing. Let's start with a problem and its traditional solution. Given a vector, v, of scalars, create a second vector, w, that contains only non-negative elements of v. Doesn't sound too complicated. In fact, though, there are a couple of subtle things in this problem that you might not get right the first time you try solve it. Here's a solution. Let's go through it line by line. First, we initialize w to be the empty array. We've got to do this because that's the answer we need if there are no non-negative elements in v at all. We don't initialize w, then for the no non-negative elements case w will end up being undefined at the end of our program. In the second line, we introduce a new variable called jj and set it to 0. Jj is necessary because, we'll need to add elements to w one by one. And we'll use jj to specify the position in w at which to add them. It can't be the same index that we used for v because they won't remain in sync as we copy some elements of v, but not others. So we've got to have two different indices for the two vectors. And we got a simple for loop, whose loop index ii can be used to index into v. We need to hit every element of v, so ii goes from 1 to the length of v. At each iteration of the loop, we need to check whether the current element of v is non-negative. In other words, whether it is greater than or equal to 0. We do that with an if statement. If v(ii) is non-negative, we need to insert it into w. We do that by first incrementing jj by 1. Remember, it started at 0. And we need to do that every time there's a new element to add. Then, we copy the number into w at the correct position. W(jj) equals v(ii). Again, notice how we use one index for w, and another one for v. And that's it. The remaining two lines are just the end keywords closing the if statement and the for loop. Handling jj is a little tricky. But it's not terribly complicated. And this is the way you do it with the typical programming language. But MATLAB is not a typical programming language. MATLAB provides a much more elegant intuitive solution. A solution that's more, well, MATLAB like. Let's start with the solution that we've come up with so far. Well, with MATLAB, we don't need to mess with jj at all. Let's get rid of that. There, jj's gone. And now we're assigning v(ii) to w. That's not we want. What we really want to do is stick v(ii) onto the end of w, like this. The hard work is done by the single expression on the right of the equal sign. It constructs a vector that has all of the elements that are currently in w. Followed by v(ii). Left bracket, w, v(ii), right bracket. And we assign this new vector back to w. It's much clearer here that we are appending v(ii) to the end of w than it was with the other version. Not to mention it's shorter. MATLAB is just so nice to work with. And folks, it gets even better. The ultimate MATLAB solution to this problem and many like it is one single line of code. In fact, it's less than ten characters. If I could have the drumroll, please. Really? Okay, we don't have the drum. Anyway, here it is. It's concise, it's elegant, and it solves the problem. If you don't believe me, just pause this video and try it out in MATLAB. I'll admit that this syntax looks a bit unusual. This is an example of logical indexing, and it definitely needs some explaining. What it says is that the elements of w are set equal to the elements of v wherever those elements are greater than or equal to 0. In order to understand how it works though, we first need to look at logical arrays. We've actually seen logical arrays already. We just didn't call them that. Last week, we saw that MATLAB considers everything that's non-zero as logically meaning true and zero as logically meaning false. And when we ask MATLAB to give a logical true value, it returns 1. The greater than operation was applied to each corresponding pair of elements in the two vectors. Like 4 and 5, 4 isn't greater than 5, so it gave 0 for false. Minus 1 is greater than minus 9, so we get a 1 for true, and so on. In effect, it goes through each pair, asserting that the left element of the pair is greater than the right element, and it returns 1 when the assertion is true, and 0 when it's false. The result is a vector of logical values. These are not just ordinary numbers. The ones and zeros that are output by the greater than operator or by any other relational operator are special. They belong to a special type of value called the logical value. We'll see later that there are lots of types of values in MATLAB. But for now, we'll just point out that we've seen ordinary numerical values like you get when you do arithmetic. We've seen characters and now we've seen logical values, like you get from the relational operators. And we just got a logical array in the form of a vector. Well, there are other ways to produce arrays of logical values too. Consider this command. So what happened here? The first element of this vector is true, 2 is greater than 1. So, we return a 1 for true. Second element is false. So, here comes a zero. These are logical values. And what about the third one? Well, we've got 3 greater than 2, that's true. And 4 greater than 5, that's false. So, true anded with false gives false. But, then we apply the not operator here, symbolized by this tilde, to the false. And that turns is into true. [LAUGH] And well finally, we get a true here. Now, we knew that these results here were logical values because the relational operators give logical values. Well, now we'll tell you that the logical operators, in additional to the relational operators, return, well, of course, logical values also. So this is an array of logical values. It's a logical array. We can also create a logical array using a built-in function. Can you guess what its name is? Well this ought to give you a clue. No, the name isn't holmes. The name is logical. And it takes each one of these inputs in this vector. And it looks at every element in this vector. And every element that is non zero is turned into true as a one and the zeroes become logical zeroes. All of these are of the type logical. A very nice feature of MATLAB is that lets you index into an array using a logical array of the same size. Let's gin up a row vector with 3 elements, called A. Easy. Now we are going to index into A, using the logical vector of the same size. Namely C. So what does A, so what does A left parenthesis C right parenthesis mean? Well, to MATLAB, it means that it should select those and only those elements of A where C has logical value true. So it did that, and the result is a two-element vector consisting of the first and third elements of A. When an array of logical values, instead of ordinary numbers, is put in parentheses after the name of an array, this is always the way it works. The output is an array of values located at the positions where the logical values are true. The output array is usually smaller than the input array, as it was here. It's called logical indexing. Here the first and third elements of the vector C were true. First and third here. The second was false. So in this case, MATLAB returned a two-element row vector consisting of the first and third elements of A. Let's put holmes to work on another case. We got a random set of integers in r. And as usual I have initialized the pseudo random number generator to make it easier for you to reproduce what I'm doing. I'll do that every time. Let's look at holmes again, there. Two ones, two zeroes, two ones. And now, let's do this. As you can see the ones picked up the 9 here, the 10 here, the 7, and the 1. And the zeros cause the 2 and the 10 to be omitted. So the output is 9, 10, 7, 1. This sort of indexing can be quite a mystery at first, but once you see what holmes is actually doing here, it's not hard to understand at all. In fact, you might say it's elementary. And now we're ready to use logical indexing to do something useful. Suppose we've measured ten voltages and saved them in a vector v. Furthermore, suppose we know that if our measurement process were perfect, there wouldn't be any negative numbers. So we want to remove these two negative values here, minus 2 and minus 3. Well we could of course just remove them by hand from this little vector. But suppose that this is a really long vector, maybe thousands of elements. So we need a code that will do it automatically. Well, we can do it this way. First, we make a logical vector like this. Let's look at this statement carefully. We know this is an assignment statement because of this equals here. On the left is the variable keepers, on the right is the expression we're going to evaluate, and then assign the result to keepers. Well, that's v greater than or equal to 0. Well, the greater than or equal, I call the relational operators, is an array operator. So the zero is compared to every element, in turn, of v. And we get that the first one and the second one are greater or equal to zero, but the third one, as you can see, minus 2 here, is not. The fourth, fifth, sixth, seventh, we get to the eighth, that's the minus 3. It's not. And the other two are, so we've got all these ones and zeros representing the truth and falseness of the assertion that v is greater than or equal to 0, which means each element of v is asserted to be greater than or equal to 0. Now we can use keepers to do logical indexing on v like this. We're going to set the result into w. These two zeros, in other words, false, causes the corresponding elements in v, the minus 2 and the minus 3, to be omitted, and then this shorter vector is assigned to w. We did all this with just two commands. That's pretty good. But one command would be better. One line, one command, same result. It has to be the same result because we're doing exactly the same logical indexing. We just avoided the step of storing the logical array produced by the expression greater than or equal to 0 in keepers. And we used it directly in the logical indexing here. You should recognize this command, by the way, because it's the same command that I showed you on the slides that replaced eight lines of for loop code. This version is a lot easier to program than the for loop version, and it runs faster. Ten to 20 times faster. We'll show you how to time code in just a little bit. In this speedy little command, we use the vector v in relational expression here to construct a logical array that we use to index into the same vector, v. But the logical array doesn't have to be derived from the array indexes. For example, suppose that when the voltages in v were measured, there was a detector running that measured a confidence value for each measurement, and the values range, say, from 100 down to zero. Like this. We can use this vector to discard values in the vector v for which we have low confidence. Let's say we want to keep only the values in v for which the measurement confidence was at least 10. We can do that with this command. So what happened here, confidence was compared with 10. This is an array operation, so each element of confidence is checked to see if it's greater than or equal to 10. And if it is, we get true, otherwise, we get false. So we get true, true, true, true, true, true, false, false, and then a true. Well that's used to index into v. Everywhere there's a true, we get something out of v, when there's a false, well, it's omitted. And let's see what v looks like, here's v. So that's going to be true, true, true all the way till we get to these next to the last elements, these two here. And they are omitted, as you can see between the 30 and the 45, those elements are gone. If we wanted to keep, say, the ones with a confidence of 20 or more, like this, then fewer elements in V survive. So now you know how to use logical indexing to omit elements that are smaller than some threshold value. Now, of course, you could omit them if they were larger than the threshold, or if they were equal to some bad value, et cetera. But there's another way to select elements to omit. Let's look at this example. There, we have another ten element row vector called v0. Now look at this statement. Here we're using v and v0 in this relational operation here, to compare each element of v with the corresponding value in v0. We can think of v0 as a whole vector of threshold values, one for each element in v. And we'll get true only if v's element is greater than v0's for each element. Since we're using the resulting logical vector to index into v here, we get rid of each element in v that's not greater than its particular threshold. So if we look all the way back up here at v, first element of v, 56, is greater than the first element in this threshold vector, so it gets kept. The next element, 34, is not greater than the threshold 35, so it disappears. The minus 2, now, it's bigger than minus 8, so it shows up, and so on. Okay, we've killed off a lot of elements with logical indexing, but it's important to point out that the vector that we've been indexing into has not changed. The omission takes place when the expression on the right of the equals sign is evaluated. So up here for example, v confidence greater than or equal to 10, that's evaluated. And we come up with some truths and falses. And that's applied to v, and the expression produces this vector. This vector is then assigned to V10. But v remains unchanged. Well, there is another way to use logical indexing that does affect the vector being indexed into. The effect is not to remove elements though, but to change their values. You do that by using logical indexing on the left side of the assignment statement. So suppose, for example, that you wanted to replace all the negative values of v with zeroes. First, let's remind ourselves what's in v. There. Now let's replace all the negative values with 0. That's these two, the minus 2 and the minus 3. We're going to replace those and here's how we do it. Sure enough, minus 2 is now 0. The minus 3 is a 0, everything else is untouched. So here, v minus 0 produces a logical array again, with true values only where the values of the elements are less than 0. And the meaning of this statement is that the value on the right side, the 0, is copied into those two elements, and only those two elements. Notice that no elements in v are lost. We still have a 10 element vector here, but some of their values are changed. Two of them, in this case. This might be a helpful summary of what's going on with these two different ways to use logical indexing. Logical indexing on the right, okay, over here, produces a subset of elements. Logical indexing on the left changes the subset of elements. In this case they were changed to zero. But you can also change each one to a different value. Let's see how to do that. Start with the same v again. Now let's assign 100 to the first negative element, and 200 to the second one, using logical indexing. When you have a vector of two or more values on the right, as in this example, the link to the vector ha s to equal number of values that will be picked out on the left, which is two. And so what happened, this element was negative, and this element was negative, so they were picked out for assignment. And the first value here was assigned the first one, so the minus 2 became 100, and the second value was assigned to the second ones. So the minus 3 became 200. And now, let's do both left and right logical indexing in one command. First, let's get that original V back again. There it is. Now let's suppose you need to add 100 to each negative element for some reason. I don't know why. Here's how you do that with logical indexing. So what's happening here? Well first, the v indexed with v less than 0 here, on the right side, produces a two-element vector containing only the values minus 2 and minus 3. Second, 100 is added to that vector to get the two-element vector containing a 98 and a 97. Third, on the left side of the equal sign, logical indexing picks out only the two elements of v that have negative values, which happen to be the same elements that we looked at over here on the right. The minus 2 and the minus 3. The minus 2 is at position one, two, three. The minus 3 is position ten, nine, eight. Three and eight, elements three and eight. And fourth, it assigns the first value that it got over here, which was 98, to element three, the first one that was less then zero. And the second element that I got over here, which was a 97, to the next element that was less then zero, which was element eight minus three. And so now it's 97. None of the other values were touched. And that's how you use logical arrays on the left and right in the same command. So far, we keep saying logical arrays, but we keep using only logical vectors Vectors. Matrices and higher dimensional arrays work fine with logical indexing, but there's a big surprise coming. Let's clear the screen and get ready for it. [SOUND] We'll start by making a two by three array [SOUND] and now we'll use logical indexing to set a variable B equal to A but keeping only the elements of A that are greater than 2. Before I hit return, I'll point out that this is just a normal right-side logical indexing that we've seen before. A greater than two will return true at every element that's greater than two so, it'll return true here, here, here, here and it'll return false for elements one and two here on the first row. That logical array of trues and falses is then indexed Into A and it'll cause the one and the two to be omitted, and the three and the four and the five and six will come on through. So, let's hit return and see what happens. Surprise! What happened to our nice, two by three array? And where'd this column vector come from? Well, remember way back at the beginning at lesson two, what we'd first said about arrays? No? Well I guess I'm not surprised that was a long time ago. Okay, now I'll say it again. We said that an array is just a set of numbers arranged in a rectangular pattern. And that each row has to have the same number of elements as all the other rows. You know, like this. Well if we were to simply omit one and two on row one, and leave the rest of it the same. This row would just have this one element three. Row two would still have three elements. And three would be hanging out way over here on the right. It looks something like this. [SOUND] I'm not sure what you'd call that. It's sure not an array. Well, what Matlab does is give up on keeping a 2 x 3 shape that a has, it just makes a column vector by stacking up the columns of a, column one on top and column two and column three before it does its logical index. Well, let's see what that vector looks like first by using the single colon as an index. Do you remember that? We showed how to do that in Lesson three. So, A looks like this, and the column version Looks like this. [SOUND] There we've used that single colon to index into A and that gives us the column version of A. You can see that it starts with the first column and then the second column, and finally, the third column. This is called column major order and that's why we gave it that name. Now that it's converted A to a column vector, in column major order, Matlab applies the logical indexing to the column vector. Like this. And there's the result, just the same as what we got directly up here. This is a strict rule. When you use logical indexing to an array on the right side of an assignment, then if the array is not a simple vector, it will always be converted to a column vector in column major order. And by strict I mean, well, strict, no exceptions. Here not one single element of value was omitted by the logical indexing on the right here. They're all here. So there's not one single reason to change it's shape. Yet it's shape was changed to a column vector. Rules are rules. This is logical indexing. A is not a vector, so the results are column vector in column major order. Case dismissed. In MatLab's court there are no appeals. but wait If you use logical indexing on the left side of an assignment, none of this column vector business happens. Logical indexing on the left of an equal sign just changes the value, if you'll remember. It doesn't omit any elements, so the problem of keeping the array rectangular never even comes up. So there's never really any need to convert the array to a column vector and the array just keeps it's same dimensions. For example here's how to set all fo the negative elements of a 5 x 5 array to zero. I've used the random normal function here, which gives numbers distributed around zero, both positive and negative. Now let's get rid of those negative numbers, replace them with zeros. [NOISE] The eight negative values have been replaced by zero. And A is still a five by five array. And it can keep its same dimensions, because no elements are emitted by indexing on the left. And you can replace these eight values with eight different values, like this, for example. [NOISE] What MatLab does is take the eight values on the right, the 101 through 108, and assign them to the eight elements that were singled out by the logical indexing on the left. An important feature is revealed here. Logical indexing on the left assigns these values to the elements that were singled out in column major order. So it scans the first column up here, looking for a negative number. It finds one here on the third row, and it assigns the first value from the right, which was 101. So this becomes 101. It continues looking on that column for negative numbers, doesn't find any. Since it's doing column major order, it goes to the next column, finds one right up here at the top. It gives it the next value from the list on the right, which is 102. You see that shows up. Then it finds another one immediately, gives that 103. Finds nothing more on this column, goes to the next column and so on. Until it's replaced all eight negative values with all eight values from the right and it's done right here. It doesn't make any difference what the shape is, the array of values on the right, so long as there are eight of them. It could be a two by four array. Or an eight by one column vector. It works the same. In fact, let's do the column vector version. I'm tired of looking at a so I used a semicolon to suppress printing this time. A single quote Up here, transposed the row vector 101 to 108 to a column vector, but the result is exactly the same, as you can see by comparing here. But it makes a big difference if the number of elements on the right is different from the number of elements found by logical indexing on the left. That's just a non-starter. But if you get the numbers to match up, the assignment works, the numbers are assigned in column major order, and A retains its shape. So we've seen that MATLAB converts a non vector to a column vector and logical indexing happens on the right of the equals sign. And it leaves the shape the same, and logical indexing happens on the left. Wait. We saw a few minutes ago that you can have logical indexing on both sides in the same command. What happens then? You can't have it both ways. Looks like MATLAB has finally painted itself into a corner. Let's look at an example. Suppose our task now is to find the elements in A that are greater than 0.1 and replace each one of them with its square root. First we'll get A back. Okay, here goes. So we've got logical indexing on the left, which means A retains its shape, and we got logical indexing on the right, which means A is changed into a column vector. Let's hit Return and see if smoke comes out of this computer. Ha. Rules are rules, huh? Well MATLAB, how do you explain this one? We've got logical indexing on the right, but the shape of A stayed the same. Well, as much as we might want to catch MATLAB in a contradiction, we haven't. It applies logical indexing on the right to omit all the elements of A that aren't greater than 0.1, which leaves 17 elements. And it puts those 17 elements into a column vector and column major order. We don't see it anywhere, but that's what's happened. It used that column vector as input to the square root function, which returned a column vector of 17 square roots. Then over here on the left, it used the same logical indexing that we used on the right and identified the same set of 17 elements as needing to get new values. And then finally, it assigned those 17 values in the same column major order that had generated them over here. It all works perfectly. MATLAB wins again. If you didn't know what's going on here, this command would be very cryptic, but a MATLAB programmer, like you, knows what's happening. And it's all self-consistent and logical. It is logical indexing, after all. If you were using C or Java, on the other hand, you'd have to write two nested for loops with an if statement in the body to get this thing accomplished. You can do that in MATLAB too, of course. Come to think of it, as an example you should implement the same thing without logical indexing. Well, we're done with our introduction to logical arrays except for one quick example. It's an example of multiple arrays all logically indexed in the same statement, just to show you that it works. There, we've got an array A and B, they're both the same size. Here each element in A that's larger than the corresponding element in B, so 89, for example, is larger than 34, is replaced by the difference between the two elements. So 89 minus 34 shows up here. 11 doesn't change, because it's not greater than the corresponding element in B. It's still 11. If you spend a minute or two analyzing it, you'll see that the rule is applied, that the same number of elements is produced on the right and consumed on the left. Well, with this example we say goodbye to logical indexing. But don't forget it whenever you need to remove or change selected elements in arrays. Logical indexing is a powerful MATLAB feature that can save you from writing many loops. Use it wherever you can. [MUSIC] [APPLAUSE]