Check element wise equality of a 3D matrix Matlab - matlab

I have a 3D matrix say for eg. A(10x5x8). I need to get a 2D matrix (Boolean) out of it of size 10x5.
True if its elemental 3 Dimensional values are all same. i.e. Result(1,1) = 1 if A(1,1,1) == A(1,1,2) == A(1,1,3) etc..
False if at least one is different.
I expect a vectored approach which is fast and efficient.
Sample input:
A(:,:,1) = 1 2
2 2
A(:,:,2) = 1 1
2 3
Expected Output:
Result = 1 0
1 0

Use bsxfun with the eq function and use the first slice as the first input and compare with the other slices for the second input. Allow the first input to broadcast itself over the multiple slices.
Once you do that, use all and check the third dimension:
ind1 = bsxfun(#eq, A(:,:,1), A(:,:,2:end);
ind2 = all(ind1, 3);
The logic behind the above is very simple. How the first line of code works is that you would create a temporary matrix that would take the first slice of A and let it duplicate itself for as many slices as you have in A, without the first slice. Once you do this, you would do an element-by-element equality with this temporary matrix and the other slices. If you had a 3D column that was all equal, the one element from the first slice would be compared with every single value that corresponds to the same 3D column. Should they all equal to each other, then you would get a 3D column of all logical 1s. Therefore, to have a 3D column that is all equal to each other, all of the values should be 1, which is why all is used - to check if all values in a 3D column are equal to 1. Should all of the 3D column be a logical 1, we have matched your criteria.
Example run
>> A1 = [1 2; 2 2];
>> A2 = [1 1; 2 3];
>> A3 = [1 3; 2 4];
>> A4 = [1 5; 2 6];
>> A = cat(3, A1, A2, A3, A4);
>> ind1 = bsxfun(#eq, A(:,:,1), A(:,:,2:end);
>> ind2 = all(ind1, 3)
ind2 =
1 0
1 0
I made a matrix of 4 slices where the 3D column at the top left corner and the bottom left corner have all of the same values. Once you run through the code at the beginning of the post, we get what you expect.

Here's with short and sweet diff and must be quite memory efficient -
out = ~any(diff(A,[],3),3)
So, basically with diff along the third dimension diff(..[],3), you would calculate differences between the same (i,j) but on different 3D slices. Thus, if all such diff outputs are zeros, that would indicate that all dim3 elements for the same (i,j) are the same. This all zeros information is then picked up by ~any() also along dim3 with ~any(.,3) for the desired 2D array output.

Related

Allocating elements from a smaller matrix to a bigger one by user input

I have 3 smaller matrices (in multidimensional arrays), all of them 4x4. I want to build a bigger matrix (8x8) for each smaller one, but rearranging the position of each element of the smaller matrix into a new position in the bigger one, according to user matrix input:
For example, I have those 3 little matrices:
A=repmat(1, [4 4]);
B=repmat(2, [4 4]);
C=repmat(3, [4 4]);
and the bigger one K=zeros(8);. The user input matrix is this one:
user=[1 2 7 8; 3 4 1 2; 3 4 7 8];
So the first bigger matrix must have a configuration where:
The red numbers are the new row and columns in the bigger matrix, resulting in this for K:
How can I insert values from the smaller matrices to the bigger matrix in this way?
It is not clear why you need this, but this is the solution I thought.
K = zeros(8,8);
for ii=1:4
for jj=1:4
K(user(1,ii),user(1,jj)) = A(ii,jj);
K(user(2,ii),user(2,jj)) = B(ii,jj);
K(user(3,ii),user(3,jj)) = C(ii,jj);
end
end
You can easily do this by using a row of user as the row and column indices into K, like so:
K(user(1, :), user(1, :)) = A;
If you want to iterate over each smaller matrix and user input, inserting them all into the same larger matrix K, I'd first put the smaller matrices into a single cell array, then simply repeat the above in a for loop:
smallMats = {A, B, C};
K = zeros(8);
for row = 1:size(user, 1)
K(user(row, :), user(row, :)) = smallMats{row};
end
This can easily be extended to more matrices by adding to user and smallMats.

How to use logical conditions only for some dimensions in multidimensional arrays in MATLAB

Let us have a 4D matrix (tensor), output:
[X,Y,Z] = ndgrid(-50:55,-55:60,-50:60);
a = 1:4;
output = zeros([size(X),length(a)]);
Next, we determine the area inside the ellipsoid:
position = [0,0,0];
radius = [10,20,10];
test_func = #(X,Y,Z) ((X-position(1))/radius(1)).^2 ...
+ ((Y-position(2))/radius(2)).^2 ...
+ ((Z-position(3))/radius(3)).^2 <= 1;
condition = test_func(X,Y,Z);
I need to fill the matrix output inside the ellipsoid for the first 3 dimensions. But for the fourth dimension I need to fill a. I need to do something like this:
output(condition,:) = a;
But it does not work. How to do it? Any ideas please!
If I understand your question correctly, you have a 4D matrix where each temporal blob of pixels in 3D for each 4D slice is filled with a number... from 1 up to 4 where each number tells you which slice you're in.
You can cleverly use bsxfun and permute to help you accomplish this task:
output = bsxfun(#times, double(condition), permute(a, [1 4 3 2]));
This takes a bit of imagination to imagine how this works but it's quite simple. condition is a 3D array of logical values where each location in this 3D space is either 0 if it doesn't or 1 if it does belong to a point inside an ellipsoid. a is a row vector from 1 through 4 or however many elements you want this to end with. Let's call this N to be more general.
permute(a, [1 4 3 2]) shuffles the dimensions such that we create a 4D vector where we have 1 row, 1 column and 1 slice but we have 4 elements going into the fourth dimension.
By using bsxfun in this regard, it performs automatic broadcasting on two inputs where each dimension in the output array will match whichever of the two inputs had the largest value. The condition is that for each dimension independently, they should both match or one of them is a singleton dimension (i.e. 1).
Therefore for your particular example, condition will be 106 x 116 x 111 while the output of the permute operation will be 1 x 1 x 1 x N. condition is also technically 106 x 116 x 111 x 1 and using bsxfun, we would thus get an output array of size 106 x 116 x 111 x N. Performing the element-wise #times operation, the permuted vector a will thus broadcast itself over all dimensions where each 3D slice i will contain the value of i. The condition matrix will then duplicate itself over the fourth dimension so we have N copies of the condition matrix, and performing the element-wise multiply thus completes what you need. This is doable as the logical mask you created contains only 0s and 1s. By multiplying element-wise with this mask, only the values that are 1 will register a change. Specifically, if you multiply the values at these locations by any non-zero value, they will change to these non-zero values. Using this logic, you'd want to make these values a. It is important to note that I had to cast condition to double as bsxfun only allows two inputs of the same class / data type to be used.
To visually see that this is correct, I'll show scatter plots of each blob where the colour of each blob would denote what label in a it belongs to:
close all;
N = 4;
clrs = 'rgbm';
figure;
for ii = 1 : N
blob = output(:,:,:,ii);
subplot(2,2,ii);
plot3(X(blob == ii), Y(blob == ii), Z(blob == ii), [clrs(ii) '.']);
end
We get:
Notice that the spatial extent of each ellipsoid is the same but what is different are the colours assigned to each blob. I've made it such that the values for the first blob, or those assigned to a = 1 are red, those that are assigned to a = 2 are assigned to green, a = 3 to blue and finally a = 4 to magenta.
If you absolutely want to be sure that the coordinates of each blob are equal across all blobs, you can use find in each 3D blob individually and ensure that the non-zero indices are all equal:
inds = arrayfun(#(x) find(output(:,:,:,x)), a, 'un', 0);
all_equal = isequal(inds{:});
The code finds in each blob the column major indices of all non-zero locations in 3D. To know whether or not we got it right, each 3D blob should all contain the same non-zero column major indices. The arrayfun call goes through each 3D blob and returns a cell array where each cell element is the column major indices for a particular blob. We then pipe this into isequal to ensure that all of these column major indices arrays are equal. We get:
>> all_equal
all_equal =
1

Change 1D vector (nx1) to 3D matrix (1x1xn)

Given a= [1;2;3] I want to change this to b where b is
b(1,1,1) = 1
b(1,1,2) = 2
b(1,1,3) = 3.
How can I do this? Is there no built-in command for this?
Use permute to throw the first dimension back at the end as third dimension and bring the third and second dimensions to the front (their orders won't matter). Thus, we would have two such implementations, like so -
permute(a,[3 2 1])
permute(a,[2 3 1])
You can also use reshape to push back the elements to the third dimension, like so -
reshape(a,1,1,numel(a))
Little tutorial on permute
A 3D array A without any permute(rearrangement of dimensions) changes would be : permute(A,[1 2 3]).
Now, any permuting you do, would be w.r.t. the original order of [1 2 3]. Let's say you want to swap 1st and 3rd dimensions, so swap the 1 and 3 in [1 2 3], giving us [3 2 1] and use it as the second argument in permute.
Here's to make your permuting skills stronger - Let's say, you swap first and third dimensions and then you do some processing on this permuted 3D array. Now, you want to get back to the original order, so you need to swap back the 1st and 3rd dimensions. So, you use [3,2,1] again, like so - permute(permute(A,[3 2 1]),[3 2 1]) and this would be essentially permute(A,[1 2 3]) and yes that's A, back to home!
You can also use
b = shiftdim(a,-2);
As per the documenation,
B = shiftdim(X,N) shifts the dimensions of X by N. When N is
positive, shiftdim shifts the dimensions to the left and wraps the
N leading dimensions to the end. When N is negative, shiftdim
shifts the dimensions to the right and pads with singletons.
A singleton dimension [or simpliy "singleton"] is any dimension dim for which size(A,dim) = 1.

creating matrix from data-set

I have a network with edges and points. The FID shows the ID number of each edge that is created by Start_Point and End_Point. For example edge 3 is between the two points of 2 and 3 in the network.
FID Start_Point End_Point
1 1 2
2 1 4
3 2 3
4 2 4
I want to create a 4-by-4 matrix of these points. if there is an edge between 2 points the value is 1 else is inf:
[inf, 1, inf, 1;
1, inf, 1, 1;
inf, 1, inf, inf;
1, 1, inf, inf]
How can I create such a matrix in MATLAB?
You can convert it to a sparse matrix and then use full command to obtain adjacency matrix.
edges= [1 2;
3 4;
3 1
2 3];
n=size(edges,1);
% create sparse matrix with given edges and their reverse direction
A = sparse([edges(:,1); edges(:,2)],[edges(:,2); edges(:,1)],[ones(n,1); ones(n,1)]);
% create adjacency matrix
B=full(A);
% set zeros to inf
B(B==0)=inf;
and this is the result :
A =
(2,1) 1
(3,1) 1
(1,2) 1
(3,2) 1
(1,3) 1
(2,3) 1
(4,3) 1
(3,4) 1
>> B
B =
Inf 1 1 Inf
1 Inf 1 Inf
1 1 Inf 1
Inf Inf 1 Inf
Edit :
the sparse command create a sparse matrix with addressing values of its elements. One prototype for this command is as follow :
A=sparse(rows,cols,values);
for example A=sparse([1;2],[1,3],[10,5]) is a matrix which A(1,1)=10 and A(2,3)=5 and other elements are zero:
A=sparse([1;2],[1,3],[10,5]);
>> full(A)
ans =
10 0 0
0 0 5
In your case you have to add two directions to sparse matrix (symmetric) and all values are one. So you need to construct sparse matrix as :
A = sparse([edges(:,1); edges(:,2)],[edges(:,2); edges(:,1)],[ones(n,1); ones(n,1)]);
full command convert a sparse matrix to a dense one.
So you basically want to create an adjacency matrix from an adjacency list of edges? The number of your edges (i.e. your FID column) is irrelevant so I'm assuming your input data is of the form
edges = [1 2
1 4
2 3
2 4]
Now the first column of edges is the rows of your adjacency matrix and the second is the columns (or vice versa, it doesn't matter since your matrix is symmetrical)
The simplest solution is to use linear index which you would get via the sub2ind function:
adj = inf(size(edges,2));
idx = sub2ind(size(adj),edges(:,1), edges(:,2))
adj(idx) = 1;
I suspect your edges matrix will already be symmetrical but if it's not then just use
edges_sym = [edges; fliplr(edges)]
instead of edges
You can use accumarray:
edges1 = accumarray([startpoint endpoint]),1);
edges2 = edges1.'; % transpose your matrix, to obtain both edges
edges = edges1+edges2;
edges(edges==0)=inf;
accumarray gathers all points with common indices, pastes the value 1 on those indices. edges1 is the transpose of edges2, thus transpose, then add the two together. Find all indices on which the matrix is 0, then fill those values with inf.
Alternative:
edges= [1 2;
3 4;
3 1
2 3];
matrix = accumarray([edges;fliplr(edges)],1,[],[],inf);
fliplr flips your matrix left to right, to get all the desired combinations of indices. Then use accumarray to set a 1 on all locations specified by edges and put inf at the other locations.
If you are sure your matrix is symmetric, don't use fliplr, if you sure your matrix is non-symmetric, use fliplr and if you are not sure use this:
matrix = accumarray([edges;fliplr(edges)],1,[],#mean,inf);
where the #mean makes sure to set double entries to 1 anyway. For weighted edges do the following, where weights is an Nx1 array containing the weights and N is the number of edges.
matrix = accumarray([edges;fliplr(edges)],weights,[],#mean,inf);

Manipulating sparse matrices in Matlab

Suppose I have a sparse matrix Sparstica that is a vertical concatenation of several other sparse matrices. When I type Sparstica(:), I get a list of the nonzero elements. In the left column, will be the index of the element, in the right column will be the nonzero element.
How can I manipulate the i-th and j-th non-zero element of every other sparse block matrix in the middle n-2 blocks (n sparse block matrices in total)?
Appended: To clarify what I mean by the i-th and j-th element of every other sparse matrix, suppose I have
Sparstica = [A_1; A_2; A_3; ... ; A_n]
This was created from vertcat. Now I need to take the i-th and j-th, say the 3rd and 5th, nonzero element of every other sparse matrix from A_2 to A_{N-1} (I know the notation for this actually isn't allowed, but just for demonstrative purposes). I'd like to accomplish this without using for-loops if possible.
You can find the non-zero elements using find:
>> A = speye(3)
A =
(1,1) 1
(2,2) 1
(3,3) 1
>> I = find(A ~= 0)
I =
1
5
9
If you need the indices in row/column format, use ind2sub:
>> [X, Y] = ind2sub(size(A), I)
X =
1
2
3
Y =
1
2
3