Count items in one array in another array matlab - arrays

Given the values in C, I want to count how many times I encounter each of these values in the vector B. For example, if C = [1 2], I'd like to count how many "1"s are in B and how many "2"s are in B by checking the elements of array C.
However, the code I have seems to count up to 5, but what the true output should be is that the number of 1s is 2 and the number of 2s is 3.
The code I wrote is shown below. Any help would be certainly appreciated.
My code :
B = [ 1 2 1 4 5 2 2 ]
C = [ 1 2 ]
lb = length(B)
lc = length(C)
for i = 1:length(B)
for j = 1:length(C)
if B(1,i)==C(1,j)
c=c+1
A(i)=c
end
end
end

Santhan Salai gave the corrected answer if you want to stick with loops. However, for efficiency, you could achieve what you want with bsxfun and sum if you don't want to loop:
>> B = [ 1 2 1 4 5 2 2 ];
>> C = [ 1 2 ];
>> D = sum(bsxfun(#eq, B, C.'), 2)
D =
2
3
bsxfun is a nice tool that allows you to broadcast data across singleton dimensions. In our case, I used the equals functions to check for element-wise equality and I transposed C so that it becomes a column vector. What will be produced is a matrix where we have as many rows as there are in C and as many columns as there are in B. Each row of this output matrix tells you which elements were equal to each corresponding element in C. C is broadcasted such that we get a matrix of a bunch of [1;2] columns mashed together for as many elements as there are in B and B is broadcasted such that we get replicates of B with as many rows as there are in C. By doing element-wise equality, we would produce a logical matrix where each element for a row tells you which elements were equal to the corresponding value of C. All you have to do now is sum over the columns to get how many elements were equal to each value in C, hence the sum call over the second dimension.
If you want overkill, you can also use accumarray. Assuming that B and C only consist of integers, do:
>> B = [ 1 2 1 4 5 2 2 ];
>> C = [ 1 2 ];
>> D = accumarray(B.', 1);
D =
2
3
0
1
1
This however computes the frequency of all unique values in B, and so you'd just want the first two entries in D.
One more alternative is to use histc:
>> B = [ 1 2 1 4 5 2 2 ];
>> C = [ 1 2 ];
>> D = histc(B, C)
D =
2 3
What you are basically computing is a histogram of B at specified bins, which are denoted at C.

Using arrayfun could be one alternate to your loop
out = arrayfun(#(x) sum(ismember(B,x)), C)
Here is your corrected loop
count = zeros(1,numel(C));
for ii = 1:numel(C)
for jj = 1:numel(B)
if (C(ii) == B(jj))
count(ii) = count(ii) + 1;
end
end
end

Related

Chain elements in matrix using rows

I have a matrix where each row is a combination of two numbers, like A = [1 2; 2 5; 3 4; 4 6; 5 6]
A is built so that, for each row, the first elements is always smaller than the second one.
I need to return, from A, the lists of chained elements (in the case above, the lists of chained elements are 1 2 5 6 and 3 4 6). These lists are essentially built by considering a row, and checking is the last number is the first number of another row. Do you have any suggestion on how to do this?
If I got the question correctly, assuming A as the input array, you can use bsxfun -
mask = bsxfun(#eq,A(:,1),A(:,2).');
out = unique(A(any(mask,1).' | any(mask,2),:))
Sample run -
>> A
A =
1 2
3 4
2 5
5 6
>> mask = bsxfun(#eq,A(:,1),A(:,2).');
>> unique(A(any(mask,1).' | any(mask,2),:))
ans =
1
2
5
6
You can also use ismember, like so -
out = unique(A(ismember(A(:,1),A(:,2)) | ismember(A(:,2),A(:,1)),:))
Third option would be to use intersect to solve it, like so -
[~,idx1,idx2] = intersect(A(:,1),A(:,2));
out = unique(A([idx1,idx2],:))
The following seems to work. It builds a matrix (B) that tells which elements are connected (by 1 step). It then extends that matrix ( C) to include 0-step, 1-step, ..., (n-1)-step connections, where n is the number of nodes.
From that matrix, groups of connected elements are obtained (R). Finally, only "maximal" groups are kept (that is, those, not contained in other groups).
A = [1 2; 3 4; 2 5; 4 6; 5 6]; %// data
n = max(A(:));
B = full(sparse(A(:,1), A(:,2), 1, n, n )); %// matrix of 1-step connections
C = eye(n) | B; %// initiallize with 0-step and 1-step connections
for k = 1:n-1
C = C | C*B; %// add k-step connections, up to k=n-1
end
[ii, jj] = find(C);
R = accumarray(ii, jj, [], #(x) {sort(x).'}); %'// all groups (maximal or not)
[xx, yy] = ndgrid(1:n);
C = cellfun(#(x,y) all(ismember(x, y)), R(xx), R(yy) ); %// group included in another?
result = R(all(~C | eye(n), 2)); %// keep only groups that are not included in others
This gives
>> result{:}
ans =
1 2 5 6
ans =
3 4 6

How to check if all the entries in columns of a matrix are equal (in MATLAB)?

I have a matrix of growing length for example a 4-by-x matrix A where x is increasing in a loop. I want to find the smallest column c where all columns before that, each, carry one single number. The matrix A can look like:
A = [1 2 3 4;
1 2 3 5;
1 2 3 1;
1 2 3 0];
where c=3, and x=4.
At each iteration of the loop where A grows in length, the value of index c grows as well. Therefore, at each iteration, I want to update the value of c. How efficiently can I code this in Matlab?
Let's say you had the matrix A and you wanted to check a particular column iito see if all its elements are the same. The code would be:
all(A(:, ii)==A(1, ii)) % checks if all elements in column are same as first element
Also, keep in mind that once the condition is broken, x cannot be updated anymore. Therefore, your code should be:
x = 0;
while true
%% expand A by one column
if ~all(A(:, x)==A(1, x)) % true if all elements in column are not the same as first element
break;
end
x = x+1;
end
You could use this:
c = find(arrayfun(#(ind)all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
This finds the first column where not all values are the same. If you run this in a loop, you can detect when entries in a column start to differ:
for x = 1:maxX
% grow A
c = find(arrayfun(#(ind)~all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
% If c is empty, all columns have values equal to first row.
% Otherwise, we have to subtract 1 to get the number of columns with equal values
if isempty(c)
c = x;
else
c = c - 1;
end
end
Let me give a try as well:
% Find the columns which's elements are same and sum the logical array up
c = sum(A(1,:) == power(prod(A,1), 1/size(A,1)))
d=size(A,2)
To find the last column such that each column up to that one consists of equal values:
c = find(any(diff(A,1,1),1),1)-1;
or
c = find(any(bsxfun(#ne, A, A(1,:)),1),1)-1;
For example:
>> A = [1 2 3 4 5 6;
1 2 3 5 5 7;
1 2 3 1 5 0;
1 2 3 0 5 8];
>> c = find(any(diff(A,1,1),1),1)-1
c =
3
You can try this (easy and fast):
Equal_test = A(1,:)==A(2,:)& A(2,:)==A(3,:)&A(3,:)==A(4,:);
c=find(Equal_test==false,1,'first')-1;
You can also check the result of find if you want.

How to remove elements of one array from another?

I have two arrays:
A=[1 1 2 2 3 3 3];
B=[1 3];
Is there any function that can remove elements which are contained in B from A?
The result should be
C=[1 2 2 3 3];
The order is not important, but if there is more specific elements like two times 1 in A, then I need operation that removes (from A) only as many of these specific elements is in B (in this case only one of 1 and one of 3; meaning other 1 and 3 should remain in final product C). This function should be analogous to setdiff, with the difference that it should take care of multiple instances of array elements. This analogy can hold because my B only contains elements that are in A.
For loop solution:
C = A;
for ii = 1:length(B)
C(find(C == B(ii), 1,'first')) = [];
end
Result
C =
1 2 2 3 3
Here's a vectorized solution using accumarray and repelem:
maxValue = max([A B]);
counts = accumarray(A(:), 1, [maxValue 1])-accumarray(B(:), 1, [maxValue 1]);
C = repelem(1:maxValue, max(counts, 0));
And the result for your sample data A = [1 1 2 2 3 3 3]; B = [1 3];:
C =
1 2 2 3 3
This will even work for cases where there are values in B not in A (like B = [1 4];) or more of a given value in B than in A (like B = [1 1 1];).
Note: The above works sinceA and B contain integers. If they were to contain floating-point values, you could map the unique values to integers first using unique and ismember. Let's say we had the following sample data:
A = [0 0 pi pi 2*pi 2*pi 2*pi];
B = [0 2*pi];
Here's a variant of the above code that can handle this:
uniqueValues = unique([A B]);
[~, A] = ismember(A, uniqueValues);
[~, B] = ismember(B, uniqueValues);
maxValue = max([A B]);
counts = accumarray(A(:), 1, [maxValue 1])-accumarray(B(:), 1, [maxValue 1]);
C = uniqueValues(repelem(1:maxValue, max(counts, 0)));
And the results:
C =
0 3.1416 3.1416 6.2832 6.2832 % [0 pi pi 2*pi 2*pi]

matlab: eliminate elements from array

I have quite big array. To make things simple lets simplify it to:
A = [1 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 5 5 5];
So, there is a group of 1's (4 elements), 2's (2 elements), 3's (4 elements), 4's (2 elements) and 5's (8 elements). Now, I want to keep only columns, which belong to group of 3 or more elements. So it will be like:
B = [1 1 1 1 3 3 3 3 5 5 5 5 5 5 5 5];
I was doing it using for loop, scanning separately 1's, 2's, 3's and so on, but its extremely slow with big arrays...
Thanks for any suggestions how to do it in more efficient way :)
Art.
A general approach
If your vector is not necessarily sorted, then you need to run to count the number of occurrences of each element in the vector. You have histc just for that:
elem = unique(A);
counts = histc(A, elem);
B = A;
B(ismember(A, elem(counts < 3))) = []
The last line picks the elements that have less than 3 occurrences and deletes them.
An approach for a grouped vector
If your vector is "semi-sorted", that is if similar elements in the vector are grouped together (as in your example), you can speed things up a little by doing the following:
start_idx = find(diff([0, A]))
counts = diff([start_idx, numel(A) + 1]);
B = A;
B(ismember(A, A(start_idx(counts < 3)))) = []
Again, note that the vector need not to be entirely sorted, just that similar elements are adjacent to each other.
Here is my two-liner
counts = accumarray(A', 1);
B = A(ismember(A, find(counts>=3)));
accumarray is used to count the individual members of A. find extracts the ones that meet your '3 or more elements' criterion. Finally, ismember tells you where they are in A. Note that A needs not be sorted. Of course, accumarray only works for integer values in A.
What you are describing is called run-length encoding.
There is software for this in Matlab on the FileExchange. Or you can do it directly as follows:
len = diff([ 0 find(A(1:end-1) ~= A(2:end)) length(A) ]);
val = A(logical([ A(1:end-1) ~= A(2:end) 1 ]));
Once you have your run-length encoding you can remove elements based on the length. i.e.
idx = (len>=3)
len = len(idx);
val = val(idx);
And then decode to get the array you want:
i = cumsum(len);
j = zeros(1, i(end));
j(i(1:end-1)+1) = 1;
j(1) = 1;
B = val(cumsum(j));
Here's another way to do it using matlab built-ins.
% Set up
A=[1 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5];
threshold=2;
% Get the unique elements of the array
uniqueElements=unique(A);
% Count haw many times each unique element occurs
counts=histc(A,uniqueElements);
% Write which elements should be kept
toKeep=uniqueElements(counts>threshold);
% Make a logical index
indexer=false(size(A));
for i=1:length(toKeep)
% For every unique element we want to keep select the indices in A that
% are equal
indexer=indexer|(toKeep(i)==A);
end
% Apply index
B=A(indexer);

Element-wise array replication in Matlab

Let's say I have a one-dimensional array:
a = [1, 2, 3];
Is there a built-in Matlab function that takes an array and an integer n and replicates each
element of the array n times?
For example calling replicate(a, 3) should return [1,1,1,2,2,2,3,3,3].
Note that this is not at all the same as repmat. I can certainly implement replicate by doing repmat on each element and concatenating the result, but I am wondering if there is a built in function that is more efficient.
I'm a fan of the KRON function:
>> a = 1:3;
>> N = 3;
>> b = kron(a,ones(1,N))
b =
1 1 1 2 2 2 3 3 3
You can also look at this related question (which dealt with replicating elements of 2-D matrices) to see some of the other solutions involving matrix indexing. Here's one such solution (inspired by Edric's answer):
>> b = a(ceil((1:N*numel(a))/N))
b =
1 1 1 2 2 2 3 3 3
a = [1 2 3];
N = 3;
b = reshape(repmat(a,N,1), 1, [])
As of R2015a, there is a built-in and documented function to do this, repelem:
repelem Replicate elements of an array.
W = repelem(V,N), with vector V and scalar N, creates a vector W where each element of V is repeated N times.
The second argument can also be a vector of the same length as V to specify the number of replications for each element. For 2D replication:
B = repelem(A,N1,N2)
No need for kron or other tricks anymore!
UPDATE: For a performance comparison with other speedy methods, please see the Q&A Repeat copies of array elements: Run-length decoding in MATLAB.
>> n=3;
>> a(floor((0:size(a,2)*n-1)/n)+1)
ans =
1 1 1 2 2 2 3 3 3
Some exotic alternatives. Admittedly more funny than useful:
Assign the (first) result of meshgrid to a vector:
b = NaN(1,numel(a)*n); %// pre-shape result
b(:) = meshgrid(a,1:n);
Build a matrix that multiplied by a gives the result:
b = a * fliplr(sortrows(repmat(eye(numel(a)),n,1))).';
Use ind2sub to generate the indices:
[~, ind] = ind2sub([n 1],1:numel(a)*n);
b = a(ind);
If you have the image processing toolbox, there is another alternative:
N = 3;
imresize(a, [1 N*numel(a)],'nearest')
% To get b = [1 1 1 2 2 2 3 3 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a,1,numel(temp_a));
% To get b = [1 2 3 1 2 3 1 2 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a',1,numel(temp_a));

Resources