1

I have a data set with multiple fields of which several of the fields are different names for equivalent properties. I have rescaled and adjusted the data so that the quantities are comparable and want to merge them into a single field.

As a toy example, let's say I have:

s = struct('pounds', [nan nan 4.8], 'pennies', [120 370 nan]);
s.pennies = s.pennies/100;

How do I merge my incomplete fields to get the desired output:

snew = struct(pounds, [1.2 3.7 4.8]);
gnovice
  • 125,304
  • 15
  • 256
  • 359
Goods
  • 225
  • 2
  • 10
  • 1
    So what's the desired output in the toy example? Also, what happens if both fields are not `NaN`? – Luis Mendo Oct 15 '18 at 14:12
  • @LuisMendo edited for clarity to include a desired output for the toy example – Goods Oct 15 '18 at 14:16
  • 2
    What if two fields have a number (not `NaN`) in the same position? Example: `s = struct('pounds',[nan,nan,4.8], 'pennies', [120,370,340]/100)` – Luis Mendo Oct 15 '18 at 14:25
  • 1
    You could start with a simple `for` loop through the values in both fields, choose from whichever field you want for each element based one some robust logic... I say *robust* because you need to think about the cases Luis pointed out. – Wolfie Oct 15 '18 at 14:30
  • Perhaps I wasn't clear but I do not have such edge cases, I constructed the toy example with this in mind. – Goods Oct 15 '18 at 14:37

3 Answers3

2

If you have modified your field values such that they should be equivalent, and simply need to combine the non-NaN values, one option is to vertically concatenate the fields then use min or max down each column (which will ignore the NaN values). Then just remove the unwanted field with rmfield:

>> s = struct('pounds', [nan,nan,4.8], 'pennies', [120,370,nan]);
>> s.pounds = min([s.pounds; s.pennies./100], [], 1);  % Scaling included here
>> s = rmfield(s, 'pennies')

s = 

  struct with fields:

    pounds: [1.2000 3.7000 4.8000]
gnovice
  • 125,304
  • 15
  • 256
  • 359
1

The following works for any number of fields. Since it is guaranteed that only one field is not NaN at each position, you can

  1. Convert to a matrix such that each original field becomes a row of the matrix.
  2. Keep only the numbers, ignoring NaN's. By assumption, this gives exactly one number per column.
  3. Arrange that into a struct with the desired field name.

s = struct('pounds',[nan,nan,4.8], 'pennies', [120,370,nan])
s.pennies = s.pennies/100; % example data
target_field = 'pounds'; % field to which the conversion has been done

t = struct2cell(s); % convert struct to cell array
t = vertcat(t{:}); % convert cell array to matrix
t = t(~isnan(t)).'; % keep only numbers, ignoring NaN's
result = struct(target_field, t); % arrange into a struct
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
0

try my two-liner below

c=struct2cell(s);
s=struct('pounds',unique([c{:}]));

even better, you can also do it using the below oneliner

s=struct('pounds',unique(cell2mat(cellfun(@(x) x(:), struct2cell(s),'UniformOutput',false)))')
FangQ
  • 1,444
  • 10
  • 18