Pairs of positions of equal elements of a matrix in Matlab - matlab

I have a row vector A in Matlab containing possibly repeated integers. I would like your help to construct a matrix B reporting all possible pairs of positions of equal elements of A. As stated in the comments below, the hard part is that I don't want to list in B "redundant" pairs.
Let me explain better with an example.
clear
A=[100 101 100 100 101 200];
We can see that
%A(1)=A(3)=A(4);
%A(2)=A(5);
Hence,
B=[1 3; 1 4; 2 5];
or, equivalently,
B=[1 3; 3 4; 2 5];
B=[1 4; 3 4; 2 5];
I am indifferent among getting any of the three vectors B reported above.
Notice that I don't want
B=[1 3; 1 4; 3 4; 2 5];
as one of the pairs among (1,3), (1,4), (3,4) is redundant, i.e., if A(1)=A(3) and A(1)=A(4), then A(4)=A(3) and similarly for other combinations.
I tried with unique but none of the outputs delivered by unique seems to give the desired matrix. Any help?

If you don't like to use loops, As suggested in the #Wolfie's answer, you can use accumarray:
[~,~,idx]=unique(A,'stable');
B = accumarray ...
( ...
idx(:), ...
(1:numel(A)).', ...
[], ...
#(x) ...
{ ...
[repmat(x(1),numel(x)-1,1) x(2:end,1)] ...
} ...
);
result = vertcat(B{:})

The part which requires thinking here is the redundant pairings. The easiest approach to remove redundant pairings is to have one key index for each value, and link all matching values to that index.
In the case of your example, this means using the following relations
% A(1) = A(3)
% A(1) = A(4)
% A(2) = A(5)
It's implied by any equivalences to the first of each value that, for instance, A(3)=A(4).
To do this, we can use the last indexing output from unique, then loop through to set up this equivalence indexing. See the below code, with comments for understanding:
% A is the input row vector
A=[100 101 100 100 101 200];
% Get the 'unique' indexing output
[~, ~, juA] = unique(A);
% Set up output as cell so we don't have to worry about how many rows each
% equivalence will take up.
B = cell( max(juA), 1 );
% Loop through all of the unique indices
for ii = 1:max(juA)
% Get indices where the value is equal to the current value
k = find( juA == ii );
% Output for this value is [1 x; 1 y; 1 z; ...] where x/y/z are indices
% of equivalent values
B{ii} = [repmat(k(1), numel(k)-1, 1), k(2:end)];
end
% Concatenate cell array B to be a 2 column numeric array
B = vertcat(B{:});
Output:
>> B = [1 3; 1 4; 2 5]

Related

extract 3D matrix's columns based on "surface" values - vectorization

I have an NxMxK matrix A
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3])
A(:,:,1) =
1 2 1
1 1 2
A(:,:,2) =
3 3 3
3 3 3
and I want to create a YxK 2D matrix B where K is the number of elements of A(:,:,1)==2:
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = A(ii,jj,:);
end
end
end
Is there a way of vectorizing this code?
My attempt was to find the indices of A(:,:,1)==2 and then try to select the whole column but I do not know how to do it:
inds = find(A(:,:,1)==2)
B = A(inds,:) %this line does not make sense
EDIT
Preallocating B helps:
inds=find(A(:,:,1)==2);
B=NaN(numel(inds),size(A,3));
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = squeeze(A(ii,jj,:));
end
end
end
But still not vectorized.
You can reshape matrix A to a (N*M)xK 2D matrix.
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3]);
A_ = reshape(A,numel(A(:,:,1)),size(A,3));
B = A_(A_(:,1)==2,:);
Your first attempt at vectorization is almost right. Just don't use find, but use the logical matrix for indexing.
inds = A(:,:,1)==2;
The inds matrix is 2D, not 3D, so we use repmat to repeat its values along the 3rd dimension:
K = size(A,3);
inds = repmat(inds,1,1,K); % or simply cat(3,inds,inds) if K==2
B = A(inds);
The result is a column vector of size Y*K, not a matrix of size YxK, we can use reshape to fix that:
B = reshape(B,[],K);
I guess this answer is similar to Anthony's, except the indexing and the reshaping are reversed. I didn't really notice the similarity until after I wrote it down. I guess also Anthony's is a little shorter. :/

Convert a cell containing matrices to a 2d matrix

I want to find easy way to convert a 1x324 cell array which contains matrices to a 2-dimensional matrix.
Each of the cell array's elements is a matrix of size 27x94, so they contain 2538 different values. I want to convert this cell array of matrices to a 324x2538 matrix - where the rows of the output contain each matrix (as a row vector) from the cell array.
To clarify what my data looks like and what I'm trying to create, see this example:
matrix1 = [1,2,3,4,...,94 ; 95,96,97,... ; 2445,2446,2447,...,2538]; % (27x94 matrix)
% ... other matrices are similar
A = {matrix1, matrix2, matrix3, ..., matrix324}; % Matrices are in 1st row of cell array
What I am trying to get:
% 324x2538 output matrix
B = [1 , 2 , ..., 2538 ; % matrix1
2539 , 2540, ..., 5076 ; % matrix2
...
819775, 819776, ..., 822312];
The cell2mat function does exactly that. The doc example:
C = {[1], [2 3 4];
[5; 9], [6 7 8; 10 11 12]};
A = cell2mat(C)
A =
1 2 3 4
5 6 7 8
9 10 11 12
You have your matrix now, so just rework it to contain rows:
B = rand(27,302456); % your B
D = reshape(B,27,94,324); % stack your matrices to 3D
E = reshape(D,1, 2538,324); % reshape each slice to a row vector
E = permute(E,[3 2 1]); % permute the dimensions to the correct order
% Based on sizes instead of fixed numbers
% D = reshape(B, [size(A{1}) numel(A)]);
% E = reshape(D,[1 prod(size(A{1})) numel(A)]);
% E = permute(E,[3 2 1]); % permute the dimensions to the correct order
Or, to one line it from your B:
B = reshape(B,prod(size(A{1})),numel(A)).'
One way to write this would be using cellfun to operate on each element of the cell, then concatenating the result.
% Using your input cell array A, turn all matrices into column vectors
% You need shiftdim so that the result is e.g. [1 2 3 4] not [1 3 2 4] for [1 2; 3 4]
B = cellfun(#(r) reshape(shiftdim(r,1),[],1), A, 'uniformoutput', false);
% Stack all columns vectors together then transpose
B = [B{:}].';
Now I found the solution and I will add it here if anyone have similar problems in future:
for ii = 1:length(A)
B{ii} = A{ii}(:);
end
B = cell2mat(B).';

Find a pair of numbers in a matrix in Matlab

Given the matrix A = [6 4 1; 1 4 3; 3 4 2;7 6 8] and the array of pairs b = [4 6; 4 1; 1 6], I want to find the pairs given in b in the rows of A without a for loop.
For example, the first pairs is (4,6) or (6,4) , which occurs in the first row of A.
Assuming, you want to find the rows of A which contain the exact pairs given in b, this is how you can do it without a loop:
% Create a matrix of pairs in A
pairs = cat(3, A(:, 1:end-1), A(:, 2:end));
% Reshape b to use bsxfun
b_ = reshape(b', [1 1 size(b')]);
% Get the matches for the pairs and for the flipped pairs
indices = all( bsxfun(#eq, pairs, b_), 3) | all( bsxfun(#eq, pairs, flip(b_,3)), 3);
% Find the indices of the rows with a match
row_indices = find(squeeze(any(any(indices,4),2)));
Please refer to the reference on vectorization for more information on how to make fast computations in Matlab without loops.

How to index a matrix with the column maxima of other matrix

I have 2 matrices A and B.
I find the max values in the columns of A, and keep their indices in I. So far so good.
Now, I need to choose those arrays of B with the same index as stored in I. I don't know how to do this.
See below:
A = [1,2,3; 0,8,9]
B = [0,1,2; 4,2,3]
[~,I] = max(A)
h = B(I)
I need to get these values of B:
h = [0 2 3]
But the code results in a different one. How can I fix it?
A =
1 2 3
0 8 9
B =
0 1 2
4 2 3
I =
1 2 2
h =
0 4 4
Thanks in advance
The max function how you used it works like
If A is a matrix, then max(A) is a row vector containing the maximum value of each column.
so M = max(A) is equivalent to M = max(A,[],1). But rather use the third input if you're not sure.
If you use max to find the maxima in the columns of the matrix, it returns the row indices. The column indices are for your case simply 1:size(A,2) = [1 2 3].
Now you need to convert your row and column indices to linear indices with sub2ind:
%// data
A = [1,2,3; 0,8,9]
B = [0,1,2; 4,2,3]
%// find maxima of each column in A
[~, I] = max( A, [], 1 ) %// returns row indices
%// get linear indices for both, row indices and column indices
I = sub2ind( size(A), I, 1:size(A,2) )
%// index B
h = B(I)
returns:
h =
0 2 3

Getting the N-dimensional product of vectors

I am trying to write code to get the 'N-dimensional product' of vectors. So for example, if I have 2 vectors of length L, x & y, then the '2-dimensional product' is simply the regular vector product, R=x*y', so that each entry of R, R(i,j) is the product of the i'th element of x and the j'th element of y, aka R(i,j)=x(i)*y(j).
The problem is how to elegantly generalize this in matlab for arbitrary dimensions. This is I had 3 vectors, x,y,z, I want the 3 dimensional array, R, such that R(i,j,k)=x(i)*y(j)*z(k).
Same thing for 4 vectors, x1,x2,x3,x4: R(i1,i2,i3,i4)=x1(i1)*x2(i2)*x3(i3)*x4(i4), etc...
Also, I do NOT know the number of dimensions beforehand. The code must be able to handle an arbitrary number of input vectors, and the number of input vectors corresponds to the dimensionality of the final answer.
Is there any easy matlab trick to do this and avoid going through each element of R specifically?
Thanks!
I think by "regular vector product" you mean outer product.
In any case, you can use the ndgrid function. I like this more than using bsxfun as it's a little more straightforward.
% make some vectors
w = 1:10;
x = w+1;
y = x+1;
z = y+1;
vecs = {w,x,y,z};
nvecs = length(vecs);
[grids{1:nvecs}] = ndgrid(vecs{:});
R = grids{1};
for i=2:nvecs
R = R .* grids{i};
end;
% Check results
for i=1:10
for j=1:10
for k=1:10
for l=1:10
V(i,j,k,l) = R(i,j,k,l) == w(i)*x(j)*y(k)*z(l);
end;
end;
end;
end;
all(V(:))
ans = 1
The built-in function bsxfun is a fast utility that should be able to help. It is designed to perform 2 input functions on a per-element basis for two inputs with mismatching dimensions. Singletons dimensions are expanded, and non-singleton dimensions need to match. (It sounds confusing, but once grok'd it useful in many ways.)
As I understand your problem, you can adjust the dimension shape of each vector to define the dimension that it should be defined across. Then use nested bsxfun calls to perform the multiplication.
Example code follows:
%Some inputs, N-by-1 vectors
x = [1; 3; 9];
y = [1; 2; 4];
z = [1; 5];
%The computation you describe, using nested BSXFUN calls
bsxfun(#times, bsxfun(#times, ... %Nested BSX fun calls, 1 per dimension
x, ... % First argument, in dimension 1
permute(y,2:-1:1) ) , ... % Second argument, permuited to dimension 2
permute(z,3:-1:1) ) % Third argument, permuted to dimension 3
%Result
% ans(:,:,1) =
% 1 2 4
% 3 6 12
% 9 18 36
% ans(:,:,2) =
% 5 10 20
% 15 30 60
% 45 90 180
To handle an arbitrary number of dimensions, this can be expanded using a recursive or loop construct. The loop would look something like this:
allInputs = {[1; 3; 9], [1; 2; 4], [1; 5]};
accumulatedResult = allInputs {1};
for ix = 2:length(allInputs)
accumulatedResult = bsxfun(#times, ...
accumulatedResult, ...
permute(allInputs{ix},ix:-1:1));
end