How do I get the indexes of specific elements based on value and then replace them in MATLAB? - matlab

From the exercises in a book I am using to learn MATLAB:
Given x = [3 15 9 12 -1 0 -12 9 6 1],
provide the command(s) that will
A) set the values of x that are
positive to zero
B) set values that are multiples of 3
to 3 (rem will help here)
C) multiply the values of x that are
even by 5
D) extract the values of x that are
greater than 10 into a vector called
y
E) set the values in x that are less
than the mean to zero
F) set the values in x that are above the mean to their difference from the mean

Question a) will teach you the following elements:
find a function that returns indexes given a condition, in your case x>0
use indexing in order to set selected values in x to 0
to be continued ...

x = [3 15 9 12 -1 0 -12 9 6 1]
vi = (x < 0) % statement that returns a boolean, gives a vector like
% [0 0 0 0 1 0 1 0 0 0]
x(vi) = -x(vi) % does the operation (negating in this case) on the relevant
% values of x (those with a 1 from above)
Without actually doing your homework, they all follow the above pattern.

I agree with the comments to your question, that is not necessarily the right way to go if you really want to learn something.
As to answer your question, MATLAB has a fantastic function browser I strongly suggest you take a look at it. With well chosen keywords you can go a long way. :)

Related

Output a matrix size n x m, 1 when the sum of the indices is even, 0 otherwise

I'm attempting the following as a hobby, not as homework. In Computer Programming with MATLAB: J. Michael Fitpatrick and Akos Ledeczi, there is a practice problem that asks this:
Write a function called alternate that takes two positive integers, n and m, as input arguments (the function does not have to check the format of the input) and returns one matrix as an output argument. Each element of the n-by-m output matrix for which the sum of its indices is even is 1.
All other elements are zero.
A previous problem was similar, and I wrote a very simple function that does what it asks:
function A = alternate(n,m)
A(1:n,1:m)=0;
A(2:2:n,2:2:m)=1;
A(1:2:n,1:2:m)=1;
end
Now my question is, is that good enough? It outputs exactly what it asks for, but it's not checking for the sum. So far we haven't discussed nested if statements or anything of that sort, we just started going over very basic functions. I feel like giving it more functionality would allow it to be recycled better for future use.
Great to see you're learning, step 1 in learning any programming language should be to ensure you always add relevant comments! This helps you, and anyone reading your code. So the first improvement would be this:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
A(1:n,1:m)=0; % Create the n*m array of zeros
A(2:2:n,2:2:m)=1; % All elements with even row and col indices: even+even=even
A(1:2:n,1:2:m)=1; % All elements with odd row and col indicies: odd+odd=even
end
You can, however, make this more concise (discounting comments), and perhaps more clearly relate to the brief:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
% Sum of row and col indices. Uses implicit expansion (R2016b+) to form
% a matrix from a row and column array
idx = (1:n).' + (1:m);
% We want 1 when x is even, 0 when odd. mod(x,2) is the opposite, so 1-mod(x,2) works:
A = 1 - mod( idx, 2 );
end
Both functions do the same thing, and it's personal preference (and performance related for large problems) which you should use.
I'd argue that, even without comments, the alternative I've written more clearly does what it says on the tin. You don't have to know the brief to understand you're looking for the even index sums, since I've done the sum and tested if even. Your code requires interpretation.
It can also be written as a one-liner, whereas the indexing approach can't be (as you've done it).
A = 1 - mod( (1:n).' + (1:m), 2 ); % 1 when row + column index is even
Your function works fine and output the desired result, let me propose you an alternative:
function A = alternate(n,m)
A = zeros( n , m ) ; % pre-allocate result (all elements at 0)
[x,y] = meshgrid(1:m,1:n) ; % define a grid of indices
A(mod(x+y,2)==0) = 1 ; % modify elements of "A" whose indices verify the condition
end
Which returns:
>> alternate(4,5)
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
initialisation:
The first line is the equivalent to your first line, but it is the cannonical MATLAB way of creating a new matrix.
It uses the function zeros(n,m).
Note that MATLAB has similar functions to create and preallocate matrices for different types, for examples:
ones(n,m) Create
a matrix of double, size [n,m] with all elements set to 1
nan(n,m) Create a
matrix of double, size [n,m] with all elements set to NaN
false(n,m) Create a
matrix of boolean size [n,m] with all elements set to false
There are several other matrix construction predefined function, some more specialised (like eye), so before trying hard to generate your initial matrix, you can look in the documentation if a specialised function exist for your case.
indices
The second line generate 2 matrices x and y which will be the indices of A. It uses the function meshgrid. For example in the case shown above, x and y look like:
| x = | y = |
| 1 2 3 4 5 | 1 1 1 1 1 |
| 1 2 3 4 5 | 2 2 2 2 2 |
| 1 2 3 4 5 | 3 3 3 3 3 |
| 1 2 3 4 5 | 4 4 4 4 4 |
odd/even indices
To calculate the sum of the indices, it is now trivial in MATLAB, as easy as:
>> x+y
ans =
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
Now we just need to know which ones are even. For this we'll use the modulo operator (mod) on this summed matrix:
>> mod(x+y,2)==0
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
This result logical matrix is the same size as A and contain 1 where the sum of the indices is even, and 0 otherwise. We can use this logical matrix to modify only the elements of A which satisfied the condition:
>> A(mod(x+y,2)==0) = 1
A =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
Note that in this case the logical matrix found in the previous step would have been ok since the value to assign to the special indices is 1, which is the same as the numeric representation of true for MATLAB. In case you wanted to assign a different value, but the same indices condition, simply replace the last assignment:
A(mod(x+y,2)==0) = your_target_value ;
I don't like spoiling the learning. So let me just give you some hints.
Matlab is very efficient if you do operations on vectors, not on individual elements. So, why not creating two matrices (e.g. N, M) that holds all the indices? Have a look at the meshgrid() function.
Than you might be able find all positions with an even sum of indices in one line.
Second hint is that the outputs of a logic operation, e.g. B = A==4, yields a logic matrix. You can convert this to a matrix of zeros by using B = double(B).
Have fun!

Find first occurrence of any number of a vector

I want to find the first occurrence of either 2,3 or 5 in x. The output should be 7 as 3 appears first at index 7. How do I do that?
x = [0 0 0 0 1 1 3 5 2 0 0];
y = [2 3 5];
output = 7
I can use find(x == 3, 1) to find just one number but how do I do that for multiple numbers?
Thanks
MATLABs find is already the right choice in combination with ismember
In this case
>> find(ismember(x,y),1)
will do what you want. It returns the index of the first occurence of array y in array x. This will work for any lengths y might have.
Find() accepts a logical term as an argument, so you can use your call but instead of checking only for 3, use something like:
(x==2 || x==3 || x==5)
I'm general, you might want to use the intersect() function.

determine lag between two vector

I want to find the minimum amount of lag between two vector , I mean the minimum distance that something is repeated in vector based on another one
for example for
x=[0 0 1 2 2 2 0 0 0 0]
y=[1 2 2 2 0 0 1 2 2 2]
I want to obtain 4 for x to y and obtain 2 for y to x .
I found out a finddelay(x,y) function that works correctly only for x to y (it gives -4 for y to x).
is there any function that only give me lag based on going to the right direction of the vector? I will be so thankful if you'd mind helping me to get this result
I think this may be a potential bug in finddelay. Note this excerpt from the documentation (emphasis mine):
X and Y need not be exact delayed copies of each other, as finddelay(X,Y) returns an estimate of the delay via cross-correlation. However this estimated delay has a useful meaning only if there is sufficient correlation between delayed versions of X and Y. Also, if several delays are possible, as in the case of periodic signals, the delay with the smallest absolute value is returned. In the case that both a positive and a negative delay with the same absolute value are possible, the positive delay is returned.
This would seem to imply that finddelay(y, x) should return 2, when it actually returns -4.
EDIT:
This would appear to be an issue related to floating-point errors introduced by xcorr as I describe in my answer to this related question. If you type type finddelay into the Command Window, you can see that finddelay uses xcorr internally. Even when the inputs to xcorr are integer values, the results (which you would expect to be integer values as well) can end up having floating-point errors that cause them to be slightly larger or smaller than an integer value. This can then change the indices where maxima would be located. The solution is to round the output from xcorr when you know your inputs are all integer values.
A better implementation of finddelay for integer values might be something like this, which would actually return the delay with the smallest absolute value:
function delay = finddelay_int(x, y)
[d, lags] = xcorr(x, y);
d = round(d);
lags = -lags(d == max(d));
[~, index] = min(abs(lags));
delay = lags(index);
end
However, in your question you are asking for the positive delays to be returned, which won't necessarily be the smallest in absolute value. Here's a different implementation of finddelay that works correctly for integer values and gives preference to positive delays:
function delay = finddelay_pos(x, y)
[d, lags] = xcorr(x, y);
d = round(d);
lags = -lags(d == max(d));
index = (lags <= 0);
if all(index)
delay = lags(1);
else
delay = lags(find(index, 1)-1);
end
end
And here are the various results for your test case:
>> x = [0 0 1 2 2 2 0 0 0 0];
>> y = [1 2 2 2 0 0 1 2 2 2];
>> [finddelay(x, y) finddelay(y, x)] % The default behavior, which fails to find
% the delays with smallest absolute value
ans =
4 -4
>> [finddelay_int(x, y) finddelay_int(y, x)] % Correctly finds the delays with the
% smallest absolute value
ans =
-2 2
>> [finddelay_pos(x, y) finddelay_pos(y, x)] % Finds the smallest positive delays
ans =
4 2

MATLAB syntax length

I'm reading some MATLAB trying to pick it up. The line below is probably rather simple but I do not understand it.
I understand length will give me the length of a vector, in this case a vector which is part of a struct, index_struct.data_incl.
The actual value of index_stuct.data_incl at run time is simply 1. What is confusing me is what is inside the brackets i.e. (index_struct.data_incl == 1)? I can't work out what this line is trying to do as simple as it may be!
int_var = length(index_struct.data_incl(index_struct.data_incl == 1));
try this (but think of x as your index_struct.data_incl:):
x = [1 4 5 13 1 1]
length(x(x==1))
ans =
3
It's just counting the number of elements of your x vector that are equal to 1
because x==1 evaluates to [1 0 0 0 1 1] and then using logical indexing x(x==1) evaluates to [1 1 1] whose length is 3;
It could have been written more simply as sum(index_struct.data_incl == 1)
If I dont see the code I can only guess..., but I guess that index_struc.data_incl should be a vector, with length n meaning that you have the option to read until n files, and all the values of the array should be 0 at the begining, and when you read a file you change the corresponding position in the vector index_struc.data_incl from 0 to 1. After some time you can see how many of these files you have read using
int_var = length(index_struct.data_incl(index_struct.data_incl == 1));
because it will give to you the number of 1 in the vector index_struct.data_incl.

Uniform random number generation from a set

I would like to find out if there is a function that can generate random numbers from a set of numbers in Matlab? For instance say I have the set [-1 1]. How can I generate numbers from that set? I have tried to use randi([-1,1]) but that obviously will generate the numbers -1,0 and 1 and I would like to generate numbers uniformly WITHOUT the 0 and only include -1 and 1. I apologise in advance if this seems like a trivial question but I can't seem to find the answer.
Thanks for any help you can provide.
Edit: I've found a simple solution for the above: (randi([0 1])*2) - 1. This only solves this problem and not a generation of numbers from a specified set.
Well, is there a function that can generate integers uniformly from the set 1:2, or, in general, from the set 1:n, where n is the number of elements in your set? (Yes)
If the above answer was yes, then can those numbers be used as an index into the generated set? (Yes)
A = [-1 1];
n = numel(A);
A(ceil(rand*n))
Will it sample randomly and uniformly? (Yes)
Generate a uniform random integer in the range 1 to N (where N is the size of the set). Then use that to index into the set.
>> S = sign(rand(1,10) - .5); S(S ~= 0)
ans =
1 -1 1 1 -1 1 -1 -1 1 -1
This is obviously specialized to your specific example, but the same general concept can be applied.
EDIT: Here is a more general example:
>> Set = [1 10 100]
Set =
1 10 100
>> Set(randi([1 3], 10, 1))
ans =
100 100 100 100 100 10 10 1 100 1