Replace repeated value based on sequence size - Matlab - matlab

I have a 2D matrix composed of ones and zeros.
mat = [0 0 0 0 1 1 1 0 0
1 1 1 1 1 0 0 1 0
0 0 1 0 1 1 0 0 1];
I need to find all consecutive repetitions of ones in each row and replace all ones with zeros only when the sequence size is smaller than 5 (5 consecutive ones):
mat = [0 0 0 0 0 0 0 0 0
1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0];
Any suggestion on how to approach this problem would be very welcome.

You can use diff to find the start and end points of the runs of 1, and some logic based on that to zero out the runs which are too short. Please see the below code with associated comments
% Input matrix of 0s and 1s
mat = [0 0 0 0 1 1 1 0 0
1 1 1 1 1 0 0 1 0
0 0 1 0 1 1 0 0 1];
% Minimum run length of 1s to keep
N = 5;
% Get the start and end points of the runs of 1. Add in values from the
% original matrix to ensure that start and end points are always paired
d = [mat(:,1),diff(mat,1,2),-mat(:,end)];
% Find those start and end points. Use the transpose during the find to
% flip rows/cols and search row-wise relative to input matrix.
[cs,r] = find(d.'>0.5); % Start points
[ce,~] = find(d.'<-0.5); % End points
c = [cs, ce]; % Column number array for start/end
idx = diff(c,1,2) < N; % From column number, check run length vs N
% Loop over the runs which didn't satisfy the threshold and zero them
for ii = find(idx.')
mat(r(ii),c(ii,1):c(ii,2)-1) = 0;
end
If you want to throw legibility out of the window, this can be condensed for a slightly faster and denser version, based on the exact same logic:
[c,r] = find([mat(:,1),diff(mat,1,2),-mat(:,end)].'); % find run start/end points
for ii = 1:2:numel(c) % Loop over runs
if c(ii+1)-c(ii) < N % Check if run exceeds threshold length
mat(r(ii),c(ii):c(ii+1)-1) = 0; % Zero the run if not
end
end

The vectorized solution by #Wolfie is nice and concise, but a bit hard to understand and far from the wording of the problem. Here is a direct translation of the problem using loops. It has the advantage of being easier to understand and is slightly faster with less memory allocations, which means it will work for huge inputs.
[m,n] = size(mat);
for i = 1:m
j = 1;
while j <= n
seqSum = 1;
if mat(i,j) == 1
for k = j+1:n
if mat(i,k) == 1
seqSum = seqSum + 1;
else
break
end
end
if seqSum < 5
mat(i,j:j+seqSum-1) = 0;
end
end
j = j + seqSum;
end
end

Related

Gauss-Jordan elimination over GF(2)

I need to transform a parity-check matrix H (that only consists of ones and zeros) from a non-standard to a standard form, this is, express it as:
Hsys = [A | I]
H and Hsys share the same dimension: (n-k,n). I above corresponds to an identity matrix of dimension (n-k).
Gauss-Jordan elimination comes in handy to solve this problem. Matlab has an specific command, rref, for this purpose, however it is no longer valid while working over GF(2) as in our case. Glancing through the Internet I found in Github a potentially suitable solution to overcome this drawback. However it does not always work out.
I also tried doing HH = mod(rref(H),2), which did not work at all, as many of the output elements weren't binary.
Here below you may find three samples of non-standard parity check matrices in which Gauss-Jordan elimination (over GF(2)) can be applied. As there should always be a way to arrange any matrix to be systematic, I would need a method that works out with matrices of any dimension.
These first sample is taken from sid's post in Stackoverflow, not responded yet:
H=[1 0 1 1 0;
0 0 1 0 1;
1 0 0 1 0;
1 0 1 1 1];
H=[1 1 0 1 1 0 0 1 0 0;
0 1 1 0 1 1 1 0 0 0;
0 0 0 1 0 0 0 1 1 1;
1 1 0 0 0 1 1 0 1 0;
0 0 1 0 0 1 0 1 0 1];
The last one is a matrix of dimension (50x100) and can be found in this link to my Dropbox.
Edit on 21/06/2017
The solution proposed by #Jonas worked out in some cases, but not in most of them, as H matrix seems to be singular. Any other similar way to do this?
Thank you in advance, and best regards.
Here is how I'd do it (using Gauss-Jordan elimination):
H=[1 1 0 1 1 0 0 1 0 0;
0 1 1 0 1 1 1 0 0 0;
0 0 0 1 0 0 0 1 1 1;
1 1 0 0 0 1 1 0 1 0;
0 0 1 0 0 1 0 1 0 1];
rows = size(H, 1);
cols = size(H, 2);
r = 1;
for c = cols - rows + 1:cols
if H(r,c) == 0
% Swap needed
for r2 = r + 1:rows
if H(r2,c) ~= 0
tmp = H(r, :);
H(r, :) = H(r2, :);
H(r2, :) = tmp;
end
end
% Ups...
if H(r,c) == 0
error('H is singular');
end
end
% Forward substitute
for r2 = r + 1:rows
if H(r2, c) == 1
H(r2, :) = xor(H(r2, :), H(r, :));
end
end
% Back Substitution
for r2 = 1:r - 1
if H(r2, c) == 1
H(r2, :) = xor(H(r2, :), H(r, :));
end
end
% Next row
r = r + 1;
end
Let me know if this doesn't solve your issue.
I went through the same issue and #jonas code also produced mostly singular matrix error. you can try the following code which I found to be helpful when searching for the systematic form of H. Its also include the calculation of G.
% Gauss-Jordan elimination
swaps=zeros(m,2);
swaps_count=1;
n=size(H, 2);
m=size(H, 1);
j=1;
index=1;
while index<=m
i=index;
while (HH(i,j)==0)&(i<m)
i=i+1;
end
if HH(i,j)==1
temp=HH(i,:);
HH(i,:)=HH(index,:);
HH(index,:)=temp;
for i=1:m
if (index~=i)&(HH(i,j)==1)
HH(i,:)=mod(HH(i,:)+HH(index,:),2);
end
end
swaps(swaps_count,:)=[index j];
swaps_count=swaps_count+1;
index=index+1;
j=index;
else
j=j+1;
end
end
for i=1:swaps_count-1
temp=HH(:,swaps(i,1));
HH(:,swaps(i,1))=HH(:,swaps(i,2));
HH(:,swaps(i,2))=temp;
end
G=[(HH(:,m+1:n))' eye(n-m)];
for i=swaps_count-1:-1:1
temp=G(:,swaps(i,1));
G(:,swaps(i,1))=G(:,swaps(i,2));
G(:,swaps(i,2))=temp;
end
disp(sum(sum((mod(H*G',2)))));

How to find a non-zero number between two zeros in a cell array in matlab

I have a cell array (11000x500) with three different type of elements.
1) Non-zero doubles
2) zero
3) Empty cell
I would like to find all occurances of a non-zero number between two zeros.
E.g. A = {123 13232 132 0 56 0 12 0 0 [] [] []};
I need the following output
out = logical([0 0 0 0 1 0 1 0 0 0 0 0]);
I used cellfun and isequal like this
out = cellfun(#(c)(~isequal(c,0)), A);
and got the follwoing output
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
I need help to perform the next step where i can ignore the consecutive 1's and only take the '1's' between two 0's
Could someone please help me with this?
Thanks!
Here is a quick way to do it (and other manipulations binary data) using your out:
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
d = diff([out(1) out]); % find all switches between 1 to 0 or 0 to 1
len = 1:length(out); % make a list of all indices in 'out'
idx = [len(d~=0)-1 length(out)]; % the index of the end each group
counts = [idx(1) diff(idx)]; % the number of elements in the group
elements = out(idx); % the type of element (0 or 1)
singles = idx(counts==1 & elements==1)
and you will get:
singles =
5 7
from here you can continue and create the output as you need it:
out = false(size(out)); % create an output vector
out(singles) = true % fill with '1' by singles
and you get:
out =
0 0 0 0 1 0 1 0 0 0 0 0
You can use conv to find the elements with 0 neighbors (notice that the ~ has been removed from isequal):
out = cellfun(#(c)(isequal(c,0)), A); % find 0 elements
out = double(out); % cast to double for conv
% elements that have more than one 0 neighbor
between0 = conv(out, [1 -1 1], 'same') > 1;
between0 =
0 0 0 0 1 0 1 0 0 0 0 0
(Convolution kernel corrected to fix bug found by #TasosPapastylianou where 3 consecutive zeros would result in True.)
That's if you want a logical vector. If you want the indices, just add find:
between0 = find(conv(out, [1 -1 1], 'same') > 1);
between0 =
5 7
Another solution, this completely avoids your initial logical matrix though, I don't think you need it.
A = {123 13232 132 0 56 0 12 0 0 [] [] []};
N = length(A);
B = A; % helper array
for I = 1 : N
if isempty (B{I}), B{I} = nan; end; % convert empty cells to nans
end
B = [nan, B{:}, nan]; % pad, and collect into array
C = zeros (1, N); % preallocate your answer array
for I = 1 : N;
if ~any (isnan (B(I:I+2))) && isequal (logical (B(I:I+2)), logical ([0,1,0]))
C(I) = 1;
end
end
C = logical(C)
C =
0 0 0 0 1 0 1 0 0 0 0 0

How can I vectorise this loop in MATLAB

I have a loop that iterates over a matrix and sets all rows and columns with only one non-zero element to all zeroes.
so for example, it will transform this matrix:
A = [ 1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1 ]
to the matrix:
A' = [ 1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1 ]
row/column 2 of A only has 1 non zero element in it, so every element in row/column 2 is set to 0 in A'
(it is assumed that the matrices will always be diagonally symmetrical)
here is my non-vectorised code:
for ii = 1:length(A)
if nnz(A(ii,:)) == 1
A(ii,:) = 0;
A(:,ii) = 0;
end
end
Is there a more efficient way of writing this code in MATLAB?
EDIT:
I have been asked in the comments for some clarification, so I will oblige.
The purpose of this code is to remove edges from a graph that lead to a vertex of degree 1.
if A is the adjacency matrix representing a undirected graph G, then a row or column of that matrix which only has one non-zero element indicates that row/column represents a vertex of degree one, as it only has one edge incident to it.
My objective is to remove such edges from the graph, as these vertices will never be visited in a solution to the problem I am trying to solve, and reducing the graph will also reduce the size of the input to my search algorithm.
#TimeString, i understand that in the example you gave, recursively applying the algorithm to your matrix will result in a zero matrix, however the matrices that I am applying it to represent large, connected graphs, so there will never be a case like that. In response to your question as to why I only check for how many elements in a row, but the clear both columns and rows; this is because the matrix is always diagonally symmetrical, so i know that if something is true for a row, so it will be for the corresponding column..
so, just to clarify using another example:
I want to turn this graph G:
represented by matrix:
A = [ 0 1 1 0
1 0 1 0
1 1 0 1
0 0 1 0 ]
to this graph G':
represented by this matrix:
A' = [ 0 1 1 0
1 0 1 0
1 1 0 0
0 0 0 0 ]
(i realise that this matrix should actually be a 3x3 matrix because point D has been removed, but i already know how to shrink the matrix in this instance, my question is about efficiently setting columns/rows with only 1 non-zero element all to 0)
i hope that is a good enough clarification..
Not sure if it's really faster (depends on Matlab's JIT) but you can try the following:
To find out which columns (equivalently, rows, since the matrix is symmetric) have more than one non zero element use:
sum(A ~= 0) > 1
The ~= 0 is probably not needed in your case since the matrix consists of 1/0 elements only (graph edges if I understand correctly).
Transform the above into a diagonal matrix in order to eliminate unwanted columns:
D = diag(sum(A~=0) > 1)
And multiply with A from left to zero rows and from right to zero columns:
res = D * A * D
Thanks to nimrodm's suggestion of using sum(A ~= 0) instead of nnz, i managed to find a better solution than my original one
to clear the rows with one element i use:
A(sum(A ~= 0) == 1,:) = 0;
and then to clear columns with one element:
A(:,sum(A ~= 0) == 1) = 0;
for those of you who are interested, i did a 'tic-toc' comparison on a 1000 x 1000 matrix:
% establish matrix
A = magic(1000);
rem_rows = [200,555,950];
A(rem_rows,:) = 0;
A(:,rem_rows) = 0;
% insert single element into empty rows/columns
A(rem_rows,500) = 5;
A(500,rem_rows) = 5;
% testing original version
A_temp = A;
for test = 1
tic
for ii = 1:length(A_temp)
if nnz(A_temp(ii,:)) == 1
A_temp(ii,:) = 0;
A_temp(:,ii) = 0;
end
end
toc
end
Elapsed time is 0.041104 seconds.
% testing new version
A_temp = A;
for test = 1
tic
A_temp(sum(A_temp ~= 0) == 1,:) = 0;
A_temp(:,sum(A_temp ~= 0) == 1) = 0;
toc
end
Elapsed time is 0.010378 seconds
% testing matrix operations based solution suggested by nimrodm
A_temp = A;
for test = 1
tic
B = diag(sum(A_temp ~= 0) > 1);
res = B * A_temp * B;
toc
end
Elapsed time is 0.258799 seconds
so it appears that the single line version that I came up with, inspired by nimrodm's suggestion, is the fastest
thanks for all your help!
Bsxfuning it -
A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
Sample run -
>> A
A =
1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1
>> A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
A =
1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1

converting elements in a vector in matlab

For a project, I'm trying to find the first 1 of a series of ones in a vector. For example, I have as input:
x1=[1 0 0 1 1 1 0 1 0 1 0 0 1 1]
and I need as output:
Y1=[1 0 0 1 0 0 0 1 0 1 0 0 1 0]
So every time there is a 1 in the vector, all consequent ones need to be turned into zeroes.
I have the following code, but for some reason it just returns Y1 with exactly the same values as x1.
n=numel(x1);
Y1=zeros(n,1);
for i = 1:n
if x1(i) == 1
Y1(i)= 1;
for j = (i+1): n
if x1(j)== 1
Y1(j)=0;
elseif x1(j) == 0
Y1(j)=0;
i=j+1;
break
end
end
elseif x1(i) == 0
Y1(i)= 0;
end
end
Any help would be greatly appreciated.
Easy with diff. No loops needed.
Y1 = [ x1(1) diff(x1)==1 ];
or equivalently
Y1 = diff([0 x1])==1;
How this works: diff computes the difference of an element with respect to the preceding element. When that difference is 1, a new run of ones has begun. The first element requires special treatment.
A generalization of the answer by #Luis for the case where your vectors don't just contain zeros and ones:
Y1 = diff([0 x1]) & x1 == 1
This checks whether the value is one, and whether it is different from the previous value.

How to count the number of 1's from the total matrix

I have code like below:
N=10;
R=[1 1 1 1 1 0 0 0 0 0;1 1 1 1 1 1 1 1 1 1];
p=[0.1,0.2,0.01];
B = zeros(N , N);
B(1:N,1:N) = eye(N);
C=[B;R];
for q=p(1:length(p))
Rp=C;
for i=1:N
if(rand < p)
Rp(i,:) = 0;
end
end
end
from this code I vary the value of p. So for different value of p, i am getting different Rp. Now I want to get the total number of "1"'s from each Rp matrix. it means may be for p1 I am getting Rp1=5, for p2, Rp=4.
For example
Rp1=[1 0 0 0 0;0 1 0 0 0;0 0 0 0 0],
Rp2=[1 0 0 0 0;0 1 0 0 0;1 0 0 0 0],
Rp3=[0 0 0 0 0;0 1 0 0 0;0 0 0 0 0],
So total result will be 2,3,1.
I want to get this result.
If the matrix contains only 0 and 1 you are trying to count the nonzero values and there is a function for that called nnz
n = nnz(Rp);
As I mentioned in the comments you should replace
if(rand < p)
with
if(rand < q)
Then you can add the number of nonzero values to a vector like
r = [];
for q=p(1:length(p))
Rp=C;
for i=1:N
if(rand < p)
Rp(i,:) = 0;
end
end
r = [r nnz(Rp)];
end
Then r will contain your desired result. There are many ways to improve your code as mentioned in other answers and comments.
Assuming Rp is your matrix, then simply do one of the following:
If your matrix only contains zeros and ones
sum(Rp(:))
Or if your matrix contains multiple values:
sum(Rp(:)==1)
Note that for two dimensional matrices sum(Rp(:)) is the same as sum(sum(Rp))
I think your real question is how to save this result, you can do this by assigning it to an indexed varable, for example:
S(count) = sum(Rp(:));
This will require you to add a count variable that increases with one every step of the loop. It will be good practice (and efficient) to initialize your variable properly before the loop:
S = zeros(length(p),1);
If you need to count the 1's in any matrix M you should be able to do sum(M(:)==1)