MATLAB Answers

How do you index an array inside an array of structures?

조회 수: 207(최근 30일)
Neil Patel
Neil Patel 26 Mar 2018
답변: arnold 2 Jan 2019
I would like to index an array which is one of the fields of an array of structures. For example if I have something like:
s(1).field = [1 2 3 4];
s(2).field = [2 4 6 8];
I would like to access all of the 2nd values of s.field. However something like [s.field(2)] gives the error "Expected one output from a curly brace or dot indexing expression, but there were 2 results." I know there are other ways to structure this data but I'm just constructing the simplest example possible (my real application has other fields which are not arrays and a much larger number of entries). With a structure in this format I know I could call [s.field] and reshape it and index but that's a bit hard to read. My current solution is to loop through all the entries but I feel like there must be a better solution.

  댓글 수: 0

로그인 to comment.

채택된 답변

Jan
Jan 26 Mar 2018
편집: Jan 28 Mar 2018
There is no direct method for "nested indexing". Your loop method is the best way, if you pre-allocate the output.
This is a disadvantage of the chosen structure. Using a different method to store the values might be more convenient. Sometimes this can be seen, when the program is in its final state when the bottlenecks are optimized. Therefore it is a smart strategy to write the code as independent as possible from the underlying structure of the data, such that it can be adjusted later without the need to rewrite the complete code.
len = size(s, 1);
v = zeros(len, 1);
for kk = 1:len
v(kk) = s(kk).field(2);
end

  댓글 수: 4

표시 이전 댓글 수: 1
Stephen Cobeldick
Stephen Cobeldick 28 Mar 2018

"Do you have a suggestion for a different method to store data that would allow for more convenient direct nested indexing?"

Use one numeric array.

Neil Patel
Neil Patel 28 Mar 2018
There are a lot of advantages to using a structure in my application which are not clear in my simple example. I'll just live with not being able to directly index.
Jan
Jan 28 Mar 2018
You can create a C-Mex function, which can extract the values faster than the loop in Matlab - probably. But it cannot be very flexible. If the struct array has multiple dimensions and you do not want to extract a scalar, but a vector from the fields, the output might be confusing in general.

로그인 to comment.

추가 답변(4개)

Jan
Jan 28 Mar 2018
편집: Jan 28 Mar 2018
Some timings:
for k = 5000:-1:1 % Backwards for implicit allocation
s(k, 1).field = rand(1, 4);
end
tic;
for k = 1:50
v1 = arrayfun(@(s) s.field(2), s);
end
toc
tic;
for k = 1:50
tmp = vertcat(s.field);
v2 = tmp(:,2);
end
toc
tic;
for k = 1:50
len = size(s, 1);
v3 = zeros(len, 1);
for kk = 1:len
v3(kk) = s(kk).field(2);
end
end
toc
Matlab R2016b/Win7:
Elapsed time is 5.150233 seconds. arrayfun
Elapsed time is 0.155297 seconds. vertcat
Elapsed time is 0.237163 seconds. loop
Stephen's solution is the fastest, but even a loop outperforms arrayfun by a factor of 20. But if the data are larger, creating the temporary array gets more expensive: For s(k).field = rand(1, 400) the timings are:
Elapsed time is 4.973404 seconds. arrayfun
Elapsed time is 1.911395 seconds. vertcat
Elapsed time is 0.257397 seconds. loop
By the way: Do you see the variation for arrayfun? Obviously tic/toc is not very precise. timeit would be better, but here the coarse values are sufficient to show the magnitude of the effects.
The best solution is to decide for storing the data in a numerical array directly instead of distribute it to a struct array. This has another advantage: Each variable, element in a cell and field in a struct needs about 100 bytes overhead. Therefore using one numerical array to store the data can save a lot of time for creating the data and memory.

  댓글 수: 2

Bruno Luong
Bruno Luong 28 Mar 2018
The vertcat solution runtime/memory requirement depends also on the length of the field (and assume data have the same length).
For-loop is a safe bet.
We have discussed some time to time about performance limitation of ARRAYFUN and CELLFUN, the only advantage is compact code.
Stephen Cobeldick
Stephen Cobeldick 28 Mar 2018
+1 for a thorough investigation.

로그인 to comment.


Stephen Cobeldick
Stephen Cobeldick 26 Mar 2018
tmp = vertcat(s.field);
tmp(:,2)

  댓글 수: 4

표시 이전 댓글 수: 1
Stephen Cobeldick
Stephen Cobeldick 28 Mar 2018
I would not consider this as a "workaround": it is probably the simplest and neatest solution for what you describe in your original question, using just a few very basic MATLAB commands. These explain why:
Jan
Jan 28 Mar 2018
+1: See the timings in my 2nd answer. For larger vectors creating the temporary variable gets more expensive than a simple loop.
Neil Patel
Neil Patel 28 Mar 2018
You are right, this is definitely a simple and neat solution to obtaining those values. By saying workaround, I simply meant that it's an alternative to my hypothetical ideal scenario of being able to directly index the desired values.

로그인 to comment.


Bruno Luong
Bruno Luong 28 Mar 2018
편집: Bruno Luong 28 Mar 2018
sf2 = arrayfun(@(s) s.field(2), s)

  댓글 수: 0

로그인 to comment.


arnold
arnold 2 Jan 2019
I've been looking for the same thing and have grown pretty frustrated with this having to create temporary variables or loops all over the place for a long time. Thank you @Jan to have clarified that there is no better option.
I'd love somebody's input on these: I want the second component of the Centroid of the structure given by the regionprops function.
My wishful thinking would be this working
idx = [1,10,55,832];
XCentroids = vertcat(RegionPropsOut(idx).Centroid(2));
Expected one output from a curly brace or dot indexing expression,
but there were 4 results.
with
size(vertcat(RegionPropsOut.Centroid))
ans =
16421 2
but yeah, this doesn't work. So I thought for years there might be a some notation like
XCentroids = vertcat(RegionPropsOut(idx).Centroid)(:,2);
but sure, there is nothing like the "appended parenthesis idea".
There is also no other one-liner that might do it, right.... this would be another idea but no
[~, XCentroid] = vertcat(RegionPropsOut(idx).Centroid);
which gives
Error using vertcat
Too many output arguments.
Is there really no nicer way? Instead of looping I still prefer the also ugly temporary variable like such...
tmp = vertcat(stats1.Centroid);
XCentroid = tmp(:,2);
clear tmp;
this is one of the annoyances I encounter far too often. I'd appreciate an idea to make this nicer and less memory intensive.

  댓글 수: 0

로그인 to comment.

이 질문에 답변하려면 로그인을(를) 수행하십시오.


Translated by