Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I preallocate a non-numeric vector in MATLAB?

I've often found myself doing something like this:

unprocessedData = fetchData();  % returns a vector of structs or objects
processedData = [];             % will be full of structs or objects

for dataIdx = 1 : length(unprocessedData) 
    processedDatum = process(unprocessedData(dataIdx));
    processedData = [processedData; processedDatum];
end

Which, whilst functional, isn't optimal - the processedData vector is growing inside the loop. Even mlint warns me that I should consider preallocating for speed.

Were data a vector of int8, I could do this:

% preallocate processed data array to prevent growth in loop
processedData = zeros(length(unprocessedData), 1, 'int8');

and modify the loop to fill vector slots rather than concatenate.

is there a way to preallocate a vector so that it can subsequently hold structs or objects?


Update: inspired by Azim's answer, I've simply reversed the loop order. Processing the last element first forces preallocation of the entire vector in the first hit, as the debugger confirms:

unprocessedData = fetchData();

% note that processedData isn't declared outside the loop - this breaks 
% it if it'll later hold non-numeric data. Instead we exploit matlab's 
% odd scope rules which mean that processedData will outlive the loop
% inside which it is first referenced: 

for dataIdx = length(unprocessedData) : -1 : 1 
    processedData(dataIdx) = process(unprocessedData(dataIdx));
end

This requires that any objects returned by process() have a valid zero-args constructor since MATLAB initialises processedData on the first write to it with real objects.

mlint still complains about possible array growth, but I think that's because it can't recognise the reversed loop iteration...

like image 310
Dan Vinton Avatar asked Feb 26 '09 17:02

Dan Vinton


3 Answers

In addition to Azim's answer, another way to do this is using repmat:

% Make a single structure element:
processedData = struct('field1',[],'field2',[]);
% Make an object:
processedData = object_constructor(...);
% Replicate data:
processedData = repmat(processedData,1,nElements);

where nElements is the number of elements you will have in the structure or object array.

BEWARE: If the object you are making is derived from the handle class, you won't be replicating the object itself, just handle references to it. Depending on your implementation, you might have to call the object constructor method nElements times.

like image 84
gnovice Avatar answered Oct 27 '22 21:10

gnovice


Since you know the fields of the structure processedData and you know its length, one way would be the following:

unprocessedData = fetchData();
processedData = struct('field1', [], ...
                       'field2', []) % create the processed data struct
processedData(length(unprocessedData)) = processedData(1); % create an array with the required length
for dataIdx = 1:length(unprocessedData)
    processedData(dataIdx) = process(unprocessedData(dataIdx));
end

This assumes that the process function returns a struct with the same fields as processedData.

like image 3
Azim J Avatar answered Oct 27 '22 22:10

Azim J


You can pass in a cell array to struct of the appropriate size:

processedData = struct('field1', cell(nElements, 1), 'field2', []);

This will make a structure array that is the same size as the cell array.

like image 3
ManWithSleeve Avatar answered Oct 27 '22 22:10

ManWithSleeve