How to shift zero in the last column of a matrix - matlab

I have one matrix like below-
A=[1 1 1 1 1;
0 1 1 1 2;
0 0 1 1 3]
But I want to place all the 0 at the end of the row, so A should be like-
A=[1 1 1 1 1;
1 1 1 2 0;
1 1 3 0 0]
How can I do this? Matlab experts please help me.

There you go. Whole matrix, no loops, works even for non-contiguous zeros:
A = [1 1 1 1 1; 0 1 1 1 2; 0 0 1 1 3];
At = A.'; %// It's easier to work with the transpose
[~, rows] = sort(At~=0,'descend'); %// This is the important part.
%// It sends the zeros to the end of each column
cols = repmat(1:size(At,2),size(At,1),1);
ind = sub2ind(size(At),rows(:),cols(:));
sol = repmat(NaN,size(At,1),size(At,2));
sol(:) = At(ind);
sol = sol.'; %'// undo transpose
As usual, for Matlab versions that do not support the ~ symbol on function return, change ~ by a dummy variable, for example:
[nada, rows] = sort(At~=0,'descend'); %// This is the important part.

A more generic example:
A = [1 3 0 1 1;
0 1 1 1 2;
0 0 1 1 3]
% Sort columns directly
[~,srtcol] = sort(A == 0,2);
% Sorted positions
sz = size(A);
pos = bsxfun(#plus, (srtcol-1)*sz(1), (1:sz(1))'); % or use sub2ind
The result
B = A(pos)
B =
1 3 1 1 0
1 1 1 2 0
1 1 3 0 0

there are many ways to do this. one fast way can be easily like this:
a = [1 2 3 4 0 5 7 0];
idx=(find(a==0));
idx =
5 8
b=a; % save a new copy of the vector
b(idx)=[]; % remove zero elements
b =
1 2 3 4 5 7
c=[b zeros(size(idx))]
c =
1 2 3 4 5 7 0 0
You may modify this code as well.

If your zeros are always together, you could use the circshift command. This shifts values in an array by a specified number of places, and wraps values that run off the edge over to the other side. It looks like you would need to do this separately for each row in A, so in your example above you could try:
A(2,:) = circshift(A(2,:), [1 -1]); % shift the second row one to the left with wrapping
A(3,:) = circshift(A(3,:), [1 -2]); % shift the third row two to the left with wrapping
In general, if your zeros are always at the front of the row in A, you could try something like:
for ii = 1:size(A,1) % iterate over rows in A
numShift = numel(find(A(ii,:) == 0)); % assuming zeros at the front of the row, this is how many times we have to shift the row.
A(ii,:) = circshift(A(ii,:), [1 -numShift]); % shift it
end

Try this (just a fast hack):
for row_k = 1:size(A, 1)
[A_sorted, A_sortmap] = sort(A(row_k, :) == 0, 'ascend');
% update row in A:
A(row_k, :) = A(row_k, A_sortmap);
end
Now optimized for versions of MATLAB not supporting ~ as garbage lhs-identifier.

#LuisMendo's answer is inspiring in its elegance, but I couldn't get it to work (perhaps a matlab version thing). The following (based on his answer) worked for me:
Aaux = fliplr(reshape([1:numel(A)],size(A)));
Aaux(find(A==0))=0;
[Asort iso]=sort(Aaux.',1,'descend');
iso = iso + repmat([0:size(A,1)-1]*size(A,2),size(A,2),1);
A=A.';
A(iso).'

I've also asked this question and got a super elegant answer (non of above answers is same) here:
Optimize deleting matrix leading zeros in MATLAB

Related

Replace specific matrix position with array value without using for loop in MATLAB

can I know how can I replace values in specific matrix position without using for loop in MATLAB? I initialize matrix a that I would like to replace its value on specified row and column for each no. This has to be done a few time within num for loop. The num for loop is important here because I would want the update the value in the original code.
The real code is more complicated, I am simplifying the code for this question.
I have the code as follow:
a = zeros(2,10,15);
for num = 1:10
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
for no = 1:15
a(b(no),d(no),no) = c(1,no,:)
end
end
A sample output for, say no 13 is as follows:
a(:,:,13) =
Columns 1 through 8
0 0 0 0 0 7.3160 0 0
0 0 0 0 0 0 0 0
Columns 9 through 10
0 0
0 0
Thank you so much for any help I could get.
It can be done using sub2ind, which casts the subs to a linear index.
Following your vague variable names, it would look like this (omitting the useless loop over num):
a = zeros(2,10,15);
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
% // we vectorize the loop over no:
no = 1:15;
a(sub2ind(size(a), b, d, no)) = c;
Apart from the sub2ind based approach as suggested in Nras's solution, you can use a "raw version" of sub2ind to reduce a function call if performance is very critical. The related benchmarks comparing sub2ind and it's raw version is listed in another solution. Here's the implementation to solve your case -
no = 1:15
a = zeros(2,10,15);
[m,n,r] = size(a)
a((no-1)*m*n + (d-1)*m + b) = c
Also for pre-allocation, you can use a much faster approach as listed in Undocumented MATLAB blog post on Preallocation performance with -
a(2,10,15) = 0;
The function sub2ind is your friend here:
a = zeros(2,10,15);
x = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
y = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
z = 1:15;
dat = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
inds = sub2ind(size(a), x, y, z);
a(inds) = dat;
Matlab provides a function 'sub2ind' may do what you expected.
with variable as the same you posted:
idx = sub2ind(size(a),b,d,[1:15]); % return the index of row a column b and page [1:15]
a(idx) = c;

Row-by-row comparison in MATLAB

I want to compare every row of a matrix with its every other row, element by element wise, using MATLAB. If two of the entries match, the result will be stored as 1, and if they don't match, it will be 0. This will give a symmetric matrix consisting of 0s and 1s.
For example, let A = [4 6 7 9 5; 2 6 9 9 1]
Then, the result expected is [1 1 1 1 1; 0 1 0 1 0; 0 1 0 1 0; 1 1 1 1 1]
The code I am using is (for a 1000*1000 random matrix):
A = randi(50,1000,1000);
B = zeros(1000000,1000);
D = zeros(1000000,1);
c=0;
for i=1:1000
for k=1:1000
for j=1:1000
if A(i,j)==A(k,j)
B(k+c,j)=1;
else
B(k+c,j)=0;
end
end
end
c=c+1000;
end
for l=1:1000000
D(l)=0;
for m=1:1000
D(l)=D(l)+(B(l,m)/(1000));
end
end
E=reshape(D,1000,1000);
This goes out of memory. Could anyone please suggest a solution or a more efficient code?
you can try row by row comparison directly as taking a complete row array and comparing with the other row array.
For example,
let
A = [4 6 7 9 5; 2 6 9 9 1];
nA = length(A(:,1));
finalMat = [];
for i = 1:nA
matRow = ones(nA,1)*A(i,:); % create a matrix size of A consists of same row elements
finalMat = [finalMat;matRow == A];
end
see if it is okay for you application.
You can use permute to align dimensions apprpriately and then bsxfun for the comparisons:
reshape(bsxfun(#eq, permute(A, [1 3 2]), permute(A, [3 1 2])), [], size(A,2))

cumsum of values for same timeunit

i have the following vectors:
A=[1 0 1 0 0 1 0 1 0 0];
B=[1 2 3 4 5 6 7 8 9 10];
in this case A represents a time vector, where the 1s signal the beginning of one time unit.
now i want to add up all the values in B which correspond to a time unit with the same length of 3 steps.
So in this example this would mean the 3rd, 4th and 5th value and the 8th, 9th and 10th value of B should be summed cause these are in a time unit of length 3.
B_result=[12 27];
i know cumsum() is the command for this but i dont know how to say that only these specific values depending on the time indices of A should be summed.
can you help me?
thanks alot
You can use cumsum alongside accumarray and hist:
csa = cumsum(A); %// from begining og unit to unit indices
n = hist(csa, 1:max(csa)); %// count num of steps in each unit
B_result = accumarray( csa', B' ); %// accumulate B into different time units
B_result(n~=3) = []; %// discard all time units that do not have 3 steps
For a simpler pattern matching, you can use strfind:
loc = strfind([A,1],[1 0 0 1]); %// add the 1 at the end of A and the pattern to avoid longer intervals
idx = bsxfun(#plus,loc,(0:2)'); %'// get the indices that need to be summed
result = sum(B(idx),1); %// obtain the result
N = 3; %// We want to detect a one followed by exactly N-1 zeros. Call that
%// sequence an "interesting part"
ind = find([A 1]); %// find ones. Append a last one to detect a possible
%// interesting part at the end.
ind = ind(diff(ind)==N); %// index of beginning of interesting parts
cs = cumsum(B); %// accumulate values
B_result = cs(ind+N-1)-cs(ind-1); %// use index to build result
A more generic application of Jonas' Idea:
A = [1 0 1 0 0 1 0 1 0 0 0 0 1];
B = [1 2 3 4 5 6 7 8 9 10 11 12];
n = 3;
result = arrayfun(#(x) sum( B(x:x+n-1) ), strfind([A,1],num2str(10^n+1)-48))
or use cumsum instead of sum, I was not sure what you actually want:
result = arrayfun(#(x) cumsum( B(x:x+n-1) ), ...
strfind( [A,1],num2str(10^n+1)-48 ) ,'uni',0)
%optional:
result = cell2mat(result')

How to add boundary to a matrix

I am quite new to MATLAB. I would like to know how can I transfer the matrix A to matrix B as shown below?
A = 1 2
3 4
5 6
B=0 0 0 0
1 1 2 1
1 3 4 1
1 5 6 1
0 0 0 0
Essentially I would like to add a boundary to A.
Thank you!
padarray implementation -
%// pad ones on left-right and then pad zeros on top-bottom
B = padarray(padarray(A,[0 1],1),[1 0],0)
If I understand your question correctly, you wish to insert a 1 element boundary surrounding the matrix. In that case, try something like this:
A = [1 2; 3 4; 5 6];
[rows,cols] = size(A);
B = zeros(rows+2, cols+2);
B(2:end-1,[1 end]) = 1;
B(2:end-1,2:end-1) = A;
However, you can also use padarray like what #Divakar has suggested. Much more elegant!

How to vectorize row-wise diagonalization of a matrix

I have an n-by-m matrix that I want to convert to a mn-by-m matrix, with each m-by-m block of the result containing the diagonal of each row.
For example, if the input is:
[1 2; 3 4; 5 6]
the output should be:
[1 0; 0 2; 3 0; 0 4; 5 0; 0 6]
Of course, I don't want to assemble the matrix step by step myself with a for loop.
Is there a vectorized and simple way to achieve this?
For a vectorized way to do this, create the linear indices of the diagonal elements into the resulting matrix, and assign directly.
%# create some input data
inArray = [10 11;12 13;14 15];
%# make the index array
[nr,nc]=size(inArray);
idxArray = reshape(1:nr*nc,nc,nr)';
idxArray = bsxfun(#plus,idxArray,0:nr*nc:nr*nc^2-1);
%# create output
out = zeros(nr*nc,nc);
out(idxArray) = inArray(:);
out =
10 0
0 11
12 0
0 13
14 0
0 15
Here's a simple vectorized solution, assuming X is the input matrix:
Y = repmat(eye(size(X, 2)), size(X, 1), 1);
Y(find(Y)) = X;
Another alternative is to use sparse, and this can be written as a neat one-liner:
Y = full(sparse(1:numel(X), repmat(1:size(X, 2), 1, size(X, 1)), X'));
The easiest way I see to do this is actually quite simple, using simple index referencing and the reshape function:
I = [1 2; 3 4; 5 6];
J(:,[1,4]) = I;
K = reshape(J',2,6)';
If you examine J, it looks like this:
J =
1 0 0 2
3 0 0 4
5 0 0 6
Matrix K is just what wanted:
K =
1 0
0 2
3 0
0 4
5 0
0 6
As Eitan T has noted in the comments, the above is specific to the example, and doesn't cover the general solution. So below is the general solution, with m and n as described in the question.
J(:,1:(m+1):m^2) = I;
K=reshape(J',m,m*n)';
If you want to test it to see it working, just use
I=reshape(1:(m*n),m,n)';
Note: if J already exists, this can cause problems. In this case, you need to also use
J=zeros(n,m^2);
It may not be the most computationally efficient solution, but here's a 1-liner using kron:
A = [1 2; 3 4; 5 6];
B = diag(reshape(A', 6, 1) * kron(ones(3, 1), eye(2))
% B =
% 1 0
% 0 2
% 3 0
% 0 4
% 5 0
% 0 6
This can be generalized if A is n x m:
diag(reshape(A.', n*m, 1)) * kron(ones(n,1), eye(m))