Create groups of sub-matrices according to one column - arrays

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

Related

Convert output of ndgrid to a single array

I want to create an n-dimensional grid from vectors xi which specify the desired grid points in dimension i. The output should be a single N x n matrix, where N=b1*b2*b3*...*bn is the total number of grid points, and bi is the number of desired grid points along that dimension. (I want to do this in Matlab.)
I know that I can use the ndgrid function to create this n-dimensional grid, but ndgrid returns n cell arrays, each of dimension b1xb2xb3x...xbn. How can I transform this to a single array, as desired?
An additional complication: I do not know the dimension n in advance.
David already got the idea in his comment, just a minor error for n>=4.
function grid_array = ndgridarr(n, varargin)
assert(length(varargin) == 1 || length(varargin) == n);
grid_cells = cell(1, n);
[grid_cells{:}] = ndgrid(varargin{:});
grid_array = reshape(cat(n+1,grid_cells{:}),[],n);
end
An alternative is to use allcomb from file exchange or
combvec (Deep learning toolbox). They both already return a single matrix, no need to stich the cell array together.
Here's one possible solution. I'd be very happy to hear about simpler approaches.
function grid_array = ndgridarr(n, varargin)
assert(length(varargin) == 1 || length(varargin) == n);
grid_cells = cell(1, n);
[grid_cells{:}] = ndgrid(varargin{:});
grid_array = cell2mat(cellfun(#(c) c(:), grid_cells, 'UniformOutput', false));
end
You can call this function exactly like you would ndgrid, just with the additional input parameter n. (ngrid infers n automatically from the number of output arguments in the case when just a single vector is provided, but we cannot do this since we have only one output parameter in any case.)
Two examples illustrating that it does what's desired:
>> ndgridarr(3, [1,2,3])
ans =
1 1 1
2 1 1
3 1 1
1 2 1
2 2 1
3 2 1
1 3 1
2 3 1
3 3 1
1 1 2
2 1 2
3 1 2
1 2 2
2 2 2
3 2 2
1 3 2
2 3 2
3 3 2
1 1 3
2 1 3
3 1 3
1 2 3
2 2 3
3 2 3
1 3 3
2 3 3
3 3 3
>> ndgridarr(3, [1,2], [3,4], [5,6])
ans =
1 3 5
2 3 5
1 4 5
2 4 5
1 3 6
2 3 6
1 4 6
2 4 6

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

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

MATLAB: detect and remove mirror imaged pairs in 2 column matrix

I have a matrix
[1 2
3 6
7 1
2 1]
and would like to remove mirror imaged pairs..i.e. output would be either:
[1 2
3 6
7 1]
or
[3 6
7 1
2 1]
Is there a simple way to do this? I can imagine a complicated for loop, something like (or a version which wouldn't delete the original pair..only the duplicates):
for i=1:y
var1=(i,1);
var2=(i,2);
for i=1:y
if array(i,1)==var1 && array(i,2)==var2 | array(i,1)==var2 && array(i,2)==var1
array(i,1:2)=[];
end
end
end
thanks
How's this for simplicity -
A(~any(tril(squeeze(all(bsxfun(#eq,A,permute(fliplr(A),[3 2 1])),2))),2),:)
Playing code-golf? Well, here we go -
A(~any(tril(pdist2(A,fliplr(A))==0),2),:)
If dealing with two column matrices only, here's a simpler version of bsxfun -
M = bsxfun(#eq,A(:,1).',A(:,2)); %//'
out = A(~any(tril(M & M.'),2),:)
Sample run -
A =
1 2
3 6
7 1
6 5
6 3
2 1
3 4
>> A(~any(tril(squeeze(all(bsxfun(#eq,A,permute(fliplr(A),[3 2 1])),2))),2),:)
ans =
1 2
3 6
7 1
6 5
3 4
>> A(~any(tril(pdist2(A,fliplr(A))==0),2),:)
ans =
1 2
3 6
7 1
6 5
3 4
Here a not so fancy, but hopefully understandable and easy way.
% Example matrix
m = [1 2; 3 6 ; 7 1; 2 1; 0 3 ; 3 0];
Comparing m with its flipped version, the function ismember returns mirror_idx, a 1D-vector with each row containing the index of the mirror-row, or 0 if there's none.
[~, mirror_idx] = ismember(m,fliplr(m),'rows');
Go through the indices of the mirror-rows. If you find one "mirrored" row (mirror_idx > 0), set its counter-part to "not mirrored".
for ii = 1:length(mirror_idx)
if (mirror_idx(ii) > 0)
mirror_idx(mirror_idx(ii)) = 0;
end
end
Take only the rows that are marked as not having a mirror.
m_new = m(~mirror_idx,:);
Greetings

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

Resources