2 views (last 30 days)

Show older comments

Given an array submitted in a form of struct field, containing integer numbers. For convenience, let's assume that the numbers are already sorted in ascending order:

>> s.x

ans =

2

ans =

2

ans =

5

ans =

5

ans =

5

ans =

8

ans =

8

Find indexes of elements, which occur exact 2 times:

ind =

1 2 6 7

Andrei Bobrov
on 20 Oct 2017

Edited: Andrei Bobrov
on 23 Oct 2017

x= [2; 2; 5; 5; 5; 8; 8; 13; 13; 13; 13];

[~,~,g] = unique(x); % OR for last versions of MATLAB: g = findgroups(x)

c = accumarray(g,1:numel(x),[],@(x){x});

out = cell2mat(c(cellfun(@numel,c) == 2));

or

[a,~,g] = unique(x);

out = find(ismember(x,a(accumarray(g,1) == 2)));

or (FIXED)

out = reshape(strfind([1,diff(x(:)')~=0,1],[1 0 1]) + [0;1],[],1);

out = reshape(bsxfun(@plus,strfind([1,diff(x(:)')~=0,1],[1 0 1]),[0;1]),[],1); % for old MATLAB

Rik
on 20 Oct 2017

Edited: Rik
on 20 Oct 2017

It always pays off to get rid of loops and/or pre-allocating your output.

x= [2; 2; 5; 5; 5; 8; 8; 13; 13; 13; 13];

s = struct('x', num2cell(x));

x=[s.x];

%only newer releases: 0.000778 seconds

tic

count=histcounts(x,0.5 : max(x)+0.5);

ind=find(sum(x==find(count==2)'));

toc

%should work on most releases: 0.000628 seconds

tic

count=histcounts(x,0.5 : max(x)+0.5);

count=find(count==2);

ind=find(sum(repmat(x,length(count),1)==repmat(count',1,length(x))));

toc

%your loop: 0.001100 seconds

tic

d=diff(x); j=0; ind=[];

for i=1:numel(d)

if d(i)==0

j=j+1;

else

if j==1

ind(end+1)=i-1;

ind(end+1)=i;

end

j=0;

end

end

toc

Image Analyst
on 20 Oct 2017

You didn't tag it as homework. Is it? This will do it:

% Assignment of a struct with a field containing integer numbers

x= [2; 2; 5; 5; 5; 8; 8; 13; 13; 13; 13];

s = struct('x', num2cell(x));

numbers = [s.x]

[groupNumber, groupValue] = findgroups(numbers)

counts = histcounts(groupNumber)

ofGroupSize2 = find(counts == 2) % Find those only if they have a length of 2.

values = groupValue(ofGroupSize2)

indexes = find(ismember(numbers, values))

Image Analyst
on 20 Oct 2017

Jan
on 21 Oct 2017

Your code looks like the input is sorted. The other approaches do not have this limitation. If it is really sorted:

d = [true; diff(x) ~= 0]; % TRUE if values change

b = x(d); % Elements without repetitions

k = find([d', true]); % Indices of changes

n = diff(k);

is2 = find(n==2);

ind4 = reshape([k(is2); k(is2)+1], 1, []);

Code taken from FEX: RunLength.

Image Analyst
on 21 Oct 2017

Edited: Image Analyst
on 21 Oct 2017

If you have the Image Processing Toolbox, you can use regionprops():

% Assignment of a struct with a field containing integer numbers

x= [2; 2; 5; 5; 5; 8; 8; 13; 13; 13; 13];

s = struct('x', num2cell(x));

numbers = [s.x] % A labeled "image"

% Find lengths of each run of numbers plus the indexes where they occur.

props = regionprops(numbers, 'Area', 'PixelIdxList')

% Extract from structure into one vector.

allLengths = [props.Area]

% Find those only if they have a length of 2.

ofGroupSize2 = find(allLengths == 2)

% Find indexes of those runs with length 2.

indexes = [props(ofGroupSize2).PixelIdxList]

% Shape into row vector

indexes = reshape(sort(indexes(:)), 1, [])

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!