ajdev8
ajdev8

Reputation: 232

How to trim leading and trailing NaN values from n-dimensional array?

This is easy in two dimensions, for example:

>> A = NaN(5,4)
>> A(2:4,2:3) = [1 2; 3 4; 5 6]
>> A(2,2) = NaN                
>> A(4,3) = NaN 

A =

   NaN   NaN   NaN   NaN
   NaN   NaN     2   NaN
   NaN     3     4   NaN
   NaN     5   NaN   NaN
   NaN   NaN   NaN   NaN

>> A(~all(isnan(A),2),~all(isnan(A),1))

ans =

   NaN     2
     3     4
     5   NaN

Note that NaN values in rows and columns that are not all NaN are retained.

How to expand this to multiple dimensions? For example if A has three dimensions:

>> A = NaN(5,4,3)           
>> A(2:4,2:3,2) = [1 2; 3 4; 5 6]
>> A(2,2,2) = NaN                
>> A(4,3,2) = NaN                

A(:,:,1) =

   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN

A(:,:,2) =

   NaN   NaN   NaN   NaN
   NaN   NaN     2   NaN
   NaN     3     4   NaN
   NaN     5   NaN   NaN
   NaN   NaN   NaN   NaN


A(:,:,3) =

   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN

How do I then get

ans =

   NaN     2
     3     4
     5   NaN

I'd like to do this in four dimensions, and with much larger matrixes than the example matrix A here.

Upvotes: 3

Views: 1915

Answers (2)

tzelleke
tzelleke

Reputation: 15345

My solution to the problem based on the input A as posted by OP:

>> [i,j,k] = ind2sub(size(A),find(~isnan(A)));
>> l = min([i j k]);
>> u = max([i j k]);
>> B=A(l(1):u(1),l(2):u(2),l(3):u(3))
B =
   NaN     2
     3     4
     5   NaN
>> size(B)
ans =
     3     2

Since you stated that you want to do this on much larger matrices I'm not sure about the performance of @ronalchn's solution - that is all the all-calls. But I have no idea to what extend that matters - maybe someone can comment...

Upvotes: 1

ronalchn
ronalchn

Reputation: 12335

Try this:

2 dimensions

A(~all(isnan(A),2),~all(isnan(A),1))

3 dimensions

A(~all(all(isnan(A),2),3),...
  ~all(all(isnan(A),1),3),...
  ~all(all(isnan(A),1),2))

4 dimensions

A(~all(all(all(isnan(A),2),3),4),...
  ~all(all(all(isnan(A),1),3),4),...
  ~all(all(all(isnan(A),1),2),4),...
  ~all(all(all(isnan(A),1),2),3))

Basically, the rule is for N dimensions:

  • on all N dimensions you do the isnan() thing.

  • Then wrap it in with the all() function N-1 times,

  • and the 2nd argument each of the all() functions for the ith dimension should be numbers 1 to N in any order, but excluding i.


Since Theodros Zelleke wants to see whose method is faster (nice way of saying he thinks his method is so fast), here's a benchmark. Matrix A defined as:

A = NaN*ones(100,400,3,3);
A(2:4,2:3,2,2) = [1 2; 3 4; 5 6];
A(2,2,2,2) = NaN;A(4,3,2,2) = NaN;
A(5:80,4:200,2,2)=ones(76,197);

His test defined as:

tic;
for i=1:100
[i,j,k,z] = ind2sub(size(A),find(~isnan(A)));
l = min([i j k z]);
u = max([i j k z]);
B=A(l(1):u(1),l(2):u(2),l(3):u(3),l(4):u(4));
end
toc

With results:

Elapsed time is 0.533932 seconds.
Elapsed time is 0.519216 seconds.
Elapsed time is 0.575037 seconds.
Elapsed time is 0.525000 seconds.

My test defined as:

tic;
for i=1:100
isnanA=isnan(A);
ai34=all(all(isnanA,3),4);
ai12=all(all(isnanA,1),2);
B=A(~all(ai34,2),~all(ai34,1),~all(ai12,4),~all(ai12,3));
end
toc

With results:

Elapsed time is 0.224869 seconds.
Elapsed time is 0.225132 seconds.
Elapsed time is 0.246762 seconds.
Elapsed time is 0.236989 seconds.

Upvotes: 1

Related Questions