Reordering a vector in Matlab? - arrays

I have a vector in Matlab B of dimension nx1 that contains the integers from 1 to n in a certain order, e.g. n=6 B=(2;4;5;1;6;3).
I have a vector A of dimension mx1 with m>1 that contains the same integers in ascending order each one repeated an arbitrary number of times, e.g. m=13 A=(1;1;1;2;3;3;3;4;5;5;5;5;6).
I want to get C of dimension mx1 in which the integers in A are reordered following the order in B. In the example, C=(2;4;5;5;5;5;1;1;1;6;3;3;3)

One approach with ismember and sort -
[~,idx] = ismember(A,B)
[~,sorted_idx] = sort(idx)
C = B(idx(sorted_idx))
If you are into one-liners, then another with bsxfun -
C = B(nonzeros(bsxfun(#times,bsxfun(#eq,A,B.'),1:numel(B))))

This requires just one sort and indexing:
ind = 1:numel(B);
ind(B) = ind;
C = B(sort(ind(A)));

Another approach using repelem, accumarray, unique
B=[2;4;5;1;6;3];
A=[1;1;1;2;3;3;3;4;5;5;5;5;6];
counts = accumarray(A,A)./unique(A);
repelem(B,counts(B));
%// or as suggested by Divakar
%// counts = accumarray(A,1);
%// repelem(B,counts(B));
PS: repelem was introduced in R2015a. If you are using a prior version, refer here

Another solution using hist, but with a loop and expanding memory :(
y = hist(A, max(A))
reps = y(B);
C = [];
for nn = 1:numel(reps)
C = [C; repmat(B(nn), reps(nn), 1)];
end

Related

Replace specific entries of a multidimensional arrays avoiding loops

I would like to replace the entry corresponding to the column number of an array that is part of a 3D matrix by zero. My matrix is of size IxJxJ. In each column j I can find a matrix of size IxJof which I would like to replace the jth column by zero.
You can find below an example of what I would like using a simple 3D matrix A. This example uses a loop, which is what I am trying to avoid.
A(:,:,1) = randi([1,2],5,3);
A(:,:,2) = randi([3,4],5,3);
A(:,:,3) = randi([5,6],5,3);
for i = 1:3
B = A(:,i,:);
B = squeeze(B);
B(:,i) = 0;
A(:,i,:) = B;
end
Firstly, you can replace the 4 lines of code in your for loop with just A(:,i,i) = 0;. I don't see any real need to avoid the for loop.
Using linear indexing, you can do
A((1:size(A,1)).'+size(A,1).*(size(A,2)+1).*(0:size(A,2)-1)) = 0
or for older version of Matlab without implicit expansion (pre-R2016b)
A(bsxfun(#plus,(1:size(A,1)).',size(A,1).*(size(A,2)+1).*(0:size(A,2)-1))) = 0
After some very quick testing, it actually looks like the bsxfun solution is fastest, but the differences aren't huge, your results may differ.
Use eye to create a logical mask and mutiply it by A.
A = A .* reshape(~eye(3), 1, 3, 3) ;

Variation of indices of an array or matrix

If I use this syntax:
mX=[1:5];
A=rand(5,1);
C(mX)=sum(A(1:mX));
Why doesn't the content of C(mX) vary with varying mX?
Instead of doing
C(1)=A(1)
C(2)=A(1)+A(2), etc
it does:
C(1)=A(1)
C(2)=A(1)
C(3)=A(1), etc
Is there any way to vary C(mX) without resorting to a loop?
To answer your first question:
mX=1:5;
A=rand(5,1);
C(mX)=sum(A(1:mX));
makes the sum over A(1:[1 2 3 4 5]), which results in A(1:1), and hence all your C(mX) values will be filled with purely the element A(1).
What you want to do is make a cumulative sum, which can be done, as #leanderMoesinger mentioned with cumsum:
A=rand(5,1);
C = cumsum(A)
C =
0.0975
0.3760
0.9229
1.8804
2.8453
If you want to learn more about indexing I can highly recommend the following post: Linear indexing, logical indexing, and all that
If you want not all elements of A, but e.g. up to element three you can do
mX = 1:3;
A = rand(5,1);
C = cumsum(A(mX)); calculate only to mX
mX = [1 3 5];
C = cumsum(A(mX)) % Also works if you only want elements 1 3 and 5 to appear
% If you want elements of C 1 3 and 5 use
tmp = cumsum(A);
C = tmp(mX);
You can do this by cumsum like so:
mX=[1:5];
A=rand(5,1);
C = cumsum(A(mX));

MATLAB pairwise differences in Nth dimension

Say i have a N-Dimensional matrix A that can be of any size. For example:
A = rand([2,5,3]);
I want to calculate all possible pairwise differences between elements of the matrix, along a given dimension. For example, if i wanted to calculate the differences along dimension 3, a shortcut would be to create a matrix like so:
B = cat(3, A(:,:,2) - A(:,:,1), A(:,:,3) - A(:,:,1), A(:,:,3) - A(:,:,2));
However, i want this to be able to operate along any dimension, with a matrix of any size. So, ideally, i'd like to either create a function that takes in a matrix A and calculates all pairwise differences along dimension DIM, or find a builtin MATLAB function that does the same thing.
The diff function seems like it could be useful, but it only calculates differences between adjacent elements, not all possible differences.
Doing my research on this issue, i have found a couple of posts about getting all possible differences, but most of these are for items in a vector (and ignore the dimensionality issue). Does anyone know of a quick fix?
Specific Dimension Cases
If you don't care about a general solution, for a dim=3 case, it would be as simple as couple lines of code -
dim = 3
idx = fliplr(nchoosek(1:size(A,dim),2))
B = A(:,:,idx(:,1)) - A(:,:,idx(:,2))
You can move around those idx(..) to specific dimension positions, if you happen to know the dimension before-hand. So, let's say dim = 4, then just do -
B = A(:,:,:,idx(:,1)) - A(:,:,:,idx(:,2))
Or let's say dim = 3, but A is a 4D array, then do -
B = A(:,:,idx(:,1),:) - A(:,:,idx(:,2),:)
Generic Case
For a Nth dim case, it seems you need to welcome a party of reshapes and permutes -
function out = pairwise_diff(A,dim)
%// New permuting dimensions
new_permute = [dim setdiff(1:ndims(A),dim)];
%// Permuted A and its 2D reshaped version
A_perm = permute(A,new_permute);
A_perm_2d = reshape(A_perm,size(A,dim),[]);
%// Get pairiwse indices for that dimension
N = size(A,dim);
[Y,X] = find(bsxfun(#gt,[1:N]',[1:N])); %//' OR fliplr(nchoosek(1:size(A,dim),2))
%// Get size of new permuted array that would have the length of
%// first dimension equal to number of such pairwise combinations
sz_A_perm = size(A_perm);
sz_A_perm(1) = numel(Y);
%// Get the paiwise differences; reshape to a multidimensiona array of same
%// number of dimensions as the input array
diff_mat = reshape(A_perm_2d(Y,:) - A_perm_2d(X,:),sz_A_perm);
%// Permute back to original dimension sequence as the final output
[~,return_permute] = sort(new_permute);
out = permute(diff_mat,return_permute);
return
So much for a generalization , huh!

Trimming vals in a matrix of arbitrary dimensions in a specified dimension

I'm writing a function that requires some values in a matrix of arbitrary dimansions to be dropped in a specified dimension.
For example, say I have a 3x3 matrix:
a=[1,2,3;4,5,6;7,8,9];
I might want to drop the third element in each row, in which case I could do
a = a(:,1:2)
But what if the dimensions of a are arbitrary, and the dimension to trim is defined as an argument in the function?
Using linear indexing, and some carefully considered maths is an option but I was wondering if there is a neater soltion?
For those interested, this is my current code:
...
% Find length in each dimension
sz = size(dat);
% Get the proportion to trim in each dimension
k = sz(d)*abs(p);
% Get the decimal part and integer parts of k
int_part = fix(k);
dec_part = abs(k - int_part);
% Sort the array
dat = sort(dat,d);
% Trim the array in dimension d
if (int_part ~=0)
switch d
case 1
dat = dat(int_part + 1 : sz(1) - int_part,:);
case 2
dat = dat(:,int_part + 1 : sz(2) - int_part);
end
end
...
It doesn't get any neater than this:
function A = trim(A, n, d)
%// Remove n-th slice of A in dimension d
%// n can be vector of indices. d needs to be scalar
sub = repmat({':'}, 1, ndims(A));
sub{d} = n;
A(sub{:}) = [];
This makes use of the not very well known fact that the string ':' can be used as an index. With due credit to this answer by #AndrewJanke, and to #chappjc for bringing it to my attention.
a = a(:, 1:end-1)
end, used as a matrix index, always refers to the index of the last element of that matrix
if you want to trim different dimensions, the simplest way is using and if/else block - as MatLab only supports 7 dimensions at most, you wont need an infinite number of these to cover all bases
The permute function allows to permute the dimension of an array of any dimension.
You can place the dimension you want to trim in a prescribed position (the first, I guess), trim, and finally restore the original ordering. In this way you can avoid running loops and do what you want compactly.

Efficient concatenation of multiple, varying in size matrices in a loop

I would like to efficiently concatenate multiple matrices into a vector. However, the number of such matrices and their sizes vary. Say, I have two stacks A and B, each consisting of m matrices.
Naive approach would be the following:
merged = [];
for i = 1 : m
merged = [merged ; A{i}(:) ; B{i}(:)];
end
The challenging part is to optimise the above code to avoid copying the older array contents to the new array as it makes each assignment. For instance, one could compute the number of elements in each matrix and then preallocate a vector capable of storing all the elements. Still, I am not entirely sure how to efficiently place the matrices inside the vector.
Any suggestions would be appreciated.
One possible approach:
merged = cellfun(#(x) x(:), [A(:) B(:)].', 'uni', false);
merged = vertcat(merged{:});
Depending on the size of A and B the follow could be faster
C = {A{:} ; B{:}};
merged = vertcat(C{:});
(starting to show above numel(A) > 500)
I used the following as test data
m = 1000;
A = cell(m,1);
B = cell(m,1);
for i=1:m
A{i} = round(10*rand(max(round(11*rand),1),1));
B{i} = round(10*rand(max(round(5*rand),1),1));
end

Resources