find the rank of an element in a vector in matlab - matlab

I have the task to combine two vectors in the following way. The input are two vectors with the first one indicating the indices of the dividors of the groups and the second one indicating the elements we are trying to classify. For example, vector [1,3,5,9] means the first group consists of 1, the second group consists of 2 and 3, the third group consists of 4 and 5, and the forth one consists of 6, 7, 8 and 9, etc. In this case, if the second vector is [2,4,6], then the output we get is [2,3,4].
I know how to impliment this in matlab with for loops. My question is: is there anyway to do it without for loops? Many thanks for your time and attentions.
EDIT:
scaleVtr=[1,3,5,9];
>> eltVtr=[2,4,6];
>> j=1; output=[];
>> for i=1:size(eltVtr,2)
while(true)
if eltVtr(i)<=scaleVtr(j)
output= [output,j];
break;
else j=j+1;
end
end
end
>> output
output =
2 3 4

qq = [1 3 5 9];
qq2 = [2 4 6];
ceil(interp1(qq,1:numel(qq),qq2))

This can also be done with bsxfun:
v1 = [1,3,5,9];
v2 = [2,4,6];
result = sum(bsxfun(#gt, v2(:).', v1(:)), 1) + 1;

Related

How to extract vectors of consecutive numbers?

Suppose that I have a Q vector which is defined as Q = [1 2 3 4 5 8 9 10 15]; and I would like to find a way to extract different vectors of consecutive numbers and also a vector for the rest of the elements. So my result would be like:
q1 = [1 2 3 4 5];
q2 = [8 9 10 ];
q3 = [15];
You can do this using diff, cumsum and accumarray:
q = accumarray(cumsum([1, diff(Q)~=1])', Q', [], #(x){x})
which returns:
{[1,2,3,4,5];
[8,9,10];
[15]}
i.e. q{1} gives you [1,2,3,4,5] etc which is a far cleaner solution to having separately named vectors. But if you really really wanted to have them, and you know exactly how many groups you will get out, you can do it as follows:
[q1,q2,q3] = q{:};
Explanation:
accumarray will apply an aggregation function (4th input) to elements of a vector (2nd input) based on groupings specified in another vector (1st input).
To use the notation in the docs:
sub = cumsum([1, diff(Q)~=1])';
val = Q';
fun = #(x){x};
Note that sub needs to start from 1. The idea is to use diff to find elements that are consecutive (i.e. where Q(i+1) - Q(i) == 1) which is vectorized using the diff function. By specifying diff(Q)~=1 we can find the breaks between groups of consecutive numbers (concatenating the 1 at the beginning to force a break at the start). cumsum then just converts these breaks into vector of in the right form for sub i.e.
sub = [1 1 1 1 1 2 2 2 3]
The aggregation function we specify is just cell concatenation.

confused about the output of hist

I am confused by the
[m,n]=hist(y,x)
such as
M = [1, 2, 3;
4, 5, 6;
1, 2, 3];
[m,n] = hist(M,1:3)
Which results in
m = 2 0 0
0 2 0
1 1 3
Can someone please explain how m is calculated?
hist actually takes vectors as input arguments, you wrote a matrix, so it just handles your input as if it was several vector-inputs. The output are the number of elements for each container (in your case 1:3, the second argument).
[m,n] = hist([1,2,3;4,5,6;1,2,3],1:3)
treats each column as one input. You put in 3 inputs (# of columns) and you get 3 outputs.
[2 0 1]'
means, for the input [1;4;1] and the bin 1:3 two elements are in bin 1 and one element is in bin 3.
Look at the last column of m, here all three values are in the third bin, which makes sense, since the corresponding vector is [3;6;3], and out of those numbers all have to go into the bin/container 3.

splitting a Matrix into column vectors and storing it in an array

My question has two parts:
Split a given matrix into its columns
These columns should be stored into an array
eg,
A = [1 3 5
3 5 7
4 5 7
6 8 9]
Now, I know the solution to the first part:
the columns are obtained via
tempCol = A(:,iter), where iter = 1:end
Regarding the second part of the problem, I would like to have (something like this, maybe a different indexing into arraySplit array), but one full column of A should be stored at a single index in splitArray:
arraySplit(1) = A(:,1)
arraySplit(2) = A(:,2)
and so on...
for the example matrix A,
arraySplit(1) should give me [ 1 3 4 6 ]'
arraySplit(2) should give me [ 3 5 5 8 ]'
I am getting the following error, when i try to assign the column vector to my array.
In an assignment A(I) = B, the number of elements in B and I must be the same.
I am doing the allocation and access of arraySplit wrongly, please help me out ...
Really it sounds like A is alread what you want--I can't imagine a scenario where you gain anything by splitting them up. But if you do, then your best bet is likely a cell array, ie.
C = cell(1,3);
for i=1:3
C{i} = A(:,i);
end
Edit: See #EitanT's comment below for a more elegant way to do this. Also accessing the vector uses the same syntax as setting it, e.g. v = C{2}; will put the second column of A into v.
In a Matlab array, each element must have the same type. In most cases, that is a float type. An your example A(:, 1) is a 4 by 1 array. If you assign it to, say, B(:, 2) then B(:, 1) must also be a 4 by 1 array.
One common error that may be biting you is that a 4 by 1 array and a 1 by 4 array are not the same thing. One is a column vector and one is a row vector. Try transposing A(:, 1) to get a 1 by 4 row array.
You could try something like the following:
A = [1 3 5;
3 5 7;
4 5 7;
6 8 9]
arraySplit = zeros(4,1,3);
for i =1:3
arraySplit(:,:,i) = A(:,i);
end
and then call arraySplit(:,:,1) to get the first vector, but that seems to be an unnecessary step, since you can readily do that by accessing the exact same values as A(:,1).

What is the quickest way to keep the non dominated elements and omit the rest in MATLAB?

For example [2 , 5] dominates [3 , 8] cause (2 < 3) and (5 < 8)
but [2 , 5] does not dominates [3 , 1] cause though (2 < 3) but (5 > 1) so these two vectors are non dominated
now for example assume that I have a matrix like this :
a =[ 1 8;
2 6;
3 5;
4 6];
here the first three are non dominated but the last one is dominated by (3,5), I need a code which can omit it and give me this output:
ans =
[ 1 8;
2 6;
3 5]
note that there may be lots of non dominated elements in a Nx2 matrix
Compare one row with other rows using bsxfun
Do this for every row using arrayfun (or a loop if you prefer that) and transform the output back to a matrix with cell2mat
use any and all to check which rows are dominated
remove these rows
code:
a=[1 8;2 6;3 5;4 6];
dominated_idxs = any(cell2mat(arrayfun(#(ii) all(bsxfun(#(x,y) x>y,a,a(ii,:)),2),1:size(a,1),'uni',false)),2);
a(dominated_idxs,:) = [];
edit
If you want to use >= instead of > comparison, each row will dominate itself and will be removed, so you'll end up with an empty matrix. Filter these false-positives out by adjusting the code as follows:
a=[1 8;2 6;3 5;4 6];
N = size(a,1);
compare_matrix = cell2mat(arrayfun(#(ii) all(bsxfun(#(x,y) x>=y,a,a(ii,:)),2),1:N,'uni',false));
compare_matrix(1:N+1:N^2)=false; % set diagonal to false
dominated_idxs = any(compare_matrix,2);
a(dominated_idxs ,:) = [];
This problem is identical to identifying the so-called Pareto front.
If the number of elements N grows large and/or you need to carry out this sort of operation often (as I suspect you do), you might want to give a thought to a fully optimized MEX file for this purpose (available on the Mathworks File Exchange):
Compiling this, putting the mex in your Matlab path, and then using something like
a = a(paretofront(a));
will accomplish your task much quicker than any combination of Matlab-builtins is able to.

How to efficiently access/change one item in each row of a matrix

I have a matrix A with size (nr,nc), a vector of column indices B (so B has size (nr,1) and every element in B is an integer between 1 and nc), and I want to do something to every element in A that is of the form A(i,B(i)) for i between 1 and nr, efficiency being the key concern.
For concreteness, say C is a vector of size (nr,1), the goal is to do
for i=1:nr
A(i,B(i))=A(i,B(i))+C(i)
end
more efficiently. The context is usually that nr>>nc (because when nr is large vectorization is efficient for many operations). I have gotten a factor 3 speedup by using an indicator function approach:
for k=1:nc
A(:,k)=A(:,k)+(k==B).*C
end
Are there other ways (more efficient hopefully) to do this?
I guess this is similar to many questions on double-indexing, but it's concretely one I run into all the time.
Use linear indexing:
idx = sub2ind(size(A), 1:nr, B');
A(idx) = A(idx) + C';
or (edited version with one less transpose)
idx = sub2ind(size(A), (1:nr)', B);
A(idx) = A(idx) + C;
One way would be to use linear indexing of the matrix. You will need a vector v holding the offsets of the first element in each line, then index using A(v + B). For example:
>A=[1 2 3; 4 5 6; 7 8 9]
A =
1 2 3
4 5 6
7 8 9
>B = [1 2 3] % we want the 1st element of row 1, 2nd of row 2, 3rd of row 3
>ii = [0 3 6] + B
>a(ii)
1 5 9
Note: As groovingandi had shown, it is also possible (and more readable) to use sub2ind to generate the ii linear indices vector. The idea is essentially the same.