Asked by Tian Lin
on 6 Jan 2013

i have a matrix like this: [1 0 0 1 1 1 1 0 1 1 0]

i need to change to: [1 0 0 4 4 4 4 0 2 2 0]

how to make a loop?thanks

*No products are associated with this question.*

Answer by Azzi Abdelmalek
on 6 Jan 2013

Edited by Azzi Abdelmalek
on 6 Jan 2013

Accepted answer

Improve my previous code speed three times

x=[1 0 0 1 1 1 1 0 1 1 0] a=find(x); b=[ 1 diff(a)]; b(b==1)=0; b(b~=0)=1; idx=zeros(numel(a),2); e=1; c=0; d=[]; for k=1:numel(a) e=e+b(k); c=c*not(b(k))+1; d(c)=a(k); idx(e,:)=[d(1) c]; end for k=1:e x(idx(k,1):idx(k,1)+idx(k,2)-1)=idx(k,2); end

Azzi Abdelmalek
on 14 Jan 2013

Answer by Jan Simon
on 6 Jan 2013

And finally an improved loop method which is about twice as fast as the vectorized method:

function a = RunLength_IgnoZero_loop2(a) len = length(a); c = a(1); ini = 1; b = zeros(size(a)); for ii = 2:len if a(ii) ~= c if c == 0 ini = ii; else b(ini) = ii - ini; b(ii) = ini - ii; end c = a(ii); end end

if c ~= 0 b(ini) = len - ini + 1; end a = cumsum(b);

Show 1 older comment

Image Analyst
on 6 Jan 2013

**1.2 seconds** *if* it were able to process more than 65536 regions.

Jan Simon
on 6 Jan 2013

x = double(rand(1, 1e6) > 0.8); tic; for ii = 1:100, y = FCN(x); end; toc

% RunLength_IgnoZero_loop2: 0.40317 seconds % Image Analyst's regionprops: 45.40 seconds

(Matlab R2009a/64/Win7/Core2Duo) I assume 0.12 sec means 1 iteration and you are using a modern machine which is 4 times faster than my older processor.

Image Analyst
on 6 Jan 2013

OK, I didn't notice at first that you were doing the same thing 100 times.

Answer by Roger Stafford
on 6 Jan 2013

Let x be the original row vector of 1's and 0's.

n = length(x); d = diff([0,x,0]); f1 = find(d(1:n)>0); f2 = find(d(2:n+1)<0)+1; y = zeros(1,n+1); y([f1,f2]) = [f2-f1,f1-f2]; y = cumsum(y(1:n));

Answer by Azzi Abdelmalek
on 6 Jan 2013

Edited by Azzi Abdelmalek
on 6 Jan 2013

clear x=[1 0 0 1 1 1 1 0 1 1 0] e=0,c=0,d=[] for k=1:numel(x) if x(k) e=e+not(c) c=c+1 d=[d k] idx(e,:)=[d(1) c] else c=0 d=[] end end for k=1:size(idx,1) x(idx(k,1):idx(k,1)+idx(k,2)-1)=idx(k,2) end

Answer by Image Analyst
on 6 Jan 2013

Very simple. No loop needed. You just reassign it:

m = [1 0 0 1 1 1 1 0 1 1 0] % Now make it into what you want: m = [1 0 0 4 4 4 4 0 2 2 0]

If you have some other algorithm then let's hear it. For example, leave the first element alone but take the next contiguous stretch of 1's and multiply them by 4, and take the next stretch and multiply them by 2. I couldn't figure out what algorithm you were applying, and you didn't say, and didn't say how general you needed this to be (for example can m have values other than 0 and 1, or can it be other lengths, or can it be 2D or 3D?).

Azzi Abdelmalek
on 6 Jan 2013

Image Analyst
on 6 Jan 2013

Oh, thanks Azzi, I didn't notice that. In that case, you can use bwlabel, regionprops, and intlut:

m = logical([1 0 0 1 1 1 1 0 1 1 0]) % Group into connected regions. labeledArray = bwlabel(m) % Measure the area of all regions. measurements = regionprops(labeledArray, 'Area'); areas = [measurements.Area] numberOfAreas = length(areas);

% Assign each connected area with its area. % Make a look up table to map each region number into the area of that region. lookUpTable = uint16([0 areas, zeros(1,65536-numberOfAreas-1)]); % Do the actual mapping. output = intlut(uint16(labeledArray), lookUpTable)

(Requires the Image Processing Toolbox.) This should also work with 2D arrays.

Answer by Jan Simon
on 6 Jan 2013

Edited by Jan Simon
on 6 Jan 2013

An inplace method, which changes the input vector on the fly without storing an index list - this muight be an advantage for large data sets:

function a = RunLength_IgnoZero_loop1(a)

len = length(a); c = a(1); ini = 1; for ii = 2:len if a(ii) ~= c if c == 0 ini = ii; else a(ini:ii-1) = ii - ini; end c = a(ii); end end

% care about last segement: if c ~= 0 a(ini:len) = len - ini + 1; end

Answer by Jan Simon
on 6 Jan 2013

Edited by Jan Simon
on 6 Jan 2013

For the test data `x = double(rand(1, 1e6) > 0.8)` this vectorized method is 9 times faster than my loop approach:

function a = RunLength_IgnoZero_Vec(a)

pos = [false, a > 0, false]; start = strfind(pos, [false, true]); stop = strfind(pos, [true, false]) -1; run = stop - start + 1;

b = zeros(size(a)); b(start) = run; b(stop+1) = -run; if length(b) == length(a) a = cumsum(b); else a = cumsum(b(1:length(b) - 1)); end

Opportunities for recent engineering grads.

## 1 Comment

## Image Analyst (view profile)

Direct link to this comment:http://www.mathworks.com/matlabcentral/answers/58059#comment_121108

Tian, as you can see there are a number of different methods to do that. But I've never done that. It seems like a strange thing to want.

Whydo you want this output array? What are you going to do with it after you get it? (It's possible you don't really need it, you justthinkyou do.)