2

Often I need to access the leaves of data in a structured array for calculations. How is this best done in Matlab 2017b?

% Minimal working example:
egg(1).weight = 30;
egg(2).weight = 33;
egg(3).weight = 34;

someeggs = mean([egg.weight]) % works fine

apple(1).properties.weight = 300;
apple(2).properties.weight = 330;
apple(3).properties.weight = 340;

someapples = mean([apple.properties.weight]) %fails
weights = [apple.properties.weight] %fails too

% Expected one output from a curly brace or dot indexing expression, 
% but there were 3 results.
Jonas Stein
  • 6,826
  • 7
  • 40
  • 72
  • Are you wanting this just for your example, or for any arbitrary structure? – gnovice Oct 30 '17 at 18:07
  • @gnovice for any structure of the type `a(k).b.c....z` where I want the `z(k)` I have added a note, that it is an example. But it is fine, if I need to change a bit in the solution to adopt it to the structure. – Jonas Stein Oct 30 '17 at 18:14

2 Answers2

1

If only the top level is a non-scalar structure array, and every entry below is a scalar structure, you can collect the leaves with a call to arrayfun, then do your calculation on the returned vector:

>> weights = arrayfun(@(s) s.properties.weight, apple)  % To get the vector

weights =

   300   330   340

>> someapples = mean(arrayfun(@(s) s.properties.weight, apple))

someapples =

  323.3333

The reason [apple.properties.weight] fails is because dot indexing returns a comma-separated list of structures for apple.properties. You would need to collect this list into a new structure array, then apply dot indexing to that for the next field weight.

gnovice
  • 125,304
  • 15
  • 256
  • 359
1

You can collect properties into a temporary structure array, and then use it as normal:

apple_properties = [apple.properties];
someapples = mean([apple_properties.weight]) %works

This wouldn't work if you had even more nested levels. Perhaps something like this:

apple(1).properties.imperial.weight = 10;
apple(2).properties.imperial.weight = 15;
apple(3).properties.imperial.weight = 18;
apple(1).properties.metric.weight = 4;
apple(2).properties.metric.weight = 7;
apple(3).properties.metric.weight = 8;

Not that I would advise such a structure, but it works as a toy example. In that case you could, do the same as the previous in two steps... or you could use arrayfun.

weights = arrayfun(@(x) x.properties.metric.weight, apple);
mean(weights)
giusti
  • 3,156
  • 3
  • 29
  • 44