Find row-wise combinations of a 2 dimensional matrix - matlab

I have a matrix:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
I want to find all row-wise combinations of X. i.e.
A(1) = [2, 6, 1; 3, 8, 1; 4, 7, 1]
A(2) = [2, 6, 1; 3, 8, 1; 6, 2, 1]
:
:
:
Here's what I've tried:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3
[m, n] = size(X);
comb = combnk(1:m, p);
[s, t] = size(comb);
c = [X(comb(:,1), :, :) X(comb(:,2), :, :) X(comb(:,3), :, :)];
This gives me a matrix like:
c = 2 6 1 3 8 1 4 7 1
2 6 1 3 8 1 6 2 1
2 6 1 3 8 1 6 4 1
I want to apply the concatenate matrix option to obtain c to make it dynamic depending on value of p but I'm not sure how to use it. I don't want to use For loops. Please help me out.

This is fully vectorized, so it should be fast:
n = 3; %// number of rows to pick each time
ind = reshape(nchoosek(1:size(X,1), n).', [], 1); %'// indices of combinations
A = permute(reshape(X(ind,:).', size(X,2), n, []), [2 1 3]);
The result is
A(:,:,1)
ans =
2 6 1
3 8 1
4 7 1
A(:,:,2)
ans =
2 6 1
3 8 1
6 2 1
etc.
Should you need the result in the form of a cell array, you can convert A from 3D-array to cell array this way:
A = mat2cell(A, size(A,1), size(A,2), ones(1,size(A,3)));

Your thinking is pretty close. This code does the job. I put comments in code, which should be easy to read.
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3;
%// List all combinations choosing 3 out of 1:8.
v = nchoosek(1:size(X,1), p);
%// Use each row of v to create the matrices, and put the results in an cell array.
%// This is the A matrix in your question.
A = arrayfun(#(k)X(v(k,:), :), 1:size(v,1), 'UniformOutput', false);
%// And you can concatenate A vertically to get c.
flatA = cellfun(#(x)reshape(x, 1, []), A, 'UniformOutput', false);
c = vertcat(flatA{:});
PS: From my understanding I thought the result you wanted was A, which is an easy to use cell array. But I added an extra step to get c exactly as in your question just in case.
Disclaimer: arrayfun and cellfun are pretty much equivalent to for loop in terms of performance.

You can do it using reshape and a bunch of transposes since Matlab is column-major ordered:
c = reshape(X(comb',:)',9,[])'
or if you want a 3D matrix:
A = permute(reshape(X(comb',:)',3,3,[])', [2,1,3])

Related

MATLAB : 3 dimensional matrix with multipication with a vector

I have A matrix which is 16x16x155460. I have a B vector which is 12955x1. I want to multiply each 1:16x1:16x1+12*n:12+12*nwith the elements of B(n). So my goal is to find the weighted sum of the A according to B. My way to do this as follows (I don't want to use for-loop and my method gives wrong answer, I could not obtain the 1:12 vectors which is consecutive) :
B = repmat(B,[1 16 16]);
B = permute(B,[2 3 1]);
B = repmat(B,[1 1 12]);
result = B.*(A);
As a small example n=2 :
A(:,:,1)=[1 2; 3 4]
A(:,:,2)=[1 2; 3 4]
A(:,:,3)=[1 2; 3 4]
A(:,:,4)=[1 2; 3 4]
B = [2,3]
Result would be:
result(:,:,1)=A(:,:,1)*B(1);
result(:,:,2)=A(:,:,2)*B(1);
result(:,:,3)=A(:,:,1)*B(2);
result(:,:,4)=A(:,:,2)*B(2);
If I understood the problem correctly, you can use the powerful trio of bsxfun, permute and reshape to solve it, like so -
[M,N,R] = size(A);
mult_out = bsxfun(#times,reshape(A,M,N,numel(B),[]),permute(B(:),[4 3 1 2]))
out = reshape(mult_out,M,N,[])

Find part of vector in another vector matlab

I would like to know if there is an easy way to find the indices of a vector in another vector in matlab:
a = [1 2 3 5 7 10 2 3 6 8 7 5 2 4 7 2 3]
b = [2 3]
So how to get the indices of a when comparing it with b (index of first element is needed)
In this case:
ans = [2 7 16]
Thanks in advance
find(a(1:end-1) == b(1) & a(2:end) == b(2) == 1)
You can re-purpose strfind by converting the elements of both vectors to byte arrays (uint8) with typecast:
bytesPerEl = numel(typecast(a(1),'uint8'));
byteLocs = strfind(char(typecast(a,'uint8')),char(typecast(b,'uint8')));
locsb = (byteLocs-1)/bytesPerEl + 1
locsb =
2 7 16
Just make sure a and b are of the same type. Also note that this works for 1D vectors, not matrixes or higher dimensional arrays.
General approach with length of b arbitrary (not necessarily 2 as in the example), and avoiding the use of strings:
match1 = bsxfun(#eq, a(:), b(:).'); %'// now we just need to make the diagonals
%// horizontal (in order to apply "all" row-wise). For that we'll use indices
%// ind, ind1, ind2
ind = reshape(1:numel(match1), numel(a), numel(b));
ind1 = nonzeros(tril(ind)); %// source indices
ind2 = sort(nonzeros(tril(flipud(ind)))); %// destination indices
match2 = zeros(size(match1));
match2(ind2) = match1(ind1); %// diagonals have become horizontal
result = find(all(match2.'));

How to assemble small n-d arrays into a larger n-d array?

The code below produces a cell array containing n (=210) 2x3x4-shaped n-d arrays.
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
For each i ∈ {1, … ,n}, the contents of the i-th n-d array are all integers whose initial digits match those of i. For example, for i = 123:
>> ndarrays{123}
ans(:,:,1) =
123001 123003 123005
123002 123004 123006
ans(:,:,2) =
123007 123009 123011
123008 123010 123012
ans(:,:,3) =
123013 123015 123017
123014 123016 123018
ans(:,:,4) =
123019 123021 123023
123020 123022 123024
I want to assemble all these n-d arrays into a larger [5x6x7x2x3x4 double] n-d array X in such a way that expressions of the form X(i,j,k,:,:,:) will correspond to one of the original smaller n-d arrays. This last requirement is the one that's giving me difficulty.
I have no problem producing the larger [5x6x7x2x3x4 double] n-d array from the smaller ones, but expressions of the form X(i,j,k,:,:,:) do not produce one of the original smaller n-d arrays.
Below is an example of one of the things I've tried; the last output should match below should match the output shown for ndarrays{123} above, but doesn't:
>> X = reshape(cell2mat(ndarrays), [5:7 2:4]);
>> [idx{1:3}] = ind2sub(5:7, 123);
>> squeeze(X(idx{:}, :, :, :))
ans(:,:,1) =
62001 62003 62005
167001 167003 167005
ans(:,:,2) =
62007 62009 62011
167007 167009 167011
ans(:,:,3) =
62013 62015 62017
167013 167015 167017
ans(:,:,4) =
62019 62021 62023
167019 167021 167023
EDIT: OK, by (pretty much blind) trial-and-error I found that the following does the trick:
tmp = cellfun(#(c) reshape(c, [1 24]), ndarrays, 'un', 0);
X = reshape(cat(1, tmp{:}), [5:7 2:4]);
IOW: linearize the subarrays before passing them to cat(1, ...). I'm surprised that it's necessary to explicitly perform this linearization step: it's what I expect MATLAB to do by default (in cell2mat, for example).
Be that as it may, is there a better (or at least clearer/easier to understand) way to achieve the same effect?
(BTW, for this problem, the initial shape of ndarray is given and not subject to change. IOW, solutions that require modifying the makesubarray function in the example do not fit the situation I'm dealing with.)
EDIT2: In reference to Luis Mendo's answer, here's the output (copy-pasted verbatim from my MATLAB workspace) of a small script (with echo on), showing the sizes of various items:
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
size(ndarrays)
ans =
1 210
size(permute(ndarrays, [2 3 4 1]))
ans =
210 1
size(cell2mat(permute(ndarrays, [2 3 4 1])))
ans =
420 3 4
echo off;
You can achieve it with a little bit of permute and reshape. The logic of this approach can be followed by observing size(X) at the end of each step (indicated in the comments):
X = cell2mat(permute(ndarrays(:), [2 3 4 1])); %// size [2 3 4 210]
X = permute(X, [4 5 6 1 2 3]); %// size [210 1 1 2 3 4]
X = reshape(X, [7 6 5 2 3 4]); %// size [7 6 5 2 3 4]
X = permute(X, [3 2 1 4 5 6]); %// size [5 6 7 2 3 4]

MATLAB: dividing a cell's members into smaller cells according to a vector

I have a cell array of size 1*n (X=cell(1, n)). every cell in this cell array(X), has a different size. So, to be clear, here is an example:
X = {X1, X2, X3} (n=3)
X1 = [1 3 5 9]
X2 = [1 2 3 6 7]
X3 = [1 7 8 9]
I have a vector with length of m. for example:
Y = [0 3 6 9] (m=4)
I want to divide cells in my cell array according to this vector in this way:
X1-1=[1] (because 1>=Y(1) & 1<Y(2)), X1-2=[3 5] (because 3 and 5>=Y(2) & 1<Y(3)), X1-3=[9] (because 9>=Y(3) & 9<Y(4))
and so on for X2 and X3...
and finally I have:
Z = {X1-1 X2-1 X3-1
X1-2 X2-2 X3-2
X1-3 X2-3 X3-3}
Here is my code with for-loops:
Z = cell(size(X,2), size(Y,2));
for i = 1:size(X,2)
T = cell2mat(X(i));
for j = 1:size(Y,2)-1
idx = (T > Y(j) & T < Y(j+1));
Z{i,j} = {T(idx)'};
end
end
Z = cellfun(#(z) cell2mat(z), Z, 'UniformOutput', false);
I want to get the same results without using for-loops. Any vectorization ideas?
You can replace the two for loops with one cellfun and one arrayfun. Not necessarily faster, though:
Z = cellfun(#(Xi) arrayfun(#(k) Xi(Y(k)<=Xi&Xi<Y(k+1)), 1:numel(Y)-1, 'uni', 0).', X, 'uni', 0);
The desired results are stored in Z{1}{1} (corresponding to your X1-1), Z{1}{2} (corresponding to X1-2), etc.
Example: I'm using your data but changing Y to [0 3 6 9.1] (note that with Y = [0 3 6 9] the X1-3 in your example is not correct):
>> Z{1}{1}
ans =
1
>> Z{1}{2}
ans =
3 5
>> Z{1}{3}
ans =
9
>> Z{2}{1}
ans =
1 2

How to find the mapping after permutation of a 2-d matrix in Matlab

I have two 2-dimensional matrices A,B, where B is produced by a (row-wise) permutation of A. There are a few repetitive records in A (and so in B). I want to find the mapping that produced B. I am using Matlab. Only one solution is sufficient for me.
Example:
A = [ 2 3 4; 4 5 6; 2 3 4];
B = [ 4 5 6; 2 3 4; 2 3 4];
The mapping would be:
p = [3 1 2] // I want this mapping, however the solution p= [2 1 3] is also correct and acceptable
where A = B(p,:) in Matlab. // EDITED
Regards
low hanging fruits first.
Suppose there are no duplicate rows:
% compute the permutation matrix
P = all( bsxfun( #eq, permute( A, [1 3 2]),permute(B,[3 1 2]) ), 3 );
[~, p] = max(P, [], 2 ); % gives you what you want
If there are duplicates, we need to "break ties" in the rows/columns of P:
n = size(A,1);
bt = abs( bsxfun(#minus, 1:n, (1:n)' ) )/n; %//'
[~, p] = max( P+bt, [], 2 );
Since we know that A and B always have the same rows, let's look for a transformation that will convert each one to a common identical representation. How about sort?
[As, Ai] = sortrows(A);
[Bs, Bi] = sortrows(B);
Now A(Ai,:) == B(Bi,:), so all we have to do is find the indices for Bi that match Ai. Bi is a forward mapping, Ai is a reverse mapping. So:
p = zeros(size(A,1),1);
p(Ai) = Bi;
(Answer edited to match edit of problem statement)
Here is a solution using sort() to get around the problem of needing to generate all permutations.
The idea is to sort both A and B which will produce the same sorted matrix. The permutation can now be found by using the indices IA and IB that produce the two sorted matrices.
A = [ 2 3 4; 4 5 6; 2 3 4];
B = [ 4 5 6; 2 3 4; 2 3 4];
[CA,IA]=sort(A,1)
[CB,IB]=sort(B,1)
idxA = IA(:,1)
idxB = IB(:,1)
[~, idxB_inverse] = sort(idxB)
idxA(idxB_inverse)