Extend a 2D-matrix to a 3D-matrix by multiplying with a vector [duplicate] - arrays

This question already has an answer here:
Multiply 2D Matrix with vector to span third dimension - MATLAB
(1 answer)
Closed 7 years ago.
Here what I would like to achieve:
I have a matrix C
C=[1 2 3; 4 5 6; 7 8 9];
And a vector a
a=[1 2];
I would like to make such an operation, that each element of the a vector is multiplied with C ( scalar multiplication ) and out comes a 3-dimensional array D:
(:,:,1) =
1 2 3
4 5 6
7 8 9
(:,:,2) =
2 4 6
8 10 12
14 16 18
It would certainly work with a loop, but, since I'll need this operation on multiple occasions, a oneliner would be a great saver.

This is a beautiful example for the use of bsxfun and reshape. While #thewaywewalks proposes first calling bsxfun and reshaping the result, I'd suggest the opposite. This makes one of the key concepts of bsxfun - the singleton dimension expansion - more clear:
out = bsxfun(#times,C,reshape(a,1,1,[]))
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
2 4 6
8 10 12
14 16 18
With reshape(a,1,1,[]), you make a be in the third dimension. If you now apply bsxfun, it will multiply the matrix C with each element of a.

Some reshape'ing and some bsxfun will do:
out = reshape(bsxfun(#mtimes, C(:), a(:).'), [size(C),numel(a)] )
As suggested in hbaderts answer one could also use bsxfun's capability of dimension expansion, and provide a permuted vector of factors:
out = bsxfun(#mtimes,C,permute(a,[3,1,2]))
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18

I have another method for the benchmark compare...
IMO it's the neatest way, at least for the syntax/readability term:
out = reshape(kron(a,C),[size(C),numel(a)]);
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18

Another possibility is to use matrix multiplication of C as a column vector times a as a row vector (this gives all element-wise products), and then reshape the result:
out = reshape(C(:)*a, size(C,1), size(C,2), numel(a));

EDIT (BENCHMARKING): Since several solutions (including mine below) have been suggested, here is some rough benchmarking to compare the different solutions, using larger arrays:
a=1:10;
N=1000; timers=zeros(N,6);
for ii=1:N; C=rand(400);
tic; out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)]); timers(ii,1)=toc;
tic; out = bsxfun(#times,C,reshape(a,1,1,[])); timers(ii,2)=toc;
tic; out = reshape(C(:)*a, size(C,1), size(C,2), numel(a)); timers(ii,3)=toc;
tic; out = bsxfun(#mtimes,C,permute(a,[3,1,2])); timers(ii,4)=toc;
tic; out = reshape(bsxfun(#mtimes, C(:), a(:).'), [size(C),numel(a)] ); timers(ii,5)=toc;
tic; out = reshape(kron(a,C),[size(C),numel(a)]); timers(ii,6)=toc;
end;
mean(timers)
ans =
0.0080863 0.0032406 0.0041718 0.015166 0.0074462 0.0033051
... suggesting that #hbaderts solution is fastest, then #Adiel's, then #Luis Mendo's, then #thewaywewalk's (1), then mine, then #thewaywewalk's (2).
My solution:
Another option, using repmat and reshape (no bsxfun):
out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
This is the element-wise multiplication of two arrays. The first is your original matrix C repeated numel(a) times in the third dimension:
repmat(C,[1,1,numel(a)])
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
1 2 3
4 5 6
7 8 9
The second is the same size as the first, with each slice containing the corresponding element of a:
reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
ans(:,:,1) =
1 1 1
1 1 1
1 1 1
ans(:,:,2) =
2 2 2
2 2 2
2 2 2

Related

Does the function movmean in Matlab create an average of all dimensions of the matrix?

I am trying to smooth the temporal history of each pixel in my matrix- in other words, trying to smooth each pixel through both 'space' (mxn) and 'time'(third dimension). I am using the function movmean to create an average of each pixel in time of a 1000x1000x8 matrix.
I am currently using the following code to take an average, using a window size of 5, operating along the third dimension:
av_matrix = movmean(my_matrix,5,3)
This is creating an average as expected, but I'm wondering if the window is just operating in the mxn direction and not taking the average along the third dimension as well.
To compute a moving average along the n dimensions of an n-dimensional array (the "window" is an n-dimensional rectangle), the simplest way is to use convolution (see convn).
You need to be careful with edge effects, that is, when the convolution kernel (or n-dimensional window) partially slides out of the data. What movmean does is average over the actual data points only. To achieve that behaviour you can
compute the sum over the kernel via convolution with the 'same' option; and then
divide each entry by the number of actual data points from which it was computed. This number can also be obtaind via convolution, namely, applying the kernel to an array of ones.
So, all you need is:
my_matrix = randi(9,5,5,3); % example 3D array
sz = [3 3 2]; % 3D window size
av_matrix = convn(my_matrix, ones(sz), 'same') ... % step 1
./convn(ones(size(my_matrix)), ones(sz), 'same'); % step 2
Check:
The following examples use
>> my_matrix
my_matrix(:,:,1) =
6 8 2 1 8
4 6 7 9 8
4 5 1 4 3
5 5 8 7 9
3 6 6 4 9
my_matrix(:,:,2) =
8 8 5 3 6
8 9 6 9 1
9 5 6 2 2
1 7 4 1 2
5 4 7 4 9
my_matrix(:,:,3) =
6 5 8 6 6
1 6 8 6 1
5 5 1 6 7
1 1 2 9 8
1 2 6 1 2
With edge effects:
>> mean(mean(mean(my_matrix(1:2,1:2,1:2))))
ans =
7.125000000000000
>> av_matrix(1,1,1)
ans =
7.125000000000000
Without edge effects:
>> mean(mean(mean(my_matrix(1:3,1:3,1:2))))
ans =
5.944444444444445
>> av_matrix(2,2,1)
ans =
5.944444444444445

Matrix transformation in MATLAB

For example, I have a matrix A (Figure 1). When the variable n = 2, I want it to be transformed to the matrix B. The red rectangle shows the transformation rule of every column. According to this rule, when the n = 3, it can become the matrix C.
I have written a script using a for loop method, but it is a waste of time when the matrix A is very large (e.g. 11688* 140000). Is there an efficient way to solve this problem?
Figure 1:
Here is a way using reshape and implicit expansion:
result = reshape(A((1:size(A,1)-n+1) + (0:n-1).', :), n, []);
For example assume that n = 3. Implicit expansion is used to extract indices of rows:
row_ind = (1:size(A,1)-n+1) + (0:n-1).';
The following matrix is created:
1 2
2 3
3 4
Extract the desired rows of A:
A_expanded = A(row_ind, :)
When the matrix row_ind is used as an index it behaves like a vector:
1
2
1 2 3
2 3 -> 2
3 4 3
4
A_expanded =
3 5 7
6 8 9
2 6 3
6 8 9
2 6 3
1 2 1
Now A_expanded can be reshaped to the desired size:
result = reshape(A_expanded, n, []);
>>result =
3 6 5 8 7 9
6 2 8 6 9 3
2 1 6 2 3 1
If you have the Image Processing Toolbox you can use im2col as follows:
result = im2col(A, [n 1], 'sliding');

MATLAB: how to pass in the diagonal of a matrix as an argument in another matrix?

Let c be 2D array, and x and y be 1D arrays of the same length (for instance, let's have x=1:7 and y=3:9).
I need to find a way to pass in arguments from x and y in the way I will describe below.
If I put simply c(x,y) it will give a 7 by 7 matrix. I don't want that.
Instead, I want to pass in the diagonal of the [x y] matrix: ((x(1), y(1)), (x(2), y(2))...(x(7), y(7)). Is there a way to do this without a for loop or any iterative statement?
You are looking for sub2ind function
res = c( sub2ind(size(c), x, y ) )
There's an easier way. If you're looking for a diagonal, use diag. If you have a matrix c:
c =
5 8 4 2 9 1 6 1 1
9 8 7 5 9 3 2 7 5
2 3 9 10 2 1 4 2 2
3 2 9 2 4 4 7 2 4
3 9 10 8 7 5 2 1 8
5 6 3 7 6 1 10 5 2
6 1 7 3 10 8 2 4 2
you can find the main diagonal by using diag with no extra arguments:
>> diag(c)
ans =
5
8
9
2
7
1
2
The second argument, though, indicates which diagonal you want as an offset from the main diagonal. So the default diagonal is equal to 0. If you want the diagonal starting at c(1,3), that's 2 above the main diagonal, so
>> diag(c,2)
ans =
4
5
2
4
2
5
2
Similarly, if you want the diagonal starting at c(4,1), the offset is -3:
>> diag(c,-3)
ans =
3
9
3
3

Matlab: Reshaping grid points from ndgrid into N x m matrix [duplicate]

This question pops up quite often in one form or another (see for example here or here). So I thought I'd present it in a general form, and provide an answer which might serve for future reference.
Given an arbitrary number n of vectors of possibly different sizes, generate an n-column matrix whose rows describe all combinations of elements taken from those vectors (Cartesian product) .
For example,
vectors = { [1 2], [3 6 9], [10 20] }
should give
combs = [ 1 3 10
1 3 20
1 6 10
1 6 20
1 9 10
1 9 20
2 3 10
2 3 20
2 6 10
2 6 20
2 9 10
2 9 20 ]
The ndgrid function almost gives the answer, but has one caveat: n output variables must be explicitly defined to call it. Since n is arbitrary, the best way is to use a comma-separated list (generated from a cell array with ncells) to serve as output. The resulting n matrices are then concatenated into the desired n-column matrix:
vectors = { [1 2], [3 6 9], [10 20] }; %// input data: cell array of vectors
n = numel(vectors); %// number of vectors
combs = cell(1,n); %// pre-define to generate comma-separated list
[combs{end:-1:1}] = ndgrid(vectors{end:-1:1}); %// the reverse order in these two
%// comma-separated lists is needed to produce the rows of the result matrix in
%// lexicographical order
combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1
combs = reshape(combs,[],n); %// reshape to obtain desired matrix
A little bit simpler ... if you have the Neural Network toolbox you can simply use combvec:
vectors = {[1 2], [3 6 9], [10 20]};
combs = combvec(vectors{:}).' % Use cells as arguments
which returns a matrix in a slightly different order:
combs =
1 3 10
2 3 10
1 6 10
2 6 10
1 9 10
2 9 10
1 3 20
2 3 20
1 6 20
2 6 20
1 9 20
2 9 20
If you want the matrix that is in the question, you can use sortrows:
combs = sortrows(combvec(vectors{:}).')
% Or equivalently as per #LuisMendo in the comments:
% combs = fliplr(combvec(vectors{end:-1:1}).')
which gives
combs =
1 3 10
1 3 20
1 6 10
1 6 20
1 9 10
1 9 20
2 3 10
2 3 20
2 6 10
2 6 20
2 9 10
2 9 20
If you look at the internals of combvec (type edit combvec in the command window), you'll see that it uses different code than #LuisMendo's answer. I can't say which is more efficient overall.
If you happen to have a matrix whose rows are akin to the earlier cell array you can use:
vectors = [1 2;3 6;10 20];
vectors = num2cell(vectors,2);
combs = sortrows(combvec(vectors{:}).')
I've done some benchmarking on the two proposed solutions. The benchmarking code is based on the timeit function, and is included at the end of this post.
I consider two cases: three vectors of size n, and three vectors of sizes n/10, n and n*10 respectively (both cases give the same number of combinations). n is varied up to a maximum of 240 (I choose this value to avoid the use of virtual memory in my laptop computer).
The results are given in the following figure. The ndgrid-based solution is seen to consistently take less time than combvec. It's also interesting to note that the time taken by combvec varies a little less regularly in the different-size case.
Benchmarking code
Function for ndgrid-based solution:
function combs = f1(vectors)
n = numel(vectors); %// number of vectors
combs = cell(1,n); %// pre-define to generate comma-separated list
[combs{end:-1:1}] = ndgrid(vectors{end:-1:1}); %// the reverse order in these two
%// comma-separated lists is needed to produce the rows of the result matrix in
%// lexicographical order
combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1
combs = reshape(combs,[],n);
Function for combvec solution:
function combs = f2(vectors)
combs = combvec(vectors{:}).';
Script to measure time by calling timeit on these functions:
nn = 20:20:240;
t1 = [];
t2 = [];
for n = nn;
%//vectors = {1:n, 1:n, 1:n};
vectors = {1:n/10, 1:n, 1:n*10};
t = timeit(#() f1(vectors));
t1 = [t1; t];
t = timeit(#() f2(vectors));
t2 = [t2; t];
end
Here's a do-it-yourself method that made me giggle with delight, using nchoosek, although it's not better than #Luis Mendo's accepted solution.
For the example given, after 1,000 runs this solution took my machine on average 0.00065935 s, versus the accepted solution 0.00012877 s. For larger vectors, following #Luis Mendo's benchmarking post, this solution is consistently slower than the accepted answer. Nevertheless, I decided to post it in hopes that maybe you'll find something useful about it:
Code:
tic;
v = {[1 2], [3 6 9], [10 20]};
L = [0 cumsum(cellfun(#length,v))];
V = cell2mat(v);
J = nchoosek(1:L(end),length(v));
J(any(J>repmat(L(2:end),[size(J,1) 1]),2) | ...
any(J<=repmat(L(1:end-1),[size(J,1) 1]),2),:) = [];
V(J)
toc
gives
ans =
1 3 10
1 3 20
1 6 10
1 6 20
1 9 10
1 9 20
2 3 10
2 3 20
2 6 10
2 6 20
2 9 10
2 9 20
Elapsed time is 0.018434 seconds.
Explanation:
L gets the lengths of each vector using cellfun. Although cellfun is basically a loop, it's efficient here considering your number of vectors will have to be relatively low for this problem to even be practical.
V concatenates all the vectors for easy access later (this assumes you entered all your vectors as rows. v' would work for column vectors.)
nchoosek gets all the ways to pick n=length(v) elements from the total number of elements L(end). There will be more combinations here than what we need.
J =
1 2 3
1 2 4
1 2 5
1 2 6
1 2 7
1 3 4
1 3 5
1 3 6
1 3 7
1 4 5
1 4 6
1 4 7
1 5 6
1 5 7
1 6 7
2 3 4
2 3 5
2 3 6
2 3 7
2 4 5
2 4 6
2 4 7
2 5 6
2 5 7
2 6 7
3 4 5
3 4 6
3 4 7
3 5 6
3 5 7
3 6 7
4 5 6
4 5 7
4 6 7
5 6 7
Since there are only two elements in v(1), we need to throw out any rows where J(:,1)>2. Similarly, where J(:,2)<3, J(:,2)>5, etc... Using L and repmat we can determine whether each element of J is in its appropriate range, and then use any to discard rows that have any bad element.
Finally, these aren't the actual values from v, just indices. V(J) will return the desired matrix.

Vectorized Reshaping of Columns in an Array

I have an array A, and want to reshape the last four elements of each column into a 2x2 matrix. I would like the results to be stored in a cell array B.
For example, given:
A = [1:6; 3:8; 5:10]';
I would like B to contain three 2x2 arrays, such that:
B{1} = [3, 5; 4, 6];
B{2} = [5, 7; 6, 8];
B{3} = [7, 9; 8, 10];
I can obviously do this in a for loop using something like reshape(A(end-3:end, ii), 2, 2) and looping over ii. Can anyone propose a vectorized method, perhaps using something similar to cellfun that can apply an operation repeatedly to columns of an array?
The way I do this is to look at the desired indices and then figure out a way to generate them, usually using some form of repmat. For example, if you want the last 4 items in each column, the (absolute) indices into A are going to be 3,4,5,6, then add the number of rows to that to move to the next column to get 9,10,11,12 and so on. So the problem becomes generating that matrix in terms of your number of rows, number of columns, and the number of elements you want from each column (I'll call it n, in your case n=4).
octave:1> A = [1:6; 3:8; 5:10]'
A =
1 3 5
2 4 6
3 5 7
4 6 8
5 7 9
6 8 10
octave:2> dim=size(A)
dim =
6 3
octave:3> n=4
n = 4
octave:4> x=repmat((dim(1)-n+1):dim(1),[dim(2),1])'
x =
3 3 3
4 4 4
5 5 5
6 6 6
octave:5> y=repmat((0:(dim(2)-1)),[n,1])
y =
0 1 2
0 1 2
0 1 2
0 1 2
octave:6> ii=x+dim(1)*y
ii =
3 9 15
4 10 16
5 11 17
6 12 18
octave:7> A(ii)
ans =
3 5 7
4 6 8
5 7 9
6 8 10
octave:8> B=reshape(A(ii),sqrt(n),sqrt(n),dim(2))
B =
ans(:,:,1) =
3 5
4 6
ans(:,:,2) =
5 7
6 8
ans(:,:,3) =
7 9
8 10
Depending on how you generate x and y, you can even do away with the multiplication, but I'll leave that to you. :D
IMO you don't need a cell array to store them either, a 3D matrix works just as well and you index into it the same way (but don't forget to squeeze it before you use it).
I gave a similar answer in this question.

Resources