MATLAB - Inserting zero rows and columns into matrix - matlab

I have written some code that compresses a matrix to remove zero columns and rows, but I can't work out how to reconstruct the original matrix.
Say I have a matrix:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0 ]
Here rows/columns 3 and 6 are empty, so my compression function will give the output:
A_dash = [ 0 3 2 1 6
3 0 4 8 5
2 4 0 2 1
1 8 2 0 7
6 5 1 7 0 ]
A_map = [ 1 2 4 5 7]
Where A_map is a vector mapping the indicies of the rows/columns of A_dash to A. This means that if A_map(3) = 4, then row/column 4 of A is the same as row/column 3 of A_dash - ie. a row/column of zeroes must be inserted between columns/rows 2 and 3 in A_dash
What is the easiest way people can suggest for me to recreate matrix A from A_dash, using the information in A_map?
Here is what I have got so far:
% orig_size is original number of columns/rows
c_count = size(A_dash,1);
A = zeros(c_count, orig_size); % c_count rows to avoid dimension mismatch
for ii = 1:c_count
A(:,A_map(ii)) == A_dash(:,ii);
end
This gives me the right result column-wise:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
2 4 0 0 2 0 1
1 8 0 2 0 0 7
6 5 0 1 7 0 0 ]
However, I'm not sure how i should go about inserting the rows, i suppose i could copy the first 1:i rows into one matrix, i:end rows to a second matrix and concatenate those with a zero row in between, but that feels like a bit of a
clunky solution, and probably not very efficient for large sized matrices..
Otherwise, is there a better way that people can suggest I store the map information? I was thinking instead of storing the mapping between column/row indices, that I just store the indices of the zero columns/rows and then insert columns/rows of zeros where appropriate. Would this be a better way?

You've got the indices of the valid rows/columns. Now all you've got to do is put them in a new matrix of zeros the same size as A:
B=zeros(size(A));
B(A_map,A_map)=A_dash
B =
0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0
Just to check...
>> A==B
ans =
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
A and B are equal everywhere, so we've reconstructed A.

Related

How to normalize matrix setting 0 for minimum values and 1 for maximum values?

I need to transform a neural network output matrix with size 2 X N in zeros and ones, where 0 will represent the minimum value of the column and 1 contrariwise. This will be necessary in order to calculate the confusion matrix.
For example, consider this matrix 2 X 8:
2 33 4 5 6 7 8 9
1 44 5 4 7 5 2 1
I need to get this result:
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
How can I do this in MATLAB without for loops? Thanks in advance.
>> d = [ 2 33 4 5 6 7 8 9;
1 44 5 4 7 5 2 1];
>> bsxfun(#rdivide, bsxfun(#minus, d, min(d)), max(d) - min(d))
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
The bsxfun function is necessary to broadcast the minus and division operations to matrices of different dimensions (min and max have only 1 row each).
Other solution is the following (works only for 2 rows):
>> [d(1,:) > d(2,:); d(1,:) < d(2,:)]
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
If it's just 2xN, then this will work:
floor(A./[max(A); max(A)])
In general:
floor(A./repmat(max(A),size(A,1),1))

Generating a matrix containing 3 numbers

I am trying to create an 8x8 matrix containing 0s, 1s and 2s. Each row and each column should contain two 0s, three 1s and three 2s.
Previously I have used the below to generate an example containing only 1s and 0s.
output = zeros(8, 8);
for i=1:8
tmp = (1:8) + (i);
tmp = rem(tmp, 4);
output(i,:) = tmp;
output(i,:) = tmp > 0;
end
output =
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
However I would now like something similar to the following:
output =
1 1 0 1 2 2 0 2
1 0 1 2 2 0 2 1
0 1 2 2 0 2 1 1
1 2 2 0 2 1 1 0
2 2 0 2 1 1 0 1
2 0 2 1 1 0 1 2
0 2 1 1 0 1 2 2
2 1 1 0 1 2 2 0
Thanks for your help.
What you have in your example is a Hankel matrix so you could use the hankel function
c = [1 1 0 1 2 2 0 2];
k = [2 1 1 0 1 2 2 0];
A = hankel(c,k)
where c is the first column of the output matrix and k is the last row.
Making your output matrix a Hankel matrix is a good idea (based on your requirements) as it will enforce the row and column frequency counts for each value. You would not necessarily get this just by creating rows that are random permutations of a base row (using randperm for example) as duplicate rows would be possible which would break your column requirements.
As an example, if you want random c with fixed numbers of specific elements, you can randomly permute a base vector containing the required values and frequencies - as per your requirement this would be
c = [0 0 1 1 1 2 2 2];
index = randperm(numel(c));
c = c(index);
c =
0 2 0 2 2 1 1 1
To get the square Hankel structure then choose k to be the next cyclic permutation of c
k = circshift(c',1)'
k =
1 0 2 0 2 2 1 1
and just call hankel with these as mentioned above
A = hankel(c,k)
A =
0 2 0 2 2 1 1 1
2 0 2 2 1 1 1 0
0 2 2 1 1 1 0 2
2 2 1 1 1 0 2 0
2 1 1 1 0 2 0 2
1 1 1 0 2 0 2 2
1 1 0 2 0 2 2 1
1 0 2 0 2 2 1 1
The above output is based on what I got on my machine based on the output from randperm.
Any output matrix generated using the above will meet your requirements specified in the question.

Calculating a partial cumulative sum for a square matrix

Let's say I have a square matrix M:
M = [0 0 0 0 0 1 9; 0 0 0 0 0 4 4; 0 0 1 1 6 1 1; 0 1 2 9 2 1 0; 2 1 8 3 2 0 0; 0 8 1 1 0 0 0; 14 2 0 1 0 0 0]
0 0 0 0 0 1 9
0 0 0 0 0 4 4
0 0 1 1 6 1 1
M = 0 1 2 9 2 1 0
2 1 8 3 2 0 0
0 8 1 1 0 0 0
14 2 0 1 0 0 0
Now I'd like to calculate two different cumulative sums: One that goes from the top of each column to the element of the column, that is a diagonal element of the matrix, and one that goes from the bottom of the column to the same diagonal element.
The resulting matrix M'should therefore be the following:
0 0 0 0 0 1 9
0 0 0 0 0 4 5
0 0 1 1 6 2 1
M' = 0 1 3 9 4 1 0
2 2 8 5 2 0 0
2 8 1 2 0 0 0
14 2 0 1 0 0 0
I hope the explanation of what I'm trying to achieve is comprehensible enough. Since my matrices are much larger than the one in this example, the calculation should be efficient as well...but so far I couldn't even figure out how to calculate it "inefficiently".
In one line using some flipping and the upper triangular function triu:
Mp = fliplr(triu(fliplr(cumsum(M)),1)) ...
+flipud(triu(cumsum(flipud(M)),1)) ...
+flipud(diag(diag(flipud(M))));
The following will do the job:
Mnew = fliplr(triu(cumsum(triu(fliplr(M)),1))) + flipud(triu(cumsum(triu(flipud(M)),1)));
Mnew = Mnew - fliplr(diag(diag(fliplr(Mnew)))) + fliplr(diag(diag(fliplr(M))));
But is it the fastest method?
I think logical indexing might get you there faster

Copying part of a matrix to another empty matrix with the same indices

I'm trying to copy part of a matrix (matrix 1) in matlab to another empty matrix of zeros (matrix 2) so that the section I copy from matrix 1 has the same indices in matrix 2, e.g.
Matrix 1 (mat1):
0 3 0 0 2 4 1 2 6
1 3 4 2 0 0 0 2 0
0 2 6 1 3 6 6 1 1
0 0 0 2 1 3 3 1 0
1 4 5 2 3 3 0 0 1
Matrix 2 (mat2) desired output:
0 0 0 0 0 0 0 0 0
0 0 4 2 0 0 0 0 0
0 0 6 1 3 6 6 0 0
0 0 0 2 1 3 3 0 0
0 0 0 0 0 0 0 0 0
I've tried something like
mat2([2:4],[3:7]) = mat1([2:4],[3:7])
but of course it doesn't work... any ideas of an efficient way to do this? I couldn't find another thread to help with this problem.
Thanks!
It does work. You just need to create mat2 first:
mat2 = zeros(size(mat1));
mat2(2:4, 3:7) = mat1(2:4, 3:7);
Note that you don't need the square brackets on those ranges.
Do this:
mat2 = zeros(size(mat1));
Before copying over.

Matrix in matlab?

I am working on Connected Components labeling, and my matrix is:
1 1 0 2 2 2 0 3
1 1 0 2 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
and now, i want to do the second scan over it, for this i have made following code:
for i=1:1:r
for j=1:1:c
if (bw(i,j)>=1)
if (i-1>0 & i+1<=r)
% if ( bw(i,j)~= bw(i-1,j) | bw(i,j)~= bw(i+1,j))
if ( (bw(i,j)~= bw(i-1,j) & bw(i-1,j)>0))
bw(i,j)= min (bw(i-1,j),bw(i,j))
elseif ((bw(i,j)~= bw(i+1,j) & bw(i+1,j)>0))
bw(i,j) = min(bw(i+1,j),bw(i,j));
end
end
if (j-1>0 & j+1<=c)
if ( (bw(i,j)~= bw(i,j-1) & bw(i,j-1)>0))
bw(i,j) = min (bw(i,j-1),bw(i,j));
elseif((bw(i,j)~= bw(i,j+1) & bw(i,j+1)>0))
bw(i,j) = min (bw(i,j+1),bw(i,j)) ;
end
end
end
end
end
disp(bw);
but the problem is, when i run this code, i get the following output
1 1 0 2 2 2 0 3
1 1 0 1 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
only one value changes (2nd row, 4 col) in my result, whereas, I want:
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
could somebody please help? where am i making a mistake?
Nice solution by Jonas. As I was a bit into coding when I saw it added, I thought I'd show you my solution as well. Hopefully it is a bit more similar to your original code.
One of the mistakes you made was assuming this connection could be done in just one pass. Generaly it can not, as detections of some of the "snake elements" are dependent on the way you iterate throught the matrix. Becuase of this, I added an outer while loop.
bw =[ 1 1 0 2 2 2 0 3;
1 1 0 2 0 2 0 3;
1 1 1 1 0 0 0 3;
0 0 0 0 0 0 0 3;
4 4 4 4 0 5 0 3;
0 0 0 4 0 5 0 3;
6 6 0 4 0 0 0 3;
6 6 0 4 0 7 7 7 ];
%Set up matrix
[r,c] = size(bw);
%Zero pad border
bwZ = [zeros(1,c+2);[zeros(r,1) bw zeros(r,1)];zeros(1,c+2)];
%Iterate over all elements within zero padded border
done=0;%Done flag
cc=1; %Infinite loop protection
while not(done) && cc<r*c
done=1;cc=cc+1;
for i=2:r+1
for j=2:c+1
%Point should be evaluated
p = bwZ(i,j);
if p >= 1
%Pick out elements around active elements
if bwZ(i-1,j)==0;ue=inf;else ue = bwZ(i-1,j); end;%Up element
if bwZ(i+1,j)==0;de=inf;else de = bwZ(i+1,j); end;%Down element
if bwZ(i,j-1)==0;le=inf;else le = bwZ(i,j-1); end;%Left element
if bwZ(i,j+1)==0;re=inf;else re = bwZ(i,j+1); end;%Right element
bwZ(i,j) = min([ue de le re]);
%Set flag, if something has changed
if bwZ(i,j) ~= p
done = 0;
end
end
end
end
end
%Remove zero padding
bw = bwZ(2:end-1,2:end-1)
Output:
bw =
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
If the numbers don't need to be preserved, you can simply call bwlabel on your original image:
newImage = bwlabel(originalImage>0);
EDIT
Here's another version. It checks each connected component to see whether there's another connected component touching it. If yes, that connected component is relabelled.
%# nCC: number of connected components
nCC = max(originalImage(:));
for cc = 1:nCC
%# check whether the component exists
myCC = originalImage==cc;
if any(any(myCC))
%# create a mask to check for neighbors
%# by creating a border of 1 pixel
%# around the original label
msk = imdilate(myCC,true(3)) & ~myCC;
%# read all the pixel values under the mask
neighbours = originalImage(msk);
%# we're not interested in zeros, remove them
neighbours = neighbours(neighbours > 0);
if ~isempty(neighbours)
%# set the label of all neighbours to cc
originalImage( ismember(originalImage,neighbours) ) = cc;
end
end
end