MATLAB: Permute and reshape a N x 4 array into a 2 x 2 x N array - arrays

I have a question that seems like it should have a simple answer that can avoid for loops.
Suppose I have an N x 4 array defined in MATLAB:
A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4];
In this example, N = 6, but it is arbitrary. I want to re-arrange A into a new array, B, which is 2 x 2 x N array of the form:
B(:,:,1) = [1 2; 3 4];
B(:,:,2) = [1 2; 3 4];
...
B(:,:,N) = [1 2; 3 4];
This seems like a simple problem and I tried a variety of things such as:
B = reshape(A',2,2,N);
However, this results in
B(:,:,1) = [1 3; 2 4];
B(:,:,2) = [1 3; 2 4];
...
B(:,:,N) = [1 3; 2 4];
I feel like there must be a simple way of doing this in one line using some combination of "reshape", "permute" and/or "transpose" that I am missing. Any suggestions are appreciated.

You are only missing a final permute. This is needed because Matlab is column-major, so it fills the new array down, then across:
B = permute(reshape(A.', 2,2,N), [2 1 3]);

Related

Finding multiple coincidences between two vectors

I have two vectors, and I'm trying to find ALL coincidences of one on the other within a certain tolerance without using a for loop.
By tolerance I mean for example if I have the number 3, with tolerance 2, I will want to keep values within 3±2, so (1,2,3,4,5).
A = [5 3 4 2]; B = [2 4 4 4 6 8];
I want to obtain a cell array containing on each cell the numbers of all the coincidences with a tolerance of 1 (or more) units. (A = B +- 1)
I have a solution with zero units (A = B), which would look something like this:
tol = 0;
[tf, ia] = ismembertol(B,A,tol,'DataScale',1); % For tol = 0, this is equivalent to using ismember
idx = 1:numel(B);
ib = accumarray(nonzeros(ia), idx(tf), [], #(x){x}) % This gives the cell array
The output is:
ib =
[]
[]
[2 3 4]
[1]
Which is as desired.
If I change the tolerance to 1, the code doesn't work as intended. It outputs instead:
tol = 1
[tf, ia] = ismembertol(B,A,tol,'DataScale',1); % For tolerance = 1, this is equivalent to using ismember
idx = 1:numel(B);
ib = accumarray(nonzeros(ia), idx(tf), [], #(x){x}) % This gives the cell array
ib =
[5]
[2 3 4]
[]
[1]
When I would expect to obtain:
ib =
[2 3 4 5]
[1 2 3 4]
[2 3 4]
[1]
What am I doing wrong? Is there an alternative solution?
Your problem is that, in the current state of your code, ismembertol only outputs 1 index per element of B found in A, so you lose information in the cases where an element can be found several times within tolerance.
As per the documentation You can use the 'OutputAllIndices',true value pair argument syntax, to output what you want in ia with just a call to ismembertol:
A = [5 3 4 2]; B = [2 4 4 4 6 8];
tol = 0;
[tf, ia] = ismembertol(A,B,tol,'DataScale',1,'OutputAllIndices',true);
celldisp(ia) % tol = 0
ia{1} =
0
ia{2} =
0
ia{3} =
2
3
4
ia{4} =
1
celldisp(ia) % tol = 1
ia{1} =
2
3
4
5
ia{2} =
1
2
3
4
ia{3} =
2
3
4
ia{4} =
1
Here is a manual approach, just to provide another method. It computes an intermediate matrix of all absolute differences (using implicit expansion), and from the row and column indices of the entries that are less than the tolerance it builds the result:
A = [5 3 4 2];
B = [2 4 4 4 6 8];
tol = 1;
[ii, jj] = find(abs(A(:).'-B(:))<=tol);
ib = accumarray(jj, ii, [numel(A) 1], #(x){x});
Note that this approach
may be memory-intensive, because of the intermediate matrix;
can be made to work in old Matlab versions, because it doesn't use ismembertol; but then implicit expansion has to be replaced by explicitly calling bsxfun:
[ii, jj] = find(abs(bsxfun(#minus, A(:).', B(:)))<=tol);
ib = accumarray(jj, ii, [numel(A) 1], #(x){x});

How do I obtain a single array from all possible combinations of the elements of two vectors?

I have two vectors:
x = [1 2 3]; y = [4 5]
I need a single array yx that gives me one-to-one combinations of the elements of both vectors. This is the code I have tried so far using of the examples from Stackoverflow.
sets = {y, x};
[y x] = ndgrid(sets{:});
yx = [y x]'
This gives me the result:
yx =
4 5
4 5
4 5
1 1
2 2
3 3
Whereas, I am expecting the following result:
yx =
4 1
4 2
4 3
5 1
5 2
5 3
Please, what am I doing wrong here? Any help/suggestions is greatly appreciated. Thanks!
What you're trying to obtain is a cartesian product of the two vector.
Here's a solution:
>> x = [1 2 3]; y = [4 5];
>> [X,Y] = meshgrid(y,x);
>> result = [X(:) Y(:)]
result =
4 1
4 2
4 3
5 1
5 2
5 3
(this works also in Octave and does not require extra libraries)
Your final cat is wrong. You expect that x and y are column vectors but they are 2x3-matrices. To get a 2-column matrix of all pairs, you need to linearize first:
yx = [y(:) x(:)]
It outputs the data in a different order. If you want the same order, transpose x and y before vectorizing and concatenating.
you are looking for combvec(x, y)
>> x = [1 2 3]
x =
1 2 3
>> y = [4 5]
y =
4 5
>> combvec(x, y)
ans =
1 2 3 1 2 3
4 4 4 5 5 5
Here is a way to do it with no complicated functions.
x = [1 2 3];
y = [4 5];
nx = numel(x);
ny = numel(y);
xy = [reshape(repmat(y,nx,1), 1, [])', repmat(x',ny,1)];
% xy = [4 1
% 4 2
% 4 3
% 5 1
% 5 2
% 5 3
Explanation:
We know that the output will have x repeated for each element in y, named ny.
We know that the output will have each element of y repeated for each element in x, nx
repmat repeats x simply for the second column.
repmat used with reshape to "interweave" y with its repeated self to get the repeated digits in the y vector as the first column.
You could condense the code by not using nx and ny.
xy = [reshape(repmat(y,numel(x),1), 1, [])', repmat(x',numel(y),1)];

MATLAB intersection of 3d arrays

I have two 3d arrays:
A(:,:,1) = [1 2 3; 4 5 6; 7 8 9];
A(:,:,2) = [1 0 0; 0 1 0; 0 0 1];
A(:,:,3) = [3 2 1; 6 5 4; 9 8 7];
...
and
B(:,:,1) = [1 1 1; 2 2 2; 3 3 3];
B(:,:,2) = [1 0 0; 0 1 0; 0 0 1];
B(:,:,3) = [3 3 3; 2 2 2; 1 1 1];
...
They both consist of 3x3 matrices and their third dimensions are very large. I want to obtain the array of matrices that exist in both arrays. I am doing it in a for loop by comparing element-wise (matrix-wise). It takes a very long time, so I am looking for an easier way (or an existing function) to do the same.
Thanks!
Collapse the first two dimensions into one and transpose, so that matrices of the 3D array become rows of a matrix. That way you can use intersect(...,'rows'). Finally, transpose back and reshape back:
[m, n, p] = size(A);
result = intersect(reshape(A, [], p).', reshape(B, [], p).', 'rows');
result = reshape(result.', m, n, []);

Find index of smallest element in an array not in another array (Matlab)

I have an array a = [6 8 2 1 9] and b = [1 2 6]. I want a function which returns 2 since a(2)=8 which is the smallest element of a not in b.
What I have tried so far:
[A,I] = sort(a);
index = I(find(~ismember(A,b),1))
I would like something faster as this piece of code has to run many times and the arrays involved are very large.
Thanks in advance!
Another (faster, I believe) solution would be:
a = [6 8 2 1 9];
b = [1 2 6];
[d,I] = setdiff(a,b); % d is the set difference, I is the indices of d elements in a
[m,J] = min(d); % m is the minimum in d, J is it's index
I(J) % This is the index of m in d (the value that you want)
ans =
2
Does this do what you need?
>> a = [6 8 2 1 9];
>> b = [1 2 6];
>> min(a(~ismember(a,b)))
ans =
8
Edit:
Oops - I meant
>> find(a==min(a(~ismember(a,b))),1)
ans =
2
This finds the index as you requested, rather than the value, which the first answer gives.

Element-wise array replication in Matlab

Let's say I have a one-dimensional array:
a = [1, 2, 3];
Is there a built-in Matlab function that takes an array and an integer n and replicates each
element of the array n times?
For example calling replicate(a, 3) should return [1,1,1,2,2,2,3,3,3].
Note that this is not at all the same as repmat. I can certainly implement replicate by doing repmat on each element and concatenating the result, but I am wondering if there is a built in function that is more efficient.
I'm a fan of the KRON function:
>> a = 1:3;
>> N = 3;
>> b = kron(a,ones(1,N))
b =
1 1 1 2 2 2 3 3 3
You can also look at this related question (which dealt with replicating elements of 2-D matrices) to see some of the other solutions involving matrix indexing. Here's one such solution (inspired by Edric's answer):
>> b = a(ceil((1:N*numel(a))/N))
b =
1 1 1 2 2 2 3 3 3
a = [1 2 3];
N = 3;
b = reshape(repmat(a,N,1), 1, [])
As of R2015a, there is a built-in and documented function to do this, repelem:
repelem Replicate elements of an array.
W = repelem(V,N), with vector V and scalar N, creates a vector W where each element of V is repeated N times.
The second argument can also be a vector of the same length as V to specify the number of replications for each element. For 2D replication:
B = repelem(A,N1,N2)
No need for kron or other tricks anymore!
UPDATE: For a performance comparison with other speedy methods, please see the Q&A Repeat copies of array elements: Run-length decoding in MATLAB.
>> n=3;
>> a(floor((0:size(a,2)*n-1)/n)+1)
ans =
1 1 1 2 2 2 3 3 3
Some exotic alternatives. Admittedly more funny than useful:
Assign the (first) result of meshgrid to a vector:
b = NaN(1,numel(a)*n); %// pre-shape result
b(:) = meshgrid(a,1:n);
Build a matrix that multiplied by a gives the result:
b = a * fliplr(sortrows(repmat(eye(numel(a)),n,1))).';
Use ind2sub to generate the indices:
[~, ind] = ind2sub([n 1],1:numel(a)*n);
b = a(ind);
If you have the image processing toolbox, there is another alternative:
N = 3;
imresize(a, [1 N*numel(a)],'nearest')
% To get b = [1 1 1 2 2 2 3 3 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a,1,numel(temp_a));
% To get b = [1 2 3 1 2 3 1 2 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a',1,numel(temp_a));

Resources