How do I remove all fields of a structure that have at least one NaN?
    8 views (last 30 days)
  
       Show older comments
    
    Giovanni Barbarossa
 on 1 Apr 2020
  
    
    
    
    
    Edited: Walter Roberson
      
      
 on 2 Apr 2020
            I have a structure S with n fields, field1, field2 etc.. How do I remove those fields that have at least one NaN?
Thank you
0 Comments
Accepted Answer
  Walter Roberson
      
      
 on 1 Apr 2020
        The question does not permit us to assume that the struct fields are exclusively numeric or that the fields will not be compound data types, or that the initial structure is a scalar structure. We must assume that it might be a non-scalar structure, containing nested data types, and that for any given field at the top level, if there is a nan anywhere inside the object in any of the array dimensions, that the field must be removed from all struct array locations.
Taking this into consideration, you need a real function, and it is probably easiest to make the function recursive.
Under some restricted conditions, this code could be simplified.
function S = structrmnan(S)
    if isstruct(S)
        Sfields = fieldnames(S);
        tc = struct2cell(S);
        cn = cellfun(@containsNan, tc);
        mask = any(cn, 2:ndims(cn));
        tc(any(cn, 2:ndims(cn)),:) = [];
        S = reshape(cell2struct(tc, Sfields(~mask), 1), size(S));
    end
end
function tf = containsNan(V)
    if isnumeric(V)
        tf = nnz(isnan(V))>0;
    elseif ischar(V) || islogical(V)
        tf = false;
    elseif ~isscalar(V)
        tf = any(arrayfun(@containsNan, V(:)));
    elseif iscell(V)
        tf = any(cellfun(@containsNan, V(:)));
    elseif isstruct(V)
        tf = any(structfun(@containsNan, V(:)));
    else
        %for example there could be a NaN inside UserData of an array
        %of line objects
        error('Needs to be extended to handle datatype "%s"', class(V))
    end
end
3 Comments
  Walter Roberson
      
      
 on 1 Apr 2020
				
      Edited: Walter Roberson
      
      
 on 2 Apr 2020
  
			Extended to handle datetime, duration, calendarDuration, categorical, string, table
(I do not promise that all cases with table are handled; for example it would not surprise me if this version failed for a table that contained a nested table.)
function S = structrmnan(S)
    if isstruct(S)
        Sfields = fieldnames(S);
        tc = struct2cell(S);
        cn = cellfun(@containsNan, tc);
        mask = any(cn, 2:ndims(cn));
        tc(mask,:) = [];
        S = reshape(cell2struct(tc, Sfields(~mask), 1), size(S));
    end
end
function tf = containsNan(V)
    if ischar(V) || islogical(V)
        tf = false;
    elseif isnumeric(V) || ...
        isdatetime(V) || isduration(V) || iscalendarduration(V) || ...
        iscategorical(V) || isstring(V) || istable(V) 
        tf = nnz(ismissing(V))>0;
    elseif ~isscalar(V)
        tf = any(arrayfun(@containsNan, V(:)));
    elseif iscell(V)
        tf = any(cellfun(@containsNan, V(:)));
    elseif isstruct(V)
        tf = any(structfun(@containsNan, V(:)));
    else
        error('Needs to be extended to handle datatype "%s"', class(V))
    end
end
More Answers (2)
  darova
      
      
 on 1 Apr 2020
        
      Edited: darova
      
      
 on 1 Apr 2020
  
      Try rmfield
a = [1 nan 3];
bb = [1 2 3];
S.a = a;
S.bb = bb;
S
nms = fieldnames(S);
for i = 1:length(nms)
    f = getfield(S,nms{i});
    if isnan(sum(f))
        S = rmfield(S,nms{i});
    end
end
S
5 Comments
  darova
      
      
 on 1 Apr 2020
				- I would have expected a simpler solution
 
Im a simple man
- Also something like a combination of isnan and any.
 
As you wish
if any(isnan(f))
  James Tursa
      
      
 on 1 Apr 2020
				
      Edited: James Tursa
      
      
 on 1 Apr 2020
  
			@Giovanni: For the invalid variable type, you can test for numeric. E.g.,
if isnumeric(f) && any(isnan(f(:)))
  Image Analyst
      
      
 on 1 Apr 2020
        
      Edited: Image Analyst
      
      
 on 1 Apr 2020
  
      If you want, you can try to use structfun() but it's rather cryptic.  darova's solution is much more intuitive and readable.
By the way, I'd have used any() instead of sum(), k instead of i (since i is the imaginary variable), and dynamic field names:
S.a = [1, nan, 3];
S.bb = [1, 2, 3];
S
nms = fieldnames(S);
for k = 1:length(nms)
	thisField = S.(nms{k});
	if any(isnan(thisField))
		S = rmfield(S, nms{k});
	end
end
S
2 Comments
  James Tursa
      
      
 on 1 Apr 2020
				Including the the type check here as well:
if isnumeric(thisField) && any(isnan(thisField(:)))
See Also
Categories
				Find more on Cell Arrays in Help Center and File Exchange
			
	Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!