Thanks in advance for the help.
Suppose I have a multi D array x such that
x(:,:,1) = [1 2; 3 4];
x(:,:,2) = [5 6; 7 8];
and a matrix y such that
y = [1 2; 5 6];
I would like to add the first row of y to each row of x(:,:,1) and the second row of y to each element of x(:,:,2). This will produce an array z such that
z(:,:,1) = [2 4; 4 6];
z(:,:,2) = [10 12; 12 14];
In reality (not the example I am giving) I would like to do this operation on a very large multi D array x and a very large matrix y. I therefore want to do this as efficiently as possible.
The naive approach would be to use for loops to do this, but this would not be efficient what-so-ever. I believe that an efficient approach would be to incorporate bsxfun to accomplish this, but I haven't been able to figure out an approach. y and x can and be restructured to accomplish this task as long as the same z is produced and, most importantly, the amount of time needed to build z is less than the for loop approach.
I was able to find this which does what I want, but only for multiplication and not summation. I could modify this code to do what I want but I feel as though with summation that there has to be a simpler approach.
You just need bsxfun and a little bit of permute:
z = bsxfun(#plus, x, permute(y, [3 2 1]));
The key is to properly rearrange the dimensions of y so that the singleton expansion performed by bsxfun gives you the desired result.
Related
I'm struggling to vectorise a function which performs a somewhat pairwise difference between two vectors x = 2xN and v = 2xM, for some arbitrary N, M. I have this to work when N = 1, although, I would like to vectorise this function to apply to inputs with N arbitrary.
Indeed, what I want this function to do is for each column of x find the normed difference between x(:,column) (a 2x1) and v (a 2xM).
A similar post is this, although I haven't been able to generalise it.
Current implementation
function mat = vecDiff(x,v)
diffVec = bsxfun(#minus, x, v);
mat = diffVec ./ vecnorm(diffVec);
Example
x =
1
1
v =
1 3 5
2 4 6
----
vecDiff(x,v) =
0 -0.5547 -0.6247
-1.0000 -0.8321 -0.7809
Your approach can be adapted as follows to suit your needs:
Permute the dimensions of either x or v so that its number of columns becomes the third dimension. I'm choosing v in the code below.
This lets you exploit implicit expansion (or equivalently bsxfun) to compute a 2×M×N array of differences, where M and N are the numbers of columns of x and v.
Compute the vector-wise (2-)norm along the first dimension and use implicit expansion again to normalize this array:
x = [1 4 2 -1; 1 5 3 -2];
v = [1 3 5; 2 4 6];
diffVec = x - permute(v, [1 3 2]);
diffVec = diffVec./vecnorm(diffVec, 2, 1);
You may need to apply permute differently if you want the dimensions of the output in another order.
Suppose your two input matrices are A (a 2 x N matrix) and B (a 2 x M matrix), where each column represents a different observation (note that this is not the traditional way to represent data).
Note that the output will be of the size N x M x 2.
out = zeros(N, M, 2);
We can find the distance between them using the builtin function pdist2.
dists = pdist2(A.', B.'); (with the transpositions required for the orientation of the matrices)
To get the individual x and y distances, the easiest way I can think of is using repmat:
xdists = repmat(A(1,:).', 1, M) - repmat(B(1,:), N, 1);
ydists = repmat(A(2,:).', 1, M) - repmat(B(2,:), N, 1);
And we can then normalise this by the distances found earlier:
out(:,:,1) = xdists./dists;
out(:,:,2) = ydists./dists;
This returns a matrix out where the elements at position (i, j, :) are the components of the normed distance between A(:,i) and B(:,j).
I have a data set that consists of two 1800 x 900 x 3 x 3 arrays, which should each be interpreted as a 1800x900 array of 3x3 matrices. As part of the analysis, at one point I need to create another such array by, at each point of the 1800x900 array, multiplying the corresponding 3x3 matrices together.
There seem to be two ways to do this that I can think of. The obvious way is
C = zeros(size(A))
for i = 1:900
for j = 1:1800
C(i,j,:,:) = A(i,j,:,:)*B(i,j,:,:)
end
end
But that's quite a long loop and doesn't really take advantage of MATLAB's vectorization. The other way is
C = zeros(size(A))
for i = 1:3
for j = 1:3
for k = 1:3
C(:,:,i,j) = C(:,:,i,j) + A(:,:,i,k).*B(:,:,k,j)
end
end
end
where the large dimensions are getting vectorized and I'm basically using the for loops to implement the Einstein summation convention. This seems really inelegant, though, which makes me think there should be a better way. Is there?
Perfect job for bsxfun with permute:
C = permute(sum(bsxfun(#times, A, permute(B, [1 2 5 3 4])), 4), [1 2 3 5 4]);
In R2016b onwards you can avoid bsxfun thanks to implicit singleton expansion:
C = permute(sum(A .* permute(B, [1 2 5 3 4]), 4), [1 2 3 5 4]);
I have two arrays of the same size, ux and uy, which I want to combine into a cell array of vectors such that U(1,1) contains a vector composed of uy(1,1),ux(1,1) and `numel(U)=numel(ux)=numel(uy)'
The components ux and uy represent the unit vector of the image gradient. The component arrays are created by elementwise multiplication:
ux = I1x./I1mag;
uy = I1y./I1mag;
I need to be able to access each vector multiple times, and call them as arguments of dot and cross and making an array of vectors would be faster and more convenient than creating an ad hoc vector for each one on every iteration where it is called.
Thanks
Edit for further clarity:
suppose I have an array
uy = (1,2,3;4,5,6);
and another array of the same size
ux = (9,8,7;6,5,4);
I need the yx vectors, so for our example that's
([1,9], [2,8], [3,7]; [4,6], [5,5], [6,4])
What's the most efficient way to do that, please? I'm going to get dot products of each pixel with its neighbours and vice versa, so each vector will be used 16 times and the full arrays contain on the order of 10^4 or 10^5 elements...
Thanks for your continued help.
You can create two layers. One layer contains ux and other layer contains uy.
ux = [10 8 7;6 5 4];
uy = [1 2 3;4 5 6];
xy(:,:,1) = ux; // [10 8 7;6 5 4]
xy(:,:,2) = uy; // [1 2 3;4 5 6]
aaa=xy(1,1,:); // [10 1]
bbb=xy(1,2,:); // [8 2]
dot(aaa,bbb)
Result will be:
82
If you really want it to be a cell vector, where each element is a [1 x 2] vector, use mat2cell:
ux = rand(15,1);
uy = rand(15,1);
U = [ux, uy];
K = mat2cell(U,ones(size(U,1),1),2);
But as others have pointed out, U = [ux, uy] is sufficient, as you can just call U(1,:) to get the exact same result without having to worry about cells.
If you are wanting to alculate the dot product on the two vectors, arrayfun(#dot,ux,uy) does that job element-wise.
Suppose I have a matrix A [m x 1], where m is not necessarily even. I to create a matrix B also [m x 1] which tells me the decile of the elements in A (i.e. matrix B has numbers from 1 to 10).
I know I can use the function sort(A) to get the position of the elements in A and from there I can manually get deciles. Is there another way of doing it?
I think one possibility would be B = ceil(10 * tiedrank(A) / length(A) . What do you think? Are there any issues with this?
Also, more generally, if I have a matrix A [m x n] and I want to create a matrix B also [m x n], in which each column of B should have the decile of the corresponding column in A , is there a way of doing it without a for loop through the columns?
Hope the problem at hand is clear. So far I have been doing it using the sort function and then manually assigning the deciles, but it is very inefficient.
This is how I would do it:
N = 10;
B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A));
This counts, for each element, how many elements are less than or equal to it; and then rounds the results to 10 values.
Depending on how you define deciles, you may want to change #le to #lt, or ceil to floor. For numel(A) multiple of N, the above definition gives exactly numel(A)/N values in each of the N quantiles. For example,
>> A = rand(1,8)
A =
0.4387 0.3816 0.7655 0.7952 0.1869 0.4898 0.4456 0.6463
>> N = 4;
>> B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A))
B =
2 1 4 4 1 3 2 3
I have 2 matrices A (nxm) and B (nxd) and want to multiply element-wise each column of A with a row of B. There are m columns in A and n 1xd vectors in B so the results are m nxd matrices. Then I want to sum(result_i, 1) to get m 1xd vectors, which I want to apply vertcat to get a mxd matrix. I'm doing this operations using for loop and it is slow because n and d are big. How can I vectorize this in matlab to make it faster? Thank you.
EDIT:
You're all right: I was confused by my own question. What I meant by "multiply element-wise each column of A with a row of B" is to multiply n elements of a column in A with the corresponding n rows of B. What I want to do with one column of A is as followed (and I repeat this for m columns of A, then vertcat the C's vector together to get an mxd matrix):
column_of_A =
3
3
1
B =
3 1 3 3
2 2 1 2
1 3 3 3
C = sum(diag(column_of_A)*B, 1)
16 12 15 18
You can vectorize your operation the following way. Note, however, that vectorizing comes at the cost of higher memory usage, so the solution may end up not working for you.
%# multiply nxm A with nx1xd B to create a nxmxd array
tmp = bsxfun(#times,A,permute(B,[1 3 2]));
%# sum and turn into mxd
out = squeeze(sum(tmp,1));
You may want to do everything in one line, which may help the Matlab JIT compiler to save on memory.
EDIT
Here's a way to replace the first line if you don't have bsxfun
[n,m] = size(A);
[n,d] = size(B);
tmp = repmat(A,[1 1 d]) .* repmat(permute(B,[1 3 2]),[1,m,1]);
It's ugly, but as far as I can see, it works. I'm not sure it will be faster than your loop though, plus, it has a large memory overhead. Anyway, here goes:
A_3D = repmat(reshape(A, size(A, 1), 1, size(A, 2)), 1, size(B, 2));
B_3D = repmat(B, [ 1 1 size(A, 2)]);
result_3D = sum(A_3D .* B_3D, 1);
result = reshape(result_3D, size(A, 2), size(B, 2))
What it does is: make A into a 3D matrix of size n x 1 x m, so one column in each index of the 3rd dimension. Then we repeat the matrix so we get an n x d x m matrix. We repeat B in the 3rd dimension as well. We then do a piecewise multiplication of all the elements and sum them. The resulting matrix is a 1 x d x m matrix. We reshape this into a m x d matrix.
I'm pretty sure I switched around the size of the dimensions a few times in my explanation, but I hope you get the general gist.
Multiplying with a diagonal matrix seems at least twice as fast, but I couldn't find a way to use diag, since it wants a vector or 2D matrix as input. I might try again later tonight, I feel there must be a faster way :).
[Edit] Split up the command in parts to at least make it a little bit readable.
This is the way I would do this:
sum(repmat(A,1,4).*B)
If you don't know the number of columns of B:
sum(repmat(A,1,size(B,2)).*B)