I have a set of 1s and 0s. How do I count the maximum number of consecutive 1s?
(For example, x = [ 1 1 0 0 1 1 0 0 0 1 0 0 1 1 1 ....]). Here the answer is 3 because the maximum number of times 1 occurs consecutively is 3.
I was looking at some search and count inbuilt function, however I have not been successful.
Try this:
max( diff( [0 (find( ~ (x > 0) ) ) numel(x) + 1] ) - 1)
Here's a solution but it might be overkill:
L = bwlabel(x);
L(L==0) = [];
[~,n] = mode(L)
Sometimes it's better to write your own function with loops ; most of the time it's cleaner and faster.
Another possibility:
x = randi([0 1], [1 100]); %# random 0/1 vector
d = diff([0 x 0]);
maxOccurence = max( find(d<0)-find(d>0) )
which is inspired by an answer to a somewhat similar question...
Cody Problem 15 is find maximum consecutive ones in a 'binary' string.
This works quite nicely. As you can tell I'm quite pleased with it!
Cody size 19
max(cellfun(#numel,strsplit(x,'0')));
Related
I have a sequence of ones and zeros and I would like to count how often islands of consecutive ones appear.
Given:
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1]
By counting the islands of consecutive ones I mean this:
R = [4 3 1]
…because there are four single ones, three double ones and a single triplet of ones.
So that when multiplied by the length of the islands [1 2 3].
[4 3 1] * [1 2 3]’ = 13
Which corresponds to sum(S), because there are thirteen ones.
I hope to vectorize the solution rather than loop something.
I came up with something like:
R = histcounts(diff( [0 (find( ~ (S > 0) ) ) numel(S)+1] ))
But the result does not make much sense. It counts too many triplets.
All pieces of code I find on the internet revolve around diff([0 something numel(S)]) but the questions are always slightly different and don’t really help me
Thankful for any advice!
The following should do it. Hopefully the comments are clear.
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1];
% use diff to find the rising and falling edges, padding the start and end with 0
edges = diff([0,S,0]);
% get a list of the rising edges
rising = find(edges==1);
% and falling edges
falling = find(edges==-1);
% and thereby get the lengths of all the runs
SRuns = falling - rising;
% The longest run
maxRun = max(SRuns);
% Finally make a histogram, putting the bin centres
R = hist(SRuns,1:maxRun);
You could also obtain the same result with:
x = find(S==1)-(1:sum(S)) %give a specific value to each group of 1
h = histc(x,x) %compute the length of each group, you can also use histc(x,unique(x))
r = histc(h,1:max(h)) %count the occurence of each length
Result:
r =
4,3,1
I have a binary vector, e.g:
x = [1 1 1 0 0 1 0 1 0 0 0 1]
I want to keep the first 4 elements that are '1' (substituting the rest with '0's). In my example the resulting vector should be:
z = [ 1 1 1 0 0 1 0 0 0 0 0 0]
Any help would be much appreciated.
First construct a vector of zeroes, then use find:
z = false(size(x));
z(find(x, 4)) = true;
No need for find for a binary vector. Use cumsum instead!
>> z = x;
>> z(cumsum( z, 2 ) > 4) = 0;
This solution (unlike find-based answers) can process a stack of such binary vectors at once (all you need is to verify that cumsum works on the proper dimension).
Try following:
z=x;
A=find(z);
z(A(5:end))=0;
Idea here is to make all, but first n, 1's to 0's
I have just started exploring world of vectorization. I got the 1-D vectorization down but i am having trouble vectorizing the following code. I want to do away with at least one of the for loops if possible b/c I plan to use this on a much larger data set over many iterations so saving computation time is of the essence.
CityPairs = [7 3
3 1
3 1
1 7
7 1
3 4
5 1
4 6];
Offices = [1;3;7];
nOffices = size(Offices,1);
connection = zeros(nOffices);
for i = 1:nOffices
for j = 1:nOffices
connection(i,j) = sum(Offices(i) == CityPairs(:,1)...
& CityPairs(:,2) == Offices(j));
end
end
disp(connection)
In this example there are 7 cities, three of which have offices. I want a pairwise matrix for the cities with offices to capture the sum of all the one way connections between each. The answer for above problem should be:
0 0 1
2 0 0
1 1 0
Any suggestions are welcome. Thanks in advance.
Keith
Here's an alternative solution with sparse:
dims = max(max(CityPairs), max(Offices));
A = sparse(CityPairs(:, 1), CityPairs(:, 2), 1, dims(1), dims(2));
result = full(A(Offices, Offices));
This should speed up your computation a bit1 when compared to the suggested bsxfun solution.
1 Runs 5 times faster on MATLAB 2012a (Windows Server 2008 R2 running on a 2.27GHz 16-core Intel Xeon processor)
Your task is some selective cross-tabulation. You can accomplish this easily by accumulating counts to the positions of interest, indexed by your Offices:
% Row and col subs
[~,rsubs] = ismember(CityPairs(:,1),Offices);
[~,csubs] = ismember(CityPairs(:,2),Offices);
% Select where both belong to Offices, i.e. non 0
subs = [rsubs,csubs];
subs = subs(all(subs,2),:);
% Accumulate
accumarray(subs,1)
The result
ans =
0 0 1
2 0 0
1 1 0
If you have the Statistics Toolbox, you could use crosstab directly, but then you would need to select the rows and columns of interest:
crosstab(CityPairs(:,1),CityPairs(:,2))
ans =
0 0 0 0 1
2 0 1 0 0
0 0 0 1 0
1 0 0 0 0
1 1 0 0 0
Answer provided by Cedric Wannaz on MathWorks Forum
It is not trivial to "vectorize" this setup, as there are operations that require some look up table, and there is accumulation (unless you find a trick). This is likely to make the approach without FOR loops more complicated (code-wise) than the basic, loop-based approach. So let's start with the trick first ;-) ..
A = double( bsxfun(#eq, CityPairs(:,1), Offices.') ) ;
B = double( bsxfun(#eq, CityPairs(:,2), Offices.') ) ;
A.' * B
Alternate approach: http://www.mathworks.com/matlabcentral/answers/91294-vectorization-of-2-for-loops-in-matlab
This is related to:
Finding islands of zeros in a sequence.
However, the problem is not exactly the same:
Let's take the same vector with the above postfor the purpose of comparison:
sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
What I am trying to find are the starting indices of islands of n consecutive zeros; however, overlapping is not allowed. For example for n=2, I want the result:
v=[3, 5, 14, 25];
I found the solution of Amro brilliant as a starting point (especially with regards to strfind), but the second part of his answer does not give me the result that I expect. This is a non-vectorized solution that I have so far:
function v=findIslands(sig, n)
% Finds indices of unique islands
% sig --> target vector
% n --> This is the length of the island
% This will find the starting indices for all "islands" of ones
% but it marks long strings multiple times
startIndex = strfind(sig, zeros(1,n));
L=length(startIndex);
% ongoing gap counter
spc=0;
if L>0 % Check if empty
v=startIndex(1);
for i=2:L
% Count the distance
spc=spc+(startIndex(i)-startIndex(i-1));
if spc>=n
v=[v,startIndex(i)];
% Reset odometer
spc=0;
end
end
else
v=[];
display('No Islands Found!')
end
I was wondering if someone has a faster vectorized solution to the above problem.
You can convert everything into strings and use regular expressions:
regexp(sprintf('%d', sig(:)), sprintf('%d', zeros(n, 1)))
Example
>> sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
>> n = 2;
>> regexp(sprintf('%d', sig(:)), sprintf('%d', zeros(n, 1)))
ans =
3 5 14 25
Do this:
As an example let's look at the case where the run length you want is 2.
Convert vector to binary number
Set index = size-1, set starting = []
Loop until n < 4:
Is n divisible by 4?
Yes? Append index to starting. Set n = n / 4
No? Set n = n / 2
Goto 3
For any other run length replace 4 with 2**run.
Use gnovice's answer from the same linked question. It's vectorized, and the runs where duration == n are the ones you want.
https://stackoverflow.com/a/3274416/105904
Take the runs with duration >= n, and then divide duration by n, and that'll tell you how many consecutive runs you have at each position and how to expand the index list. This could end up faster than the regexp version, if your island density isn't too high.
Say I have the following matrix:
1 0 1 1 0 0
0 0 1 0 1 0
1 1 1 0 0 1
0 1 1 0 0 1
1 1 1 1 1 0
I want to convert it to a different format, where I replace each 1 in each row by its column index, so it would become the following:
1 0 3 4 0 0
0 0 3 0 5 0
1 2 3 0 0 6
0 2 3 0 0 6
1 2 3 4 5 0
I can do it the 'dumb' way:
[H, W] = size(a);
for i = 1:H
for j = 1:W
if(a(i, j) == 1)
a(i, j) = j;
end
end
end
But there surely must be a way to do it with one line (perhaps using the 'find' function), anyone know how?
This isn't super general but does what you want. find returns indices into the one-dimensional version of the data, so we need to do a little arithmetic to get the two-d versions:
a(a == 1) = floor((find(a == 1) - 1) / size(a, 1)) + 1
If you wanted to do the row indices instead, you could use
a(a == 1) = mod(find(a == 1) - 1, size(a, 1)) + 1
If you were doing this with a big matrix, you might want to assign find(a == 1) to a temporary variable first:
inds = find(a == 1)
a(inds) = floor((inds - 1) / size(a, 1)) + 1
(Note that indexing into a with either a list of indices or a matrix of booleans works the same.)
You could also just use find(a) if you know the original matrix is only 0s and 1s.
Note that this is just doing manually basically what #tmpearce's answer does.
you have a matrix a
[r,c]=ind2sub(size(a),find(a));
a(find(a))=c;
Edit: this is doable in one line, since that's important to you:
[r,a(find(a))]=ind2sub(size(a),find(a));
You can use meshgrid to do this:
[H, W] = size(a);
a = a.*meshgrid(1:H,1:W);
It's been a long time since I have use matlab, so I wont be able to give you the code out of my head. But here is the way I would do it:
Create a vector 1:colums, repeat it once for each row using repmat and then multiply this elementwise with the original matrix.
Also since loops are slow in matlab whereas matrix operations are fast, such a one liner will probably be much faster than the code you have right now.