Putting a smaller 3D matrix into a bigger 3D matrix (3D sub2ind) - matlab

I need to put a smaller 3D matrix into a bigger 3D matrix. Explaining with an example:
Suppose I have the following 3D matrices:
%A is the big matrix
A(:,:,1)=[ 0.3545 0.8865 0.2177
0.9713 0.4547 0.1257
0.3464 0.4134 0.3089];
A(:,:,2)=[ 0.7261 0.0098 0.7710
0.7829 0.8432 0.0427
0.6938 0.9223 0.3782];
A(:,:,3) = [0.7043 0.2691 0.6237
0.7295 0.6730 0.2364
0.2243 0.4775 0.1771];
%B is the small matrix
B(:,:,1) = [0.3909 0.5013
0.0546 0.4317];
B(:,:,2) =[0.4857 0.1375
0.8944 0.3900];
B(:,:,3) =[0.7136 0.3433
0.6183 0.9360];
Now to put B in A such that: use first dimension: [1 3], second dimension [2 3] and do this for [1,2,3] pages of A. For the given matrix, putting these values will result in:
NewA(:,:,1) = [ 0.3545 0.3909 0.5013 % putting the value of %B(1,:,1)
0.9713 0.4547 0.1257
0.3464 0.0546 0.4317; % putting the value of %B(2,:,1)
NewA(:,:,2)=[ 0.7261 0.4857 0.1375 % putting the value of %B(1,:,2)
0.7829 0.8432 0.0427
0.6938 0.8944 0.3900]; % putting the value of %B(2,:,2)
NewA(:,:,3) = [0.7043 0.7136 0.3433 % putting the value of %B(1,:,3)
0.7295 0.6730 0.2364
0.2243 0.6183 0.9360]; % putting the value of %B(2,:,3)
I won't necessarily have square matrices as 3D pages and the size of A to put B in can vary as well. But the matrices will always be 3D. Above is just a small example. What I actually have is dimensions as big as A -> [500,500,5] and B as -> [350,350,4].
This is what sub2ind do for 2D matrices but I am not yet able to manipulate into use for 3D matrices.
Something like:
NewA = A;
NewA(sub2ind(size(A), [1 3], [2 3], [1 2 3])) = B;
but it gives:
Error using sub2ind (line 69)
The subscript vectors must all be of the same size.
How can I do this?

You don't need sub2ind, just assign directly:
newA(1,2:3,:)=B(1,:,:)
If you want to use sub2ind, you need to specify each of the 3 dimensions, for each of the elements you want to replace:
dim1A=[1 1 1 1 1 1]; % always first row
dim2A=[2 3 2 3 2 3]; % second and third column, for each slice
dim3A=[1 1 2 2 3 3]; % two elements from each slice
newA(sub2ind(size(A),dim1A,dim2A,dim3A))=B(1,:,:)
newA(:,:,1) =
0.3545 0.3909 0.5013
0.9713 0.4547 0.1257
0.3464 0.4134 0.3089
newA(:,:,2) =
0.7261 0.4857 0.1375
0.7829 0.8432 0.0427
0.6938 0.9223 0.3782
newA(:,:,3) =
0.7043 0.7136 0.3433
0.7295 0.6730 0.2364
0.2243 0.4775 0.1771

Related

Using vector as indices not working as expected, matlab

Given a nXm matrix A and a mX2 matrix B and a matrix C of size mX1 containing 1s and 2s C=[1 2 1 2 1...], depending on which column, I want every row of A to be multiplied with. How can this be done? Or equivalently, given D = A*B how can I access only the values dictated by C. I tried D(:,C), but the result is not the expected.
Example a =[1 2; 3 4; 5 6] . c = [1 2 1] . a(?) = [1 4 5]
Any idea?
%example data
n=10;m=20;
A=rand(n,m)
B=rand(m,2)
C=round(rand(m,1))+1;
%solution:
B2=B(:,1); %multiplication vector
B2(C==2)=B(C==2,2) %change the ones where C==2
A*B2
You can run the following command for the last example:
a(sub2ind([3,2],1:3,c))'
In general case you can do like the following:
% n is the length of the D which is nx2 matrix
D(sub2ind([n,2],1:n,C))'

Append vector to 3d matrix in Matlab

I wish to append a row-vector (and later, also a column vector) to an existing x by y by z matrix. So basically "Add a new row (at the "bottom") for each z in the original 3d matrix. Consider the following short Matlab program
appendVector = [1 2 3 4 5]; % Small matrix for brevity. Actual matrices used are much larger.
origMatrix = ones(5,5,3);
appendMatrix = [origMatrix( ... ); appendVector];
My question is: How do I adress (using Matlab-style matrix adressing, not a "manual" C-like loop) origMatrix( ... ) in order to append the vector above? Feel free to also include a suggestion on how to do the same operation for a column-vector (I am thinking that the correct way to do the latter is to simply use the '-operator in Matlab).
A "row" in a 3D matrix is actually a multi-dimensional array.
size(origMatrix(1,:,:))
% 5 3
So to append a row, you would need to append a 5 x 3 array.
toAppend = rand(5, 3);
appendMatrix = cat(1, origMatrix, toAppend);
You could append just a 5 element vector and specify an index for the third dimension. In this case, the value for the "row" for all other indices in the third dimension would be filled with zeros.
appendVector = [1 2 3 4 5];
origMatrix = ones(5,5,3);
appendMatrix = origMatrix;
appendMatrix(end+1, :, 1) = appendVector;
If instead, you want to append the same vector along the third dimension, you could use repmat to turn your vector into a 1 x 5 x 3 array and then append that.
appendVector = repmat([1 2 3 4 5], 1, 1, size(origMatrix, 3));
appendMatrix = cat(1, origMatrix, appendVector);

for loop in matlab storing values (scalar times vector array)

I would like to know in matlab how to accomplish the following task:
If i have a for loop like in the following lines:
b=[1 2 3 4];
for i=1:1:10
x=i.*b
end
the code iterates i-times multiplying a scalar times the vector b; but if i put in the for loop x(i) in order to store the resulting vector of every iteration, i wouldn't get what i'm looking for. What i'm lookig for is to get:
x(1)=[1 2 3 4]
x(2)=[2 4 6 8]
... and so on
As in P0W's answer, you need a two-dimensional matrix to store "a vector of vectors". You can't use x(n), since addresses a single value in a one-dimensional matrix.
Another solution, maybe closer to what you want, is to use cell a cell array, which allow to create a matrix containing mixed-typed values (so you can put vectors too!). They are quite similar to regular array, but you need curly brackets:
b=[1 2 3 4];
x = cell(1,10); % preallocating, not necessary but always a good idea
for i = 1:10
x{i} = i*b % notice the curly bracket with the index
end
note: you don't need the .* operator, since is a scalar-matrix multiplication.
You can get back your values with
x{1} = [1 2 3 4] % again curly brackets
x{2} = [2 4 6 8]
...
Can use:
x=[1:10]'*b
then
x(1,:)
x(2,:)
etc

Creating matrix from two vectors of (duplicated) indices in MATLAB

Suppose now I have two vectors of same length:
A = [1 2 2 1];
B = [2 1 2 2];
I would like to create a matrix C whose dim=m*n, m=max(A), n=max(B).
C = zeros(m,n);
for i = 1:length(A)
u = A(i);
v = B(i);
C(u,v)=C(u,v)+1;
end
and get
C =[0 2;
1 1]
More precisely, we treat the according indices in A and B as rows and columns in C, and C(u,v) is the number of elements in {k | A(i)=u and B(i)=v, i = 1,2,...,length(A)}
Is there a faster way to do that?
Yes. Use sparse. It assembles (i.e., sums up) the matrix values for repeating row-column pairs for you. You need an additional vector with the values that will be assembled into the matrix entries. If you use ones(size(A)), you will have exactly what you need - counting of repeated row-column pairs
spA=sparse(A, B, ones(size(A)));
full(spA)
ans =
0 2
1 1
The same can be obtained by simply passing scalar 1 to sparse function instead of a vector of values.
For matrices that have a large number of zero entries this is absolutely crucial that you use sparse storage. Another function you could use is accumarray. It can essentially do the same thing, but also works on dense matrix structure:
AA=accumarray([A;B]', 1);
AA =
0 2
1 1
You can pass size argument to accumarray if you want to create a matrix of specific size
AA=accumarray([A;B]', 1, [2 3]);
AA =
0 2 0
1 1 0
Note that you can actually also make it produce sparse matrices, and use a different operator in assembly (i.e., not necessarily a sum)
AA=accumarray([A;B]', 1, [2 3], #sum, 0, true)
will produce a sparse matrix (last parameter set to true) using sum for assembly and 0 as a fill value, i.e. a value which is used in cases a given row-column pair does not exist in A/B.

Matlab for loop replacement

I am trying to reduce the amount of loops in my code to speed up computation. I have encountered a portion of code I am completing with a loop which I cannot see a solution.
I have a matrix of x y coords of various particles.
For example, generated as rand(2,5)
0.8715 0.0363 0.0657 0.6289 0.3279
0.0272 0.4380 0.9794 0.6563 0.4755
I would like a matrix in (5,5,2) with a vector between each particle.
This would be a matrix of x lengths as (:,:,1) and y lengths as (:,:,2).
You can use bsxfun for this, though you'll also need permute to "3D-transpose" the coordinate matrix. permute turns coordinates into a 5-by-1-by-2, and a 1-by-5-by-2 array, respectively:
coordinates = rand(2,5);
%# subtract all coordinate pairs from one another
vectorArray = bsxfun(#minus,permute(coordinates,[2,3,1]),permute(coordinates,[3 2 1]));
size(vectorArray)
ans =
5 5 2
Note that the vectorArray is antisymmetric, so you may want to look into pdist if you run into space problems.
Here's an indexing-based solution that uses REPMAT to create the linear index into the coordinate matrix and CAT to create the 3-D matrix result:
>> xy = [0.8715 0.0363 0.0657 0.6289 0.3279; ... %# Your coordinates
0.0272 0.4380 0.9794 0.6563 0.4755];
>> N = size(xy, 2);
>> index = repmat(1:2:2*N, N, 1); %# A matrix of linear indices into xy
>> result = cat(3, xy(index)-xy(index.'), ... %.'# X differences
xy(index+1)-xy(index.'+1)) %.'# Y differences
result(:,:,1) =
0 -0.8352 -0.8058 -0.2426 -0.5436
0.8352 0 0.0294 0.5926 0.2916
0.8058 -0.0294 0 0.5632 0.2622
0.2426 -0.5926 -0.5632 0 -0.3010
0.5436 -0.2916 -0.2622 0.3010 0
result(:,:,2) =
0 0.4108 0.9522 0.6291 0.4483
-0.4108 0 0.5414 0.2183 0.0375
-0.9522 -0.5414 0 -0.3231 -0.5039
-0.6291 -0.2183 0.3231 0 -0.1808
-0.4483 -0.0375 0.5039 0.1808 0