Copy element of matrix to a vector MATLAB - matlab

Assume we have a matrix A (2x5), with the first row containing the numbers:
1 2 3 5 7
and the second row:
0.4 0.1 0.2 0.1 0.2
Also, there is a 10 dimensional vector B with numbers 1,2,3...10.
How can I create a new 10 dimensional vector C that will only contain the values of A (second row) when A(1,:) == B, else 0.
So the new vector C should have the form:
0.4 0.1 0.2 0 0.1 0 0.2 0 0 0
(add zero for the cells of B that are not in A).
I tried this solution but I have a problem due to the difference in dimensions between A and B.
for i=1:53
if B(i) == A(1,i)
C{1,i} = A(2,i);
else
C{1,i}=0;
end
end
Index exceeds matrix dimensions.

It's not very clear what you're after, but this at least gives the desired output:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
B = 1:10;
[tf,loc] = ismember(A(1,:), B);
C = zeros(1,10);
C(loc(tf)) = A(2,tf)
[I'm assuming you mean 10 element vector, rather than 10 dimensional...]
If you just want to use the first row of A as indices and the second row as assigned values, then you don't need to use B at all and you can do something like this:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
C = zeros(1,10);
C(A(1,:)) = A(2,:)

How about removing the for loop and do it inline using ismember function:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
B = 1:10;
C = zeros(1,10);
C(B(ismember(B, A(1,:)))) = A(2,ismember(A(1,:),B));
Hint: Even if we happen to have a value in A(1,:) which B does not have, this solution will work.

Using ismember and a for loop:
clc; clear;
A=[
1 2 3 5 7;
0.4 0.1 0.2 0.1 0.2
];
B = 1:10;
C = zeros(1,10);
for j = 1:10
if ismember(j, A(1,:))
C(j) = A(2, A(1,:) == j);
else
C(j) = 0;
end
end
C

Related

Create matrices from a given cell-array of strings with different lengths

I have 3 sequences in a cell-array:
Input_cell= {'ABCD','ACD', 'ABD'}
S1= 'ABCD' % which means A<B<C<D
S2= 'ACD' % which means A<C<D % missing B in the full string of 'ABCD'
S3= 'ABD' % which means A<B<D % missing C in the full string of 'ABCD'
I want to convert each of the strings in the Input_cell into a matrix M (i-by-j) which has to satisfy these conditions:
M(i,j) and M(j,i) are random
M(i,i) = 0.5
M(i,j) + M(j,i) = 1
M(i,j) < M(j,i) For example if A<B then M(A,B) < M(B,A)
For example if we have S1 = 'ABCD' (which means A<B<C<D), the M1 matrix will be expected as follows:
A B C D
A 0.5 0.3 0.2 0.1
B 0.7 0.5 0 0.4
C 0.8 1 0.5 0.1
D 0.9 0.6 0.9 0.5
If we have S2 = 'ACD' (which means A<C<D), missing B in the full string of 'ABCD', we will put the value 0.5 in every position of B in the matrix, the M2 matrix will be expected as follows:
A B C D
A 0.5 0.5 0.2 0.1
B 0.5 0.5 0.5 0.5
C 0.8 0.5 0.5 0.1
D 0.9 0.5 0.9 0.5
If we have S3 = 'ABD' (which means A<B<D), missing C in the full string of 'ABCD', we will put the value 0.5 in every position of C in the matrix, the M3 matrix will be expected as follows:
A B C D
A 0.5 0.4 0.5 0.1
B 0.6 0.5 0.5 0.3
C 0.5 0.5 0.5 0.5
D 0.9 0.7 0.5 0.5
How to create that kind of above matrices from a given cell-array of sequences?
Firstly you need to work out how to do this just for a single sequence:
Create a matrix of random numbers between 0.5 and 1:
M = 0.5*rand(4) + 0.5;
Set the main diagonal to equal 0.5
M(logical(eye(4))) = 0.5;
Set the upper triangle of M equal to 1 - the lower triangle:
M(triu(true(4))) = 1 - M(tril(true(4))); %// Note the main diagonal doesn't matter...
Work out which letter is missing and set the row and column equal to 0.5 accordingly:
fullSeq = 'abcd';
idx = find(fullSeq == setdiff(fullSeq, 'abd'));
%// at this point you'll need to check if idx is empty first...
M(:,idx) = 0.5;
M(idx,:) = 0.5;
And now that you can do it for one matrix, just loop over your cell array or else encapsulate this into a function and use cellfun.

Is it possible to vectorize procedural operations (for/if ) in Matlab?

I wonder if the following code can be vectorized? Or, more straightforward, I am trying to match a number to several intervals, the result of which determines the update of a increment process. Thanks a lot!
pop_matrix = [10 0 0 0 0 0];
rand_num =rand;
divid = [0.05 0.05 0.1 0.2 0.1 0.1];
for i = 1:6
if rand_num < sum(divid(1:i))
pop_matrix(i) = pop_matrix(i)+1;
break
end
end
The following should work:
pop_matrix = [10 0 0 0 0 0];
rand_num =rand;
divid = [0.05 0.05 0.1 0.2 0.1 0.1];
idx = find(cumsum(divid) > rand_num,1);
pop_matrix(idx) = pop_matrix(idx) + 1;
EDIT: A method using interp1 which is about 10x faster, assuming you want to draw N samples from the distribution called divid:
pop_matrix = [10 0 0 0 0 0];
divid = [0.05 0.05 0.1 0.2 0.1 0.1];
N = 1000; %// number of random samples to take
rand_num = rand(N,1); %// generate N random numbers
dcs = cumsum(divid); %// get cumulative distribution
dcs = dcs/dcs(end); %// ensure this is normalized to 1
dcs = [0,dcs]; %// put a zero in front to create a new bin
s = interp1(dcs, 1:length(dcs), rand_num, 'previous', NaN); %// draw samples
pop_matrix = pop_matrix + accumarray(s,1)'; %'//add up samples together
This process is basically sampling from the probability distribution defined by divid using the Inverse Transform Sampling method, where dcs is the distribution's cumulative density function (CDF).

changing multiple columns of a matrix with respect to sorted indices of its specific columns

Let's say I have a 2 by 9 matrix. I want to replace the 2 by 3 matrices inside this matrix with respect to descending sort of a(2,3), a(2,6), and a(2,9) elements. For example:
a =
0.4 0.4 0.5 0.6 0.2 0.2 0.6 0.2 0.6
0.5 0.8 0.9 0.9 0.6 0.6 0.1 0.2 0.8
[b i] = sort(a(2,3:3:end),2,'descend')
b =
0.9 0.8 0.6
i =
1 3 2
So, I want to have the following matrix:
a =
0.4 0.4 0.5 0.6 0.2 0.6 0.6 0.2 0.6
0.5 0.8 0.9 0.1 0.2 0.8 0.9 0.6 0.6
Try converting to a cell matrix first and then using your i to rearrange the cells
[b i] = sort(a(2,3:3:end),2,'descend')
A = mat2cell(a, 2, 3*ones(1,3));
cell2mat(A(i))
If for whatever reason you don't want to convert the whole of a into a cell matrix, you can do it by extending your indexing vector i to index all the columns. In your case you'd need:
I = [1,2,3,7,8,9,4,5,6]
which you could generate using a loop or else use bsxfun to get
[1 7 4
2 8 5
3 9 6]
and then "flatten" using reshape:
I = reshape(bsxfun(#plus, 3*s-2, (0:2)'), 1, [])
and then finally
a(:,I)
Typically, when a 2d matrix is separated into blocks, best practice ist to use more dimensions:
a=reshape(a,size(a,1),3,[]);
Now you can access each block via a(:,:,1)
To sort use:
[~,idx]=sort(a(2,3,:),'descend')
a=a(:,:,idx)
If you really need a 2d matrix, change back:
a=reshape(a,2,[])
sortrows-based approach:
n = 3; %// number of columns per block
m = size(a,1);
a = reshape(sortrows(reshape(a, m*n, []).', -m*n).', m, []);
This works by reshaping each block into a row, sorting rows according to last column, and reshaping back.

Remove any column with same values in MATLAB

I need to remove any column in a matrix which has same values in it. I designed it using a for-loop in MATLAB. I wanted to know if a better/faster way exists using vectorization.
mat = [ 0.56 0.2 1 0 45; 0.566 0.2 4 0 45; 0.52 0.2 6 0 45; 0.56 0.2 6 0 41 ];
[row col] = size(mat) ;
bitmat = true(1,col) ;
for i = 2:row, tf = (mat(i-1,:) == mat(i,:)) ; bitmat = bitmat & tf ; end
mat(:,bitmat) = [] ;
Thanks!
Here's a simple one-liner using the functions DIFF and ANY:
mat = mat(:,any(diff(mat,1)));

Mapping elements in vector to related, but larger vector

I need some help to map elements from a short vector to a larger vector in Matlab. I can do this with a for-loop, but I'm sure there's a way to avoid this.
I have input vectors of same size: A = [ 2 3 5 ] and B = [ 0.1 0.3 0.23 ]. Vector A contains "index" and vector B data. A third input vector is given as C = [ 2 2 2 3 3 3 3 5 5 ] and now I want to generate the vector D = [ 0.1 0.1 0.1 0.3 0.3 0.3 0.3 0.23 0.23 ].
How can I, in Matlab, generate vector D, without using for-loops?
Thank you in advance!
A = [2 3 5];
B = [0.1 0.3 0.23];
C = [2 2 2 3 3 3 3 5 5];
Use the second output of ismember to create an index vector:
[~, ind] = ismember(C, A);
D = B(ind);
Alternatively, use interp1:
D = interp1(A, B, C);
You can also use unique to look up the index of each element in C, assuming that the values correspond to exactly the values in A. If A is not sorted then we have to sort the items of B first in order to match the indexing given by unique:
A = [2 3 5];
B = [0.1 0.3 0.23];
C = [2 2 2 3 3 3 3 5 5];
[~,isort] = sort(A);
Bsort = B(isort); % sorted according to A
[~,~,k] = unique(C); % indices of items in C to pick from A
D = Bsort(k); % each matching element from (sorted) B
If the elements of the index vector are positive integers you can just use indexing:
idx(A,1) = 1:numel(A);
D = B(idx(C));
If A contains positive integers of large values you can use sparse matrix:
idx = sparse(A,1,1:numel(A));
D = B(idx(C));