Find unique pairs in a matrix - arrays

Let's assume I have the following matrix:
A = [1 1 2 1;1 2 2 1;2 1 3 0;2 2 2 0;3 1 2 1]
Where the first column is an index and the next two an interaction and the last one a logic saying yes or no.
So know I would like to generate the following heat map based on the interactions. "X" axis represents interactions and "Y" axis represents index.
1-2 1-3 2-2
1 1 NaN 1
2 NaN 0 0
3 1 NaN NaN
My current approach:
B = sortrows(A,[2,3]);
Afterwards I apply find for each row and column individually.
Is there a function similar to unique which can check for two columns at the same time?

Here's a way, using unique(...,'rows'):
A = [1 1 2 1; 1 2 2 1; 2 1 3 0; 2 2 2 0; 3 1 2 1]; % data
[~, ~, jj] = unique(A(:,[2 3]),'rows'); % get interaction identifiers
B = accumarray([A(:,1) jj], A(:,4), [], #sum, NaN); % build result, with NaN as fill value
This gives
B =
1 NaN 1
NaN 0 0
1 NaN NaN

>> A
A =
1 1 2 1
1 2 2 1
2 1 3 0
2 2 2 0
3 1 2 1
>> [C, IA, IC] = unique(A(:, [2, 3]), 'rows')
C =
1 2
1 3
2 2
IA =
1
3
2
IC =
1
3
2
3
1
C is a set of unique pairs. IA is the corresponding index of C (i.e., C == A(IA, [2, 3])). IC is the corresponding index of each row (i.e., A(:, [2, 3]) == C(IC, :)).

this is a possible solution with the aid of #Jeon 's answer(Updated):
A = [1 1 2 1;1 2 2 1;2 1 3 0;2 2 2 0;3 1 2 1]
[~,IA,idx] = unique(A(:, [2, 3]), 'rows');
r = max(A(:,1));
c = numel(IA);
out= NaN(r,c );
out(sub2ind([r ,c], A(:,1),idx)) = A(:,4)

Related

Sort a matrix according to ordering in another matrix

I am trying to sort an array based on another array. I tried the sort method with index return, but it is somehow behaving strangely.
y = [1 2 3; 2 3 4]
x = [1 3 4; 2 2 3]
[yy, ii] = sort(y,'descend');
yy =
2 3 4
1 2 3
ii =
2 2 2
1 1 1
But my x(ii) is not the matrix sorted based on y.
x(ii) =
2 2 2
1 1 1
I am expecting the matrix to be:
x(ii) =
2 2 3
1 3 4
How can I sort the matrix x according to another matrix y?
ii are row subscripts but are being inputted by you as linear indices.
You need to convert them to relevant linear indices before proceeding i.e.
>> szx = size(x);
>> x(sub2ind(szx, ii, repmat(1:szx(2),szx(1),1)))
ans =
2 2 3
1 3 4

Extract pattern and subsequent n elements from array and count number of occurences

I have an array of doubles like this:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30]
i want to find the pattern [1 2 3 4] within the array and then store the 2 values after that pattern with it like:
A = [1 2 3 4 0 3]
B = [1 2 3 4 150 30]
i can find the pattern like this but i don't know how to get and store 2 values after that with the previous one.
And after finding A, B if i want to find the number of occurrences of each arrays within array C how can i do that?
indices = cellfun(#(c) strfind(c,pattern), C, 'UniformOutput', false);
Thanks!
Assuming you're fine with a cell array output, this works fine:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30 42 1 2 3 4 0 3]
p = [1 2 3 4]
n = 2
% full patttern length - 1
dn = numel(p) + n - 1
%// find indices
ind = strfind(C,p)
%// pre check if pattern at end of array
if ind(end)+ dn > numel(C), k = -1; else k = 0; end
%// extracting
temp = arrayfun(#(x) C(x:x+dn), ind(1:end+k) , 'uni', 0)
%// post processing
[out, ~, idx] = unique(vertcat(temp{:}),'rows','stable')
occ = histcounts(idx).'
If the array C ends with at least n elements after the last occurrence of the pattern p, you can use the short form:
out = arrayfun(#(x) C(x:x+n+numel(p)-1), strfind(C,p) , 'uni', 0)
out =
1 2 3 4 0 3
1 2 3 4 150 30
occ =
2
1
A simple solution can be:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30];
pattern = [1 2 3 4];
numberOfAddition = 2;
outputs = zeros(length(A),length(pattern)+ numberOfAddition); % preallocation
numberOfFoundPattern = 1;
lengthOfConsider = length(C) - length(pattern) - numberOfAddition;
for i = 1:lengthOfConsider
if(sum(C(i:i+length(pattern)) - pattern) == 0) % find pattern
outputs(numberOfFoundPattern,:) = C(i:i+length(pattern)+numberOfAddition);
numberOfFoundPattern = numberOfFoundPattern + 1;
end
end
outputs = outputs(1:numberOfFoundPattern - 1,:);

Eliminate repeated vectors but with elements on different order

I have a matrix A which is (243 x 5). I want to pick the unique row vectors of that matrix but taking into account that row vectors with the same elements but in different order shall be considered as being the same.
E.g., suppose for simplicity that the A matrix is (10 x 5) and equal to:
A=[1 2 1 2 3
1 3 1 1 1
1 3 1 1 2
1 2 1 1 3
2 3 1 2 1
1 3 1 2 2
1 3 1 2 3
1 3 1 3 2
1 3 1 3 1
1 3 2 3 1]
On the example above, rows (1, 5, 6) are to be considered equivalent they have the same elements but in different order. Also, rows (3 and 4) are equivalent, and rows (7, 8, 10) are also equivalent.
Is there any way to write a code that removes all "repeated rows", i.e. a code that delivers only the rows (1, 2, 3, 7 and 9) from A?
So far I came across with this solution:
B(:,1) = sum(A == 1,2);
B(:,2) = sum(A == 2,2);
B(:,3) = sum(A == 3,2);
[C, ia, ic] = unique(B,'rows');
Result = A(ia,:);
This delivers what I am looking for with one caveat - it is delivering the unique rows of A according to the criteria defined above, but it is not delivering the first row it finds. I.e. instead of delivering rows (1,2,3,7,9) it is delivering rows(7, 1, 9, 3, 2).
Anyway I can force him to deliver the rows in correct order? Also any better way of doing this?
You can do it as follows:
Sort A along the second dimension;
Get stable indices of unique (sorted) rows;
Use the result as row indices into the original A.
That is:
As = sort(A, 2);
[~, ind] = unique(As, 'rows', 'stable');
result = A(ind,:);
For
A = [1 2 1 2 3
1 3 1 1 1
1 3 1 1 2
1 2 1 1 3
2 3 1 2 1
1 3 1 2 2
1 3 1 2 3
1 3 1 3 2
1 3 1 3 1
1 3 2 3 1];
this gives
result =
1 2 1 2 3
1 3 1 1 1
1 3 1 1 2
1 3 1 2 3
1 3 1 3 1

Calculating difference between both adjacent and non-adjacent pairs using multiple index vectors

I have three numerical vectors containing position values (pos), a category (type), and an index (ind), in these general forms:
pos =
2 4 5 11 1 5 8 11 12 20
type =
1 2 1 2 1 1 2 1 2 3
ind =
1 1 1 1 2 2 2 2 2 2
I want to calculate the difference between values held within pos but only between the same types, and confined to each index. Using the above example:
When ind = 1
The difference(s) between type 1 positions = 3 (5-2).
The difference(s) between type 2 positions = 7 (11-4).
In the case where more than two instances of any given type exist within any index, the differences are calculate sequentially from left to right as shown here:
When ind = 2
The difference(s) between type 1 positions = 4 (5-1), 6 (11-5).
The difference(s) between type 2 positions = 4 (12-8).
Even though index 2 contains type '3', no difference is calculated as only 1 instance of this type is present.
Types are not always only 1, 2 or 3.
Ideally, the desired output would be matrix containing the same number of columns as length(unique(type)) with rows containing all differences calculated for that type. The output does not need to separate by index, only the actual calculation needs to. In this case there are three unique types, so the output would be (labels added for clarity only):
Type 1 Type 2 Type 3
3 7 0
4 4 0
6 0 0
Any empty entries can be padded with zeroes.
Is there a concise or fast manner to do this?
EDIT:
EDIT 2:
Additional input/output example.
pos = [1 15 89 120 204 209 8 43 190 304]
type = [1 1 1 2 2 1 2 3 2 3]
ind = [1 1 1 1 1 1 2 2 2 2]
Desired output:
Type 1 Type 2 Type 3
14 84 261
74 182 0
120 0 0
In this case, the script works perfectly:
At least for creating the output matrix a loop is required:
pos = [2 4 5 11 1 5 8 11 12 20]
type = [1 2 1 2 1 1 2 1 2 3]
ind = [1 1 1 1 2 2 2 2 2 2]
%// get unique combinations of type and ind
[a,~,subs] = unique( [type(:) ind(:)] , 'rows')
%// create differences
%// output is cell array according to a
temp = accumarray(subs,1:numel(subs),[],#(x) {abs(diff(pos(x(end:-1:1))))} )
%// creating output matrix
for ii = 1:max(a(:,1)) %// iterating over types
vals = [temp{ a(:,1) == ii }]; %// differences for each type
out(1:numel(vals),ii) = vals;
end
out =
3 7 0
4 4 0
6 0 0
In case it doesn't work for your real data you may need unique(...,'rows','stable') and a 'stable' accumarray.
It appeared that the above solution gives different results depending on the system.
The only reason, why the code could give different results on different machines, is that accumarray is not "stable" as mentioned above. And in some very rare cases it could return unpredictable results. So please try the following:
pos = [2 4 5 11 1 5 8 11 12 20]
type = [1 2 1 2 1 1 2 1 2 3]
ind = [1 1 1 1 2 2 2 2 2 2]
%// get unique combinations of type and ind
[a,~,subs] = unique( [type(:) ind(:)] , 'rows')
%// take care of unstable accumarray
[~, I] = sort(subs);
pos = pos(I);
subs = subs(I,:);
%// create differences
%// output is cell array according to a
temp = accumarray(subs,1:numel(subs),[],#(x) {abs(diff(pos(x(end:-1:1))))} )
%// creating output matrix
for ii = 1:max(a(:,1)) %// iterating over types
vals = [temp{ a(:,1) == ii }]; %// differences for each type
out(1:numel(vals),ii) = vals;
end
out =
3 7 0
4 4 0
6 0 0

Create groups of sub-matrices according to one column

I would like to know how to create groups of matrices starting from a Matrix in Matlab.
I have this Matrix :
A= [ 1 1 2
1 2 3
1 3 4
2 1 3
2 2 4
2 3 5
3 1 4
3 2 5
3 3 6]
Now I would like to create several new matrices in which the elements, of each new matrix, are the first two columns of each row in A that have the third column of A in common.
For this case will be :
Af1=[1 1] % elements in common '2' (third column of A)
Af2= [1 2
2 1] % elements in common '3' (third column of A)
and so on.
Thanks in advance
Here's another approach:
B = sortrows(A, size(A,2)); %// sort rows acording to last column
gs = diff(find(diff([inf; B(:,3); inf])~=0)); %// sizes of groups determined by last col
result = mat2cell(B(:,1:end-1), gs); %// split according to those group sizes
This is a job for accumarray:
[ofGroup,~,subs] = unique(A(:,3));
values = accumarray(subs,1:size(A,1),[],#(x) {A(x,[1,2])});
out = [ofGroup values]
For accessing the result you could use the approach proposed by Divakar using deal. But I'd rather rethink and use the cell array out directly:
>> out{3,2}
ans =
1 3
2 2
3 1
You can use one approach with unique & arrayfun -
[~,~,idx] = unique(A(:,3),'rows','stable')
out = arrayfun(#(n) A(idx==n,1:2),1:max(idx),'Uni',0)
Verify output with celldisp -
>> celldisp(out)
out{1} =
1 1
out{2} =
1 2
2 1
out{3} =
1 3
2 2
3 1
out{4} =
2 3
3 2
out{5} =
3 3
Or if you already know how many groups you would have and would like to save each such cell as a new matrix with names Af1, Af2, etc., you can use deal (distribute inputs to outputs) -
>> [Af1,Af2,Af3,Af4,Af5] = deal(out{:})
Af1 =
1 1
Af2 =
1 2
2 1
Af3 =
1 3
2 2
3 1
Af4 =
2 3
3 2
Af5 =
3 3

Resources