reducing number of 0 rows in matrix using matlab - matlab

I have written following program in Matlab
clc;
clear all;
close all;
matrix = [ 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0;
1 1 1 1 1 1 1 1;
1 1 1 0 0 1 1 1;
0 0 1 1 1 1 1 1;
0 0 0 0 0 0 0 0;
0 0 1 1 1 1 1 1;
0 0 0 0 0 0 0 0;
0 0 1 1 1 1 1 1;
1 0 0 1 1 1 1 1;
1 1 1 0 0 0 1 1];
[row,column] = size(matrix);
for i = 1:row
if matrix(i,:) == 0
matrix(i,:) = [];
end
end
disp(matrix);
what I expect is the 0 rows will be removed in the output matrix.
Where as I am getting error
Index in position 1 exceeds array bounds (must not exceed
8).
Error in Untitled (line 21)
if matrix(i,:) == 0
>>

When you delete a row from the matrix, it no longer has row rows, but row-1. Since you cannot adjust the limits of the loop, you will index out of range. Also, you will skip the row i+1 if you delete row i.
Instead, first find all the rows that need to be deleted, then delete them all at once:
index = all(matrix == 0, 2);
matrix(index,:) = [];
You can of course write that in a single line of code.

As #Cris said, the reason that you're getting an out of bounds error is that you're deleting from the beginning and shrinking the matrix before you check the end.
Another way to fix this is to simply reverse the loop, starting from the end and working toward the beginning. That way, even if you remove a row, its index will never be checked again.
for i = row:-1:1 % loop from last row to first
if matrix(i,:) == 0
matrix(i,:) = [];
end
end

Related

Spread elements of rows to multiple rows

I am working on graph theory using adjacency matrix, I want to split the edges between multiple nodes for instance I have the following initial adjacency matrix :
a= [ 0 2 3;
2 0 1;
3 1 0]
From that matrix its clear that we have 3 nodes, Now I want to split the aforementioned rows (edges) into new random nodes between (1-3) :
split= randi([1 3],1,length(A));
split = [ 2 2 1]
I know now that I need to split the elements of the first row into two rows, the elements of the second rows also into two rows, while the elements of th third row will remain as is, and I'll have new matrix with size 5X5 as following:
A = [0 0 2 0 3;
0 0 0 0 0;
2 0 0 0 1;
0 0 0 0 0;
3 0 1 0 0]
What I want to do is to split the non-zero elements in the first row between this row and the second row, and the third with the fourth, so my matrix will look like:
An = [0 0 2 0 0;
0 0 0 0 3;
2 0 0 0 0;
0 0 0 0 1;
0 3 0 1 0]
It's not fully clear to me what's the initial point, the prerequisites and conditions. I assume that every second row/column is a row/column of zeros. I furthermore assume that the non-zero rows/columns have exactly two non-zero values whereas the second value should be moved to the next row/column. For this, I'd suggest:
A = [0 0 2 0 3 ; 0 0 0 0 0 ; 2 0 0 0 1 ; 0 0 0 0 0 ; 3 0 1 0 0];
for n = 1:2
if n==2
A = A';
end % if
for k = 1:2:size(A,1)-1
m = find(A(k,:));
A(k+(0:1),m(end)) = flipud(A(k+(0:1),m(end)));
end % for
if n==2
A = A';
end % if
end % for
A
A =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0
Here An is directly generated from a without creating A:
a = [ 0 2 3;
2 0 1;
3 1 0];
split = [ 2 2 1];
L = length(a);
cum = cumsum([1 split(1:end-1)]);
%ro = rot90(split - (0:L-1).' + cum-1, -1); %MATLAB R2016b
ro = rot90(bsxfun(#minus,split + cum-1 , (0:L-1).') , -1);
co = repmat(cum, L, 1);
idx = triu(true(L), 1);
N = sum(split);
An = zeros(N);
sub = sub2ind([N,N], ro(idx), co(idx));
An(sub) = a(idx);
An = An + An.'
An =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0

Matlab - Shuffling matrix values based on some conditions

I have the following two matrices which are outputs of a procedure. The size of the matrices may change but both matrices will always be the same size: size(TwoHopMat_1) == size(Final_matrix)
Example:
TwoHopMat_1 =
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0
Final_matrix =
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1
Now I need to shuffle the final_matrix such that i meet the following conditions after shuffling:
Every column should have a minimum of one 1s
If i have a 1 in a particular position of TwoHopMat_1 then that particular position should not have 1 after shuffling.
The conditions should work even if we give matrices of size 100x100.
first step: set one element of each column of the result matrix ,that is not 1 in Final_matrix ,to 1
second step: then remaining ones randomly inserted into positions of the result matrix that are not 1 in Final_matrix and are not 1 in the first step result
TwoHopMat_1=[...
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0];
Final_matrix=[...
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1];
[row col] = size(Final_matrix);
result = zeros(row ,col);
%condition 1 & 2 :
notTwoHop = ~TwoHopMat_1;
s= sum(notTwoHop,1);
c= [0 cumsum(s(1:end - 1))];
f= find(notTwoHop);
r = floor(rand(1, col) .* s) + 1;
i = c + r;
result(f(i)) = 1;
%insert remaining ones randomly into the result
f= find(~(result | TwoHopMat_1));
i = randperm(numel(f), sum(Final_matrix(:))-col);
result(f(i)) =1
A possible solution:
function [result_matrix] = shuffle_matrix(TwoHopMat_1, Final_matrix)
% Condition number 2
ones_mat = ones(size(TwoHopMat_1));
temp_mat = abs(TwoHopMat_1 - ones_mat);
% Condition number 1
ones_to_remove = abs(sum(sum(temp_mat)) - sum(sum(Final_matrix)));
while ones_to_remove > 0
% Random matrix entry
i = floor((size(Final_matrix, 1) * rand())) + 1;
j = floor((size(Final_matrix, 2) * rand())) + 1;
if temp_mat(i,j) == 1
temp_mat(i,j) = 0;
ones_to_remove = ones_to_remove - 1;
end
end
result_matrix = temp_mat;
end
Note: this solution uses brute force.

How to replace duplicate elements as 0 in column matrix in matlab

I need to replace the repeated elements in column of a matrix as 0's and delete the rows which has all 0's. If my matrix is like this means.
Input =
1 0 0 1
0 1 0 1
0 0 1 1
1 1 1 1
My expected output should be like this
Output =
1 0 0 1
0 1 0 0
0 0 1 0
0 0 0 0 ---> this row should be get deleted in this case
This doesn't work for my problem
c = [ 1 1 0 1 0 1 1 1 0 1 1 0];
[c, ic] = unique(a, 'first');
c(~ismember(1:length(a),ic)) = 0;
You can use logical indexing and cumsum:
A = [1 0 0 1;
0 1 0 1;
0 0 1 1;
1 1 1 1];
ind = cumsum(A); %cumulative sum (by column)
A(ind>1) = 0;
A(sum(A')==0,:)=[]

Matrix of 0s and 1s Where Assignment in Subsequent Rows are Contingent on the Previous Row

I'd like to create a Matrix in MATLAB where:
The First row consists of a random arrangement of 0s and 1s, split evenly (i.e. 50-50).
The Second row randomly assigns zeros to 50% of the 0s and 1s in the first row, and ones to the remaining 50%.
The Third row randomly assigns zeros to 50% of the 0s and 1s in the second row, and ones to the remaining 50%.
Non-randomized Example:
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
Any suggestions?
A solution based on checking whether numbers are bigger or smaller than the median. As long as the number of columns tested is even, exactly half of a set of random doubles will be bigger than the median, and half will be smaller. This guarantees that there's exactly 50% of bits get flipped.
nRows = 3;
nCols = 16; %# divisible by 4
%# seed the array
%# assume that the numbers in each row are unique (very, very likely)
array = rand(nRows,nCols);
out = false(nRows,nCols);
%# first row is special
out(1,:) = array(1,:) > median(array(1,:));
%# for the rest of the row, check median for the zeros/ones in the previous row
for iRow = 2:nRows
zeroIdx = out(iRow-1,:) == 0;
%# > or < do not matter, both will replace zeros/ones
%# and replace with exactly half zeros and half ones
out(iRow,zeroIdx) = array(iRow,zeroIdx) > median(array(iRow,zeroIdx));
out(iRow,~zeroIdx) = array(iRow,~zeroIdx) > median(array(iRow,~zeroIdx));
end
I'd offer a short bsxfun solution:
%// number of divisions
n = 4;
%// unshuffled matrix like in your example
unshuffled = bsxfun(#(a,b) mod(a,2*b) > b-1, meshgrid(1:n^2,1:n) - 1, (2.^((n-1):-1:0)).') %'
%// shuffle columns
shuffled = unshuffled(:,randperm(n^2))
unshuffled =
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
shuffled =
1 0 1 1 0 1 0 1 1 1 1 0 0 0 0 0
1 1 1 0 0 1 1 0 1 0 0 0 1 0 1 0
1 0 0 1 0 0 0 0 1 1 0 1 1 0 1 1
1 1 1 1 0 0 0 0 0 0 1 0 0 1 1 1
First you need to create the unshuffled matrix, which can be done by comparing the matrix generated by meshgrid(1:n^2,1:n) with a row dependent modulus. Finally you just need to shuffle the columns.
If you have the Statistics Toolbox, you can do it very easily with randsample:
M = 3; %// number of rows
N = 16; %// number of columns. Should be multiple of 4, according to problem definition
result = zeros(M,N); %// preallocate and initiallize to zeros
result(1, randsample(1:N,N/2)) = 1; %// first row: half values set to one, half to zero
for m = 2:M
result(m, :) = result(m-1, :); %// initiallize row m equal to row m-1
result(m, randsample(find(result(m-1,:)), N/4)) = 0; %// change half of ones
result(m, randsample(find(~result(m-1,:)), N/4)) = 1; %// change half of zeros
end
Example result:
result =
0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1
1 1 0 0 0 1 1 1 0 1 0 1 0 0 0 1
1 0 0 0 1 0 0 1 0 1 1 0 1 1 0 1
A solution using randperm:
nrows = 3;
ncols = 16;
M = zeros(nrows,ncols);
%// seed the first row
M(1,1:ncols/2) = 1;
M(1,:) = M(1,randperm(ncols));
for r = 2:nrows
%// Find ncols/4 random between 1 and ncols/2. These will be used to index half of the previous rows 1 elements and set them to one
idx = randperm(ncols/2);
idx1 = idx(1:ncols/4);
%// Do the same thing again, but this time it will be used for the 0 elements of the previous row
idx = randperm(ncols/2);
idx0 = idx(1:ncols/4);
idx_prev1 = find(M(r-1,:)); %// Find where the 1 elements were in the last row
idx_prev0 = find(~M(r-1,:)); %// Find where the 0 elements were in the last row
M(r,idx_prev1(idx1))=1; %// Set half of the previous rows 1 elements in this row to 1
M(r,idx_prev0(idx0))=1; %// Set half of the previous rows 0 elements in this row to 1
end

Using find function on columns and rows in matlab

I am having some problems with the find function in MATLAB. I have a matrix consisting of zeros and ones (representing the geometry of a structural element), where material is present when the matrix element = 1, and where no material is present when the matrix element = 0. The matrix may have the general form shown below (it will update as the geometry is changed, but that isn't too important).
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 0 1 0 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
I'm trying to find the the rows and columns that are not continuously connected (i.e. where the row and columns are not all equal to 1 between the outer extents of the row or column) and then update them so they are all connected. I.e. the matrix above becomes:
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
The problem I am having is I want to be able to find the indices of the first and last element that is equal to 1 in each row (and column), which will then be used to update the geoemtry matrix.
Ideally, I want to represent these in vectors, so going across the columns, find the row number of the first element equal to 1 and store this in a vector called rowfirst.
I.e.:
rowfirst = zeros(1,numcols)
for i = 1:numcols % Going across the columns
rowfirst(i) = find(Geometry(i,1) == 1, 1,'first')
% Store values in vector called rowfirst
end
and the repeat this for the columns and to find the last elements in each row.
For some reason, I can't get the values to store properly in the vector, does anyone have an idea of where I'm going wrong?
Thanks in advance. Please let me know if that isn't clear, as I may not have explained the problem very well.
0) bwmorph(Geometry,'close') dose it all in one line. If the holes may be bigger, try bwmorph(Geometry,'close',Inf).
Regarding your attempt:
1) It should be Geometry(i,:) instead of Geometry(i,1).
2) Your real problem here is empty matrices. Actually, what do you want rowfirst(i) to be if there are no 1s in the i'th row?
Ok, I can spot two mistakes:
You should use an array as the first argument of find. So, if you want to find the row number of the first element of each column, then you should use find(Geometry(:, i), 1, 'first').
Find returns an empty array if the column contains only zeros. You should handle this case and decide what number you want to put into rownumber (e.g. you can put -1, to indicate that the corresponding column contains no non-zero elements).
Following the above, you can try this:
for i = 1:numcols
tmp = find(Geometry(:, i), 1, 'first');
if(tmp)
rowfirst(i) = tmp;
else
rowfirst(i) = -1;
end;
end;
I'm pretty sure there's a more efficient way of doing this, but if you replace your call to find with this, it should work ok:
find(Geometry(i,:), 1,'first')
(otherwise you're just looking at the first cell of the ith row. And the == 1 is useless, since find already returns only non-zero elements, and your matrix is binary)
Use the AccumArray() function to find the min and max col (row) number.
Imagine finding the last (first) row in each column that contains a NaN.
a = [1 nan nan nan ;
2 2 3 4;
3 nan 3 3;
4 nan 4 4]
This code gets the row indices for the last NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#max)
This code gets the row indices for the first NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#min)
Swap the row and col variables to scan row-wise instead of column-wise.
This answer inspired by Finding value and index of min value in a matrix, grouped by column values