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.
Related
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
my problem is: I have to make a code that takes a matrix 6x6 of 0's and 1's and check certain conditions in each position of the matrix to iterate through it, and create a matrix with the new values based on the conditions, that are based in the neighbor elements of the center value. The conditions are the following:
if the center value is 1 it has 3 options:
-If in the neighbor cells there is only one cell containing a number 1, or cero cells containing a number 1 the center value converts from 1 to 0.
-If the neighbor cells of the center value contains in total 4 or more 1's, it converts from 1 to 0.
-And if the neighbor cells contain in total 2 or 3 number 1's, it keeps being 1.
Now, if the center value is 0, it has 1 option:
-If the neighbor cells contain 3 1's in total (tops), it converts from 0 to 1.
-Else, it keeps being 0
The matrix is the following:
A = [0 1 0 1 1 0; 1 0 1 0 0 1; 1 1 0 1 1 1; 0 0 1 0 0 0; 0 1 0 0 0 1; 1 1 1 0 0 1]
So for example, the number 1 in the position A(5,2) should become 0, because it has 4 1's surrounding it, and another example, the zero in A(4,6) should become 1, because it has 3 1's surrounding it.
I made 2 loops using 1 for the rows and another for the columns to iterate through each value of the matrix, but in the conditional I don't know how to check the values of the surrounding cells or neighbor cells
I'm new to Matlab, if you can help me it would be great! Thanks.
Edit: added code the code I've made until now.
[f,c]=size(A)
for i=1:f
for j=1:c
if A(i,:)==1
if A(i+1,j+1)==1 && A(i+1,j-1)==1 && A(i-1,j-1)==1
A(i,:)=1;
end
elseif A(i,:)==0
if A(i+1,j+1)==1 && A(i+1,j-1)==1 && A(i-1,j-1)==1
A(i,:)=1;
end
end
end
end
I tried to set the conditions to the (i-1,j-1),(i+1,j+1),(i+1,j),(i,j+1),(i-1,j),(i,j-1) but I think that will not work because I'm checking coditions as a group not counting the total numbers of 1 in the neighbors
Edit 2: been thinking how to solve it and I thought that I could make a variable that counts the quantity of 1's in the surroundings of the center value, but it gives me an error that it exceeds the dimensions of the matrix in line 50, even after I made considerations for the first column, row and last column, row.
This is the complete code:
[f,c] = size(gen0);
nrogen = input('Introduzca el número de generaciones de bacterias que quiera crecer: ');
if isempty(nrogen) || ~isnumeric(nrogen) || ~isscalar(nrogen) || round(nrogen)~=nrogen || nrogen < 0
disp('Número no pertinente, intente de nuevo');
return
end
nac = 0;
mue = 0;
neigh = 0;
for k=1:nrogen
for i=1:f
for j=1:c
if i>1 && i<6
if gen0(i+1,j+1)==1
neigh=neigh+1;
end
if gen0(i,j+1)==1
neigh=neigh+1;
end
if gen0(i+1,j)==1
neigh=neigh+1;
end
if gen0(i-1,j-1)==1
neigh=neigh+1;
end
if gen0(i,j-1)==1
neigh=neigh+1;
end
if gen0(i-1,j)==1
neigh=neigh+1;
end
elseif i==1
if gen0(i+1,j+1)==1
neigh=neigh+1;
end
if gen0(i,j+1)==1
neigh=neigh+1;
end
if gen0(i+1,j)==1
neigh=neigh+1;
end
elseif i==6
if gen0(i-1,j-1)==1
neigh=neigh+1;
end
if gen0(i,j-1)==1
neigh=neigh+1;
end
if gen0(i-1,j)==1
neigh=neigh+1;
end
if gen0(i,:)==1
if neigh==2 || neigh==3
gen0(i,:)=1;
elseif neigh==1 || neigh==0
gen0(i,:)=0;
end
end
end
end
disp(gen0);
end
if gen0(i,:)==1
if neigh==2 || neigh==3
gen0(i,:)=1;
elseif neigh==1 || neigh==0 || neigh>3
gen0(i,:)=0;
end
end
end
Nested loops is a sensible option. However, what you are doing is basically an image filter, something that is already implemented by the function imfilter, so Why not to take advantage of that and simplify your life? What you want to do can be accomplished with:
%Creating the filter that adds up all neighbors
filter=ones(3,3);
filter(2,2)=0;
%Apply the filter to compute the sum of all neighboring elements
onesCount=imfilter(A,filter);
%Creating the rules in arrays that contains result for a given count of ones (+1)
%ones count 0 1 2 3 4 5 6 7 8 9
onesRule = [0 0 1 1 0 0 0 0 0 0];
zeroRule = [0 0 0 1 0 0 0 0 0 0];
%Initializing output matrix
out=zeros(size(A));
%Assigning values to the cells with ones
outForOnes=onesRule(onesCount+1);
out(A==1)=outForOnes(A==1);
%Assigning values to the cells with zeros
%(if you want you can skip this step initializing out=outForZeros)
outForZeros=zeroRule(onesCount+1);
out(A==0)=outForZeros(A==0);
Otherwise, if you want to keep the nested loops, instead of dealing with the exceptions of index out of bounds, I would suggest to pad A with zeros all around. So if A size is n by m (6 by 6 in this case) you do:
B=zeros(n+2,m+2);
B(2:end-1,2:end-1)=A;
A=B;
And then you loop i and j between 2:n+1 and 2:m+1
In my opinion, using two nested loops in this case is a good approach. Retrieving the surrounding values of a matrix element is always tricky, but it can be accomplished with some efforts. Here is the solution I propose you:
A = [
0 1 0 1 1 0;
1 0 1 0 0 1;
1 1 0 1 1 1;
0 0 1 0 0 0;
0 1 0 0 0 1;
1 1 1 0 0 1
];
A_rows = size(A,1);
A_cols = size(A,2);
for i = 1:A_rows
for j = 1:A_cols
% Retrieve the current center...
value = A(i,j);
% Retrieve the neighboring column and row offsets...
c = bsxfun(#plus,j,[-1 0 1 -1 1 -1 0 1]);
r = bsxfun(#plus,i,[-1 -1 -1 0 0 1 1 1]);
% Filter the invalid positions...
idx = (c > 0) & (c <= A_cols) & (r > 0) & (r <= A_rows);
% Transform the valid positions into linear indices...
idx = (((idx .* c) - 1) .* A_rows) + (idx .* r);
idx = reshape(idx.',1,numel(idx));
% Filter the invalid linear indices...
idx = idx(idx > 0);
% Find the center neighbors and their sum...
neighbors = A(idx);
neighbors_sum = sum(neighbors);
% Apply the transformation criterions to A...
if (value == 0)
if (neighbors_sum == 3)
A(i,j) = 1;
end
else
if (neighbors_sum <= 1) || (neighbors_sum >= 3)
A(i,j) = 0;
end
end
end
end
The final output for the given example is:
A =
0 1 1 0 0 0
0 0 0 1 0 1
0 0 1 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 1 1 0 0 0
I just have a few doubts about the whole process you described.
The first one concerns the criterions to apply when the center value is equal to 1. Two of them seem to be contradictory:
-If the neighbor cells of the center value contains in total 3 or more 1's, it converts from 1 to 0.
-And if the neighbor cells contain in total 2 or 3 number 1's, it keeps being 1.
When the neighbors sum is equal to 3... which condition should be applied? The first one or the second one?
The second one concerns the original matrix A. Should it be updated inside the loop? I mean, in the current code, the values of A change when certain conditions are met... but this also means that the conditions are influenced by the outcome of the previous iterations. Maybe your goal is to keep A static while updating a clone of it instead?
Anyway, both issues are easy to deal with and you should be able to adapt my code to your needs without any problem.
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
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)
Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.
This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0
You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.
It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.