0

How can I delete all-zero pages from a 3D matrix in a loop?

I have come up with the following code, though it is not 'entirely' correct, if at all. I am using MATLAB 2019b.

%pseudo data
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;

xy = cat(3,x,y); %this is a 3x2x4 array; (:,:,1) and (:,:,2) are all zeros, 
                 % (:,:,3) is ones and zeros, and (:,:,4) is all ones
                 

%my aim is to delete the arrays that are entirely zeros i.e. xy(:,:,1) and xy(:,:,2),
%and this is what I have come up with; it doesn't delete the arrays but instead,
%all the ones.
for ii = 1:size(xy,3)
    
    for idx = find(xy(:,:,ii) == 0)

        xy(:,:,ii) = strcmp(xy, []); 

    end
    
end
Adriaan
  • 17,741
  • 7
  • 42
  • 75
CinelliT
  • 27
  • 5
  • That's not a good edit. The question doesn't look the same anymore and seem to invalidate the posted answers. It is also not a [mre]. – Sardar Usama Aug 14 '20 at 15:35
  • Apologies, my intention is not to invalidate anyone's efforts, I do appreciate all your assistance. – CinelliT Aug 14 '20 at 15:39
  • Please do not change the meaning of the question so significantly, especially after answers have been posted. In case you have a **new** question after reading these answers, please ask a new question. In case one of these answers helped you, please consider accepting it using the checkmark below the voting arrows, signaling that you no longer require help. – Adriaan Aug 14 '20 at 17:54

2 Answers2

4

Use any to find indices of the slices with at least one non-zero value. Use these indices to extract the required result.

idx = any(any(xy));   % idx = any(xy,[1 2]); for >=R2018b
xy = xy(:,:,idx);
Sardar Usama
  • 19,536
  • 9
  • 36
  • 58
  • I think the OP wanted all-zero pages, so should replace the `any(...)` with `all(...)`? – Justin Aug 14 '20 at 15:18
  • @Justin The OP wants to *delete* all-zero pages. If you use `all` instead of `any`, you will get only those pages that have all non-zero values. In other words, with `all`, pages with at least one but not all zeros will also be removed. – Sardar Usama Aug 14 '20 at 15:30
  • I have updated my question to provide more information. I will try using 'any', as you have suggested, and will comment back. – CinelliT Aug 14 '20 at 15:38
  • Updated; hopefully it is minimal and reproducible. – CinelliT Aug 14 '20 at 15:49
  • @CinelliT Which array is the equivalent of the 3D array `xy` in your edit? – Sardar Usama Aug 14 '20 at 15:52
  • Someone edited my title. My original title was along the lines of how do I remove matrices from a multidimensional array in a for loop. The meanDataInd would be equivalent to xy. But I originally posted a question focusing on a 3D array to simplify things. – CinelliT Aug 14 '20 at 15:55
  • I would imagine that the principle behind deleting all-zero pages is transferable to any dimension? Maybe I am wrong... – CinelliT Aug 14 '20 at 16:03
  • @CinelliT Your code is not minimal. You are creating arrays of size >26 GB. – Sardar Usama Aug 14 '20 at 16:04
  • @CinelliT: If you have an array of size 244141 x 16 x 6 x 3, then you cannot simply remove the array at (:,:,4,1). You have to remove all such arrays along one index of the 3rd dimension, or one index along the 4th dimension. Please give an example input and example output, so we can understand how you want to transform the matrix. – Cris Luengo Aug 14 '20 at 16:07
  • Sardar Usama: reduced the sizes of the arrays; Cris Luengo: ok I will provide you with an example, bear with me a few minutes – CinelliT Aug 14 '20 at 16:12
  • The transformation is going to depend on the length of the 'control' array minus the number of [ ]; for instance, if folder1 enters the for loop with an array size of 24x16x10x6x1, after averaging the 3rd dim, the output should be an array size of 24x16x3 (two [ ] in folder1). Likewise, if folder2 enters the for loop as 24x16x10x1, it should come out as a 24x16x5 (because it only has two [ ]). I hope that makes sense. – CinelliT Aug 14 '20 at 16:30
  • @CinelliT: You want to concatenate a 24x16x3 array and a 24x16x5 array along the 4th dimension? You can’t do that. You can concatenate them along the 3rd dimension, you end up with 24x16x8, and the answers you got here apply. – Cris Luengo Aug 14 '20 at 17:47
3

I am unsure what you'd expect your code to do, especially given you're comparing strings in all-numerical arrays. Here's a piece of code which does what you desire:

x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;

xy = cat(3,x,y);

idx = ones(size(xy,3),1,'logical');  % initialise catching array
for ii = 1:size(xy,3)
   if sum(nnz(xy(:,:,ii)),'all')==0  % If the third dimension is all zeros
       idx(ii)= false;  % exclude it
   end
end

xy = xy(:,:,idx);  % reindex to get rid of all-zero pages

The trick here is that sum(xy(:,:,ii),'all')==0 is zero iff all elements on the given page (third dimension) are zero. In that case, exclude it from idx. Then, in the last row, simply re-index using logical indexing to retain only pages whit at least one non-zero element.

You can do it even faster, without a loop, using sum(a,[1 2]), i.e. the vectorial-dimension sum:

idx = sum(nnz(xy),[1 2])~=0;
xy = xy(:,:,idx);
Adriaan
  • 17,741
  • 7
  • 42
  • 75
  • 2
    This assumes that xy contains >=0 values. You can sum logical xy to fix that – Sardar Usama Aug 14 '20 at 14:10
  • @SardarUsama not necessarily; it assumes that there isn't e.g. `-1, 1` and further zeros. Thanks for the catch, I added a fail-safe using `nnz` – Adriaan Aug 14 '20 at 17:53