How do I use indexing (vectorization?) rather than for loops for dynamic field referencing into a cell array?

4 views (last 30 days)
I think there's a way to eliminate for loops by indexing, or perhaps it's called vectorizing. How do I do this?
For example, I'm trying to plot several vectors of different lengths in the same 3D scatter plot. This code stores 34 DICOM ROI items' double arrays in a 1x34 cell array:
info = dicominfo('filename.dcm');
numberofarrays = 34;
datacells = cell(1,numberofarrays);
names = fieldnames(info.ROIContourSequence.Item_9.ContourSequence);
for loop = 1:numberofarrays
datacells(loop) = {info.ROIContourSequence.Item_9.ContourSequence.(names{loop}).ContourData(:,1)};
end
but this code results in the following error:
datacellsfaster(1:34) = {info.ROIContourSequence.Item_9.ContourSequence.(names{1:34}).ContourData(:,1)};
Expected one output from a curly brace or dot indexing expression, but there were 34 results.
Why was it expecting only one output? How do I eliminate for loops through indexing?

Accepted Answer

Guillaume
Guillaume on 10 Jun 2016
I'm suprised you get Expected one output from a curly brace or dot indexing expression as an error and not Argument to dynamic structure reference must evaluate to a valid field name.
Unfortunately, you cannot pass a cell array as dynamic field names, The dynamic name must be a scalar string so,
.ContourSequence.(name{1:34})
is never going to work.
If all the ContourData have the same size you could eliminate the loop with struct2cell and by concatenating all the CountourData into one big matrix. There's no guarantee that it'd be faster, and it certainly would use up a lot more memory.
You're better off with the loop. In any case, with only 34 iterations that loop should execute very quickly.
  4 Comments
Daniel Bridges
Daniel Bridges on 17 Jun 2016
Edited: Daniel Bridges on 17 Jun 2016
Thank you for clarifying and introducing me to two additional commands. My goal is to pull multiple rows with the same "z" value of an n-by-3 data matrix*, so ismember appears more elegant than looping over the same data repeatedly. Is it faster, though? It might require the same number of array-scans, but would they occur faster from not being in a top-level (user) script? accumarray seems not what I want to do, though, since it sums the resulting value each time it is referenced; rather, I'm seeking to merely extract a series of coordinates. (I'm using cell arrays because each depth has a different number of coordinates.)
My latest example is merely to show progress in knowledge of index use, i.e. using logical indices to pull data. I had initially thought to loop using find, but then with that scratchwork figured out how to do it via indices instead. This relates to my OP because I was actually seeking clarity both for dynamic field referencing and using indices instead of loops to create faster, more elegant code.
*I constructed the n-by-3 data matrix from a cell array from x1y1z1...xnynzn vectors of different lengths stored in a structure. Of course it would be better to pull the code directly, but searching documentation and online it seemed necessary to import the data from structures to cell arrays ...
Guillaume
Guillaume on 17 Jun 2016
ismember, as a built-in function, is going to be faster than a loop. The bottleneck is going to be the accumarray calls due to the use of a non-standard accumulation function (the @(x) {x} anonymous function).
In my example, accumarray does not sum the resulting values, it extracts the series of coordinates exactly as you want. The accumulation function I've specified simply wrap the accumulated vectors (all the values that match a depth) into a cell array.
find is indeed often unnecessary and using logical indexing directly will speed up your code by a small amount indeed.
By the way, I forgot to say, another way to write your initial loop
for loop = 1:numberofarrays
datacells(loop) = {info.ROIContourSequence.Item_9.ContourSequence.(names{loop}).ContourData(:,1)};
end
would be, assuming that numberofarrays == numel(fieldnames(info.ROIContourSequence.Item_9.ContourSequence)):
datacells = structfun(@(seq) {seq.ContourData(:, 1)}, info.ROIContourSequence.Item_9.ContourSequence);
structfun iterates over all the fields of ContourSequence and, again, the anonymous function simply wrap the ContourData of each field into a cell array. I don't expect any significant gain in speed with that syntax, the looping may be faster but you now have the cost of a function call (to the anonymous function).

Sign in to comment.

More Answers (1)

geotocho
geotocho on 28 Oct 2017
Rather than curly brace indexing, the vectorized way of assigning a command to all cells uses parentheses. Much like the traditional array but in this example A and B are cell arrays of the same size. I wish to assign the row of cells in A to B. In this manner dot indexing is allowed.
B(1,:) = A(1,:);

Categories

Find more on Loops and Conditional Statements 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!