A.Rainer
A.Rainer

Reputation: 719

Remove single elements from a vector

I have a vector M containing single elements and repeats. I want to delete all the single elements. Turning something like [1 1 2 3 4 5 4 4 5] to [1 1 4 5 4 4 5].

I thought I'd try to get the count of each element then use the index to delete what I don't need, something like this:

uniq = unique(M);
list = [uniq histc(M,uniq)];

Though I'm stuck here and not sure how to go forward. Can anyone help?

Upvotes: 3

Views: 211

Answers (4)

Lucas
Lucas

Reputation: 980

X = [1 1 2 3 4 5 4 4 5];
Y = X;
A = unique(X);
for i = 1:length(A)
    idx = find(X==A(i));
    if length(idx) == 1
        Y(idx) = NaN;
    end
end
Y(isnan(Y)) = [];   

Then, Y would be [1 1 4 5 4 4 5]. It detects all single elements, and makes them as NaN, and then remove all NaN elements from the vector.

Upvotes: 0

Sardar Usama
Sardar Usama

Reputation: 19689

Here is a solution using unique, histcounts and ismember:

tmp=unique(M) ;            %finding unique elements of M
%Now keeping only those elements in tmp which appear only once in M
tmp = tmp(histcounts(M,[tmp tmp(end)])==1); %Thanks to rahnema for his insight on this 
[~,ind] = ismember(tmp,M); %finding the indexes of these elements in M
M(ind)=[];

histcounts was introduced in R2014b. For earlier versions, hist can be used by replacing that line with this:

tmp=tmp(hist(M,tmp)==1);

Upvotes: 2

rahnema1
rahnema1

Reputation: 15837

Here is a cheaper alternative:

[s ii] = sort(a);
x = [false s(2:end)==s(1:end-1)]
y = [x(2:end)|x(1:end-1) x(end)]
z(ii) = y;
result = a(z);

Assuming the input is

a =

1   1   8   8   3   1   4   5   4   6   4   5

we sort the list s and get index of the sorted list ii

s= 

1   1   1   3   4   4   4   5   5   6   8   8

we can find index of repeated elements and for it we check if an element is equal to the previous element

x =

0   1   1   0   0   1   1   0   1   0   0   1

however in x the first elements of each block is omitted to find it we can apply [or] between each element with the previous element

 y = 

1   1   1   0   1   1   1   1   1   0   1   1

we now have sorted logical index of repeated elements. It should be reordered to its original order. For it we use index of sorted elements ii :

z =

1   1   1   1   0   1   1   1   1   0   1   1

finally use z to extract only the repeated elements.

result = 

1   1   8   8   1   4   5   4   4   5

Here is a result of a test in Octave* for the following input:

a = randi([1 100000],1,10000000);

-------HIST--------
Elapsed time is 5.38654 seconds.
----ACCUMARRAY------
Elapsed time is 2.62602 seconds.
-------SORT--------
Elapsed time is 1.83391 seconds.
-------LOOP--------
Doesn't complete in 15  seconds.

*Since in Octave histcounts hasn't been implemented so instead of histcounts I used hist.

You can test it Online

Upvotes: 1

OmG
OmG

Reputation: 18838

You can get the result with the following code:

A = [a.', ones(length(a),1)];
[C,~,ic] = unique(A(:,1));
result = [C, accumarray(ic,A(:,2))];
a = A(~ismember(A(:,1),result(result(:,2) == 1))).';

The idea is, add ones to the second column of a', then accumarray base on the first column (elements of a). After that, found the elements in first column which have accum sum in the second column. Therefore, these elements repeated once in a. Finally, removing them from the first column of A.

Upvotes: 1

Related Questions