2

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
Santhan Salai
  • 3,848
  • 17
  • 29

2 Answers2

2

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.

rayryeng
  • 96,704
  • 21
  • 166
  • 177
2

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.

Divakar
  • 204,109
  • 15
  • 192
  • 292
  • Very clever. Using `diff` would mean that if all elements for a 3D column are equal, it would generate a sequence of zeroes. You are checking to see if any element in a 3D column is non-zero. – rayryeng Apr 06 '15 at 17:05
  • When 3rd dimension of A is 1, doing `diff(A,[],3)` gives `mxnx0` matrix but still `~any(..,3)` or `all(..,3)` works.. may i know how? – Santhan Salai Apr 06 '15 at 17:36
  • @SanthanSalai Well technically `diff(A,[],3)` in that case is `mxnx1` that is there is just one slice in the third dimension. So next with `~any(..,3)` its trying to find all zeros along all 3D slices, and it's just one slice, so it still works. – Divakar Apr 06 '15 at 17:40
  • I know `mxnx1` is what i expect to get too. but when i try to print it out i get `Empty array: m-by-n-by-0` – Santhan Salai Apr 06 '15 at 17:43
  • @SanthanSalai Well `A` is 3D array, as stated in the question right? It's not possible to get an empty array for `diff(A,[],3)` if A is a non-empty 3D array. If `A` is 2D, then you need to use `~any(diff(A,[],2),2)` that is all those operations along dim2. – Divakar Apr 06 '15 at 17:45
  • Well, if A is `mxnxp` matrix, then `diff(A,[],3)` gives `mxnx(p-1)` matrix. then if `p=1`, then `diff(A,[],3)` should give `mxnx0` rite? that is what i'm getting. but `~any(..,3)` or `all(..,3)` seems to identify the value even when its empty. strange!! – Santhan Salai Apr 06 '15 at 17:59
  • @SanthanSalai - If you only have one slice, then by the definition of the question, you should have a 2D matrix of all 1s. You are essentially checking each 3D column that consists of only one element, and so if you compared something with itself, the output should be 1. Therefore, what I suggest is that you don't use the above code when there is only 1 slice in your 3D matrix. If `size(A,3) == 1`, you should output: `true(size(A))`. A caveat that myself and Divakar should have stated is that you should have **at least two slices** for `A`. Doing this on one slice is ill-defined. – rayryeng Apr 06 '15 at 18:02
  • 1
    @SanthanSalai Ok, I must have cleared this at the start of the comments that I missed out on : `m x n x 1` isn't a 3D array, it's a 2D array. So, if you know that `A` is a 2D array, you need to use the modified code `~any(diff(A,[],2),2)`. – Divakar Apr 06 '15 at 18:03
  • @rayryeng, divakar, Thanks for the help. +1 for both answers – Santhan Salai Apr 06 '15 at 18:06
  • 1
    @SanthanSalai - You're very welcome. Good luck, and thanks for posing an interesting question! – rayryeng Apr 06 '15 at 18:07