Mapping elements in vector to related, but larger vector - matlab

I need some help to map elements from a short vector to a larger vector in Matlab. I can do this with a for-loop, but I'm sure there's a way to avoid this.
I have input vectors of same size: A = [ 2 3 5 ] and B = [ 0.1 0.3 0.23 ]. Vector A contains "index" and vector B data. A third input vector is given as C = [ 2 2 2 3 3 3 3 5 5 ] and now I want to generate the vector D = [ 0.1 0.1 0.1 0.3 0.3 0.3 0.3 0.23 0.23 ].
How can I, in Matlab, generate vector D, without using for-loops?
Thank you in advance!

A = [2 3 5];
B = [0.1 0.3 0.23];
C = [2 2 2 3 3 3 3 5 5];
Use the second output of ismember to create an index vector:
[~, ind] = ismember(C, A);
D = B(ind);
Alternatively, use interp1:
D = interp1(A, B, C);

You can also use unique to look up the index of each element in C, assuming that the values correspond to exactly the values in A. If A is not sorted then we have to sort the items of B first in order to match the indexing given by unique:
A = [2 3 5];
B = [0.1 0.3 0.23];
C = [2 2 2 3 3 3 3 5 5];
[~,isort] = sort(A);
Bsort = B(isort); % sorted according to A
[~,~,k] = unique(C); % indices of items in C to pick from A
D = Bsort(k); % each matching element from (sorted) B

If the elements of the index vector are positive integers you can just use indexing:
idx(A,1) = 1:numel(A);
D = B(idx(C));
If A contains positive integers of large values you can use sparse matrix:
idx = sparse(A,1,1:numel(A));
D = B(idx(C));

Related

how can one obtain all combinations of a vector?

I want to find all possible variations (combinations) of a vector, choosing various numbers of elements from that vector.
For example, suppose I have the vector:
x = [1 2 3 4 5];
I can determine the number of combinations for each number of chosen elements:
x = [1 2 3 4 5]';
n = numel(x);
for k = 1:n
combs(k) = nchoosek(n,k);
end
sum(combs)
This results in:
combs = 5 10 10 5 1
sum(combs) = 31
I want a way to store all 31 of these combinations in an array, for example a cell array, with n cells, within each is an array in which each row is a vector combination of the elements.
e.g. at k = 4:
combs{4} =
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5
Is there an existing function that does this, or what would be the most simple approach to this?
Call nchoosek with a vector as first input, using arrayfun (or equivalently for) to loop over the number of picked elements:
n = 5;
combs = arrayfun(#(k) nchoosek(1:n,k), 1:n, 'UniformOutput', false);
Here is an approach using dec2bin , find and accumarray:
x = [1 2 3 4 5];
[a b] = find(dec2bin(1:2^numel(x)-1)=='1');
combs = accumarray(a,x(b),[],#(c){c});

Turning matrix diagonals to columns

I am looking for a matrix operation of the form: B = M*A*N where A is some general square matrix and M and N are the matrices I want to find.
Such that the columns of B are the diagonals of A. The first column the main diagonal, the second the diagonal shifted by 1 from the main and so on.
e.g. In MATLAB syntax:
A = [1, 2, 3
4, 5, 6
7, 8, 9]
and
B = [1, 2, 3
5, 6, 4
9, 7, 8]
Edit:
It seems a pure linear algebra solution doesn't exist. So I'll be more precise about what I was trying to do:
For some vector v of size 1 x m. Then define C = repmat(v,m,1). My matrix is A = C-C.';.
Therefore, A is essentially all differences of values in v but I'm only interested in the difference up to some distance between values.
Those are the diagonals of A; but m is so large that the construction of such m x m matrices causes out-of-memory issues.
I'm looking for a way to extract those diagonals in a way that is as efficient as possible (in MATLAB).
Thanks!
If you're not actually looking for a linear algebra solution, then I would argue that constructing three additional matrices the same size as A using two matrix multiplications is very inefficient in both time and space complexity. I'm not sure it's even possible to find a matrix solution, given my limited knowledge of linear algebra, but even if it is it's sure to be messy.
Since you say you only need the values along some diagonals, I'd construct only those diagonals using diag:
A = [1 2 3;
4 5 6;
7 8 9];
m = size(A, 1); % assume A is square
k = 1; % let's get the k'th diagonal
kdiag = [diag(A, k); diag(A, k-m)];
kdiag =
2
6
7
Diagonal 0 is the main diagonal, diagonal m-1 (for an mxm matrix) is the last. So if you wanted all of B you could easily loop:
B = zeros(size(A));
for k = 0:m-1
B(:,k+1) = [diag(A, k); diag(A, k-m)];
end
B =
1 2 3
5 6 4
9 7 8
From the comments:
For v some vector of size 1xm. Then B=repmat(v,m,1). My matrix is A=B-B.'; A is essentially all differences of values in v but I'm only interested in the difference up to some distance between values.
Let's say
m = 4;
v = [1 3 7 11];
If you construct the entire matrix,
B = repmat(v, m, 1);
A = B - B.';
A =
0 2 6 10
-2 0 4 8
-6 -4 0 4
-10 -8 -4 0
The main diagonal is zeros, so that's not very interesting. The next diagonal, which I'll call k = 1 is
[2 4 4 -10].'
You can construct this diagonal without constructing A or even B by shifting the elements of v:
k = 1;
diag1 = circshift(v, m-k, 2) - v;
diag1 =
2 4 4 -10
The main diagonal is given by k = 0, the last diagonal by k = m-1.
You can do this using the function toeplitz to create column indices for the reshuffling, then convert those to a linear index to use for reordering A, like so:
>> A = [1 2 3; 4 5 6; 7 8 9]
A =
1 2 3
4 5 6
7 8 9
>> n = size(A, 1);
>> index = repmat((1:n).', 1, n)+n*(toeplitz([1 n:-1:2], 1:n)-1);
>> B = zeros(n);
>> B(index) = A
B =
1 2 3
5 6 4
9 7 8
This will generalize to any size square matrix A.

repmat and/or transpose into singleton dimensions

Let's say I have a non-empty vector z.
I would like to replicate and shape this vector into a matrix with dimensions [r, c, length(z)].
For example:
z = 1:5;
r = 3;
c = 3;
I am looking for a function that gives me:
ans(:, :, 1) =
[ 1 1 1 ]
[ 1 1 1 ]
[ 1 1 1 ]
ans(:, :, 2) =
[ 2 2 2 ]
[ 2 2 2 ]
[ 2 2 2 ]
...
ans(:, :, 5) =
[ 5 5 5 ]
[ 5 5 5 ]
[ 5 5 5 ]
NOTE:
Doing something like
tmp = zeros(r,c,length(z));
tmp = repmat(z, 3, 3, 1);
doesn't work. Instead it returns a 15x15 matrix as
tmp =
[ 1:5, 1:5, 1:5 ]
[ 1:5, 1:5, 1:5 ]
[ 1:5, 1:5, 1:5 ]
which is not what I want.
Transposing z first doesn't work either.
The only solution I know is to initially set z to be the 3rd dimension of a vector:
z(1,1,:) = 1:5;
Is there more efficient way to do this? Or is this the most effective approach?
COROLLARY: Is there a "transpose"-like function that transposes a vector into singleton dimension? That is, if transpose() shapes row vectors into column vectors and vice versa, is there a function that shapes a row/column vector into singleton dimensions and back?
Thanks in advance!
First permute the vector so that it's a single 3D vector, then use repmat:
z = permute(1:5, [1 3 2]);
r = 3; c = 3;
out = repmat(z, [r c]);
We get:
>> out
out(:,:,1) =
1 1 1
1 1 1
1 1 1
out(:,:,2) =
2 2 2
2 2 2
2 2 2
out(:,:,3) =
3 3 3
3 3 3
3 3 3
out(:,:,4) =
4 4 4
4 4 4
4 4 4
out(:,:,5) =
5 5 5
5 5 5
5 5 5
permute works by shuffling the dimensions of the input vector around. What we're doing here is that we are switching the column values so that they appear in slices of a single 3D vector. We then replicate this 3D vector for as many rows and as many columns as you want.
If permute is confusing, then assuming z hasn't been allocated, you can also do this:
z(1,1,:) = 1:5;
You can then go ahead and use this z with the same repmat syntax that I talked about before.
You can generate that using ndgrid
z = 1:5;
r = 3;
c = 3;
[~,~,out]=ndgrid(1:r,1:c,z)
or using bsxfun and permute
out = bsxfun(#plus,permute(z,[3,1,2]),zeros(r,c));

Find the minimum positive difference between elements in vector

A = [1 3 5 8]
B = [1 2 3 4 5 6 7 8]
I would like to create a vector C which returns the rownumber of the element in vector A with the smallest non-negative difference to each element in vector B.
So, given the example above, it should return:
C = [1 2 2 3 3 4 4 4]
I'm sure there are many ways to do this. Here's one:
A = [1 3 5 8]
B = [1 2 3 4 5 6 7 8]
%create matrices of the values to subtract
[a,b] = meshgrid(A,B);
%subtract
aLessB = a-b;
%make sure we don't use the negative values
aLessB(aLessB < 0) = Inf;
%sort the subtracted matrix
[dum, idx] = sort(aLessB, 2, 'ascend');
idx(:,1) is the solution you are looking for.
An alternative solution:
D = bsxfun(#minus, A', B);
D(D < 0) = Inf;
[~, C] = min(D, [], 1);

Construct a matrix from existing two other matrices, all with different sizes

I have two matrices
a = randi ([0 10], 5, 6)
b = randi ([0 10], 2, 45)
Now I want to construct a matrix c of size 8 x 15 with all the elements of a and b. Is it possible to do it in a single line code? Some suggestions please.
Here is an example of what I'm trying to do:
a = [1 4 6;
5 8 0;
3 7 9;
4 10 5];
b = [5 6;
5 0];
c = [1 4 6 5;
8 0 3 7;
9 4 10 5;
5 6 5 0]
The specifications for how to combine a and b aren't clear. Here is one way to do it.
Create a single column vector built from a and b. Then reshape that column vector into a matrix.
c = reshape( [ a(:); b(:) ], 8, 15);
This will only work if the numel(a) + numel(b) equals the total number of elements in c.
Attempts to execute c = reshape( [ a(:); b(:) ], 7,12); will fail as you aren't providing enough elements to create an 7x12 matrix.
Update
Noufal's comment on this answer changes the problem reqs a bit. Basically you stil create the column vector but you only populate C depending on how many elements you have at your disposal:
A = rand(5,6);
B = rand(2,45);
C = zeros(8,10);
tmp = [A(:); B(:)]; % create temporary column vector
maxIdx = min( [numel(tmp), numel(C)] ); % determine if tmp or C has fewer elements
C(1:maxIdx) = tmp(1:maxIdx); % fill C from tmp using indices 1:maxIdx