Finding and counting numbers from one matrix in another
4 views (last 30 days)
Show older comments
Hello all
I have a question, which seems complex to me but probably isn't.
I have two matrices and I am trying to find and count each number from one matrix in another.
For example: Lets say I have two matrices A and B. A is a list of nodes (mx1) and B is a list of triangles that contain those nodes (nx3).
I need to find all the nodes from A that are in B, count how many times they appear and in which triangles they appear.
So in the end I will get two new matrices, C=[nodeID, No. of times it appears in all columns in B] and D=[node ID, list of triangles (1:9) that contain that node (will contain 0s for nodes that few elements attached].
I have tried to use find and ismember but they don't see to do exactly what I want.
I hope I've explained that well enough. Help is very much appreciated.
Meghan
0 Comments
Accepted Answer
dpb
on 15 Mar 2017
Is fairly simple, yes, but have to think about it a while to find "the Matlab way"... :)
There's always another way, but what came to me first...
>> A=[1:13].'; % Assume total of 13 nodes
>> B=randi(20,20,3) % Random set of triangles that include more nodes than in the list
B =
2 2 19
11 5 4
16 19 6
19 4 3
3 17 3
12 11 18
10 20 12
1 2 11
7 9 3
4 3 18
16 20 13
7 1 8
11 16 11
4 17 9
13 18 2
6 2 5
14 8 3
14 6 4
15 17 5
10 9 9
>> [n,bin]=histc(B(:),A); % count how many of each and locate where they are
>> C=[A n] % 'C' is now easy-peasy...
C =
1 2
2 5
3 6
4 5
5 3
6 3
7 2
8 2
9 4
10 2
11 5
12 2
13 2
>>
D takes a little thinking...first reshape bin to match the shape of B so can get the row corresponding to position...
>> bin=reshape(bin,[],3);
>> D=zeros(length(A),length(B)); % preallocate for all possible locations
>> for i=1:length(A) % for each node in A
[r c]=find(bin==A(i)) % get row where located
r=unique(r); % save only the unique rows for repeated; probably not needed
t(i,r)=r; % and populate array at those locations with the value
end
>> D=[n t]
D =
2 0 0 0 0 0 0 0 8 0 0 0 12 0 0 0 0 0 0 0 0
5 1 0 0 0 0 0 0 8 0 0 0 0 0 0 15 16 0 0 0 0
6 0 0 0 4 5 0 0 0 9 10 0 0 0 0 0 0 17 0 0 0
5 0 2 0 4 0 0 0 0 0 10 0 0 0 14 0 0 0 18 0 0
3 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 19 0
3 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 16 0 18 0 0
2 0 0 0 0 0 0 0 0 9 0 0 12 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 17 0 0 0
4 0 0 0 0 0 0 0 0 9 0 0 0 0 14 0 0 0 0 0 20
2 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 20
5 0 2 0 0 0 6 0 8 0 0 0 0 13 0 0 0 0 0 0 0
2 0 0 0 0 0 6 7 0 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 11 0 0 0 15 0 0 0 0 0
>>
The duplicated nodes not possible in a real dataset I'd presume; I just added so could use the randomized array w/o having to clean it up here.
Oh, above assumes the row is the same as the node in assigning r as the row, that needs to be the elements of A for the corresponding index if the array of nodes isn't 1:N.
2 Comments
dpb
on 16 Mar 2017
Edited: dpb
on 16 Mar 2017
Yes, I mentioned above that if A has missing values there's an issue. I've a conflicting engagement here in just a few minutes but the size length(A) is still correct for row dimension; the dimension I used was maximum possible, for usage not accounting for some external constraints (like, what happens if somebody goofs and there are more than 9 connections? was what I figured the code was for, perhaps).
Use
t(A(i),1:length(r))=r; % (*)
should, I think, give you the result you're looking for...gotta' run, sorry.
(*) Which is Guillaume's loop below with the correction in my rush to make appointment I forgot the length() expression for column position and, of course, I later appended t to the first column whereas he's storing beginning with column 2.
More Answers (1)
Guillaume
on 16 Mar 2017
Edited: Guillaume
on 16 Mar 2017
Another method of obtaining the result, which works regardless of the values of the node ids, and whether or not a node is present in any triangle
%A: column vector of ids
[~, id] = ismember(B(:), A);
C = [A, accumarray(nonzeros(id), 1)]; %nonzero not required if all nodes are sure to be found in triangles
trigids = repmat((1:size(B, 1))', 1, size(B, 2));
triglist = accumarray(nonzeros(id), trigid(find(id)), [], @(list) {[list.', nan(1, 9-numel(list))]}); %again nonzeros and find(id) not needed if all nodes are sure to be found
triglist(cellfun(@isempty, triglist)) = {nan(1, 9)};
D = [A, cell2mat(triglist)]
Alternatively, D could be generated with a loop:
D = [A, nan(numel(A), 9)];
for rowid = 1:numel(A)
[trigid, ~] = find(B == A(rowid));
D(rowid, 2:numel(trigid)+1) = trigid;
end
4 Comments
dpb
on 16 Mar 2017
"...why I then offered the loop option which may actually be faster, and certainly easier to understand."
Indeed, it dawned on me that the loop over the bins was equivalent to a direct loop over the array while writing the posted answer but had it tested and with the time constraint of an appointment in town decided better just leave good-enough alone.
See Also
Categories
Find more on Creating and Concatenating Matrices in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!