How to initialize a Matlab struct array for growing? - matlab

Growing an array, e.g. through x = [x, a] in a loop, is usually frowned upon in Matlab programming, because it leads to one or more resize operations, and therefore preallocation is usually the better option. However, in some contexts, for example as a reduction assignment in a parfor block, it can have advantages. For that purpose, the array has to be created as zero-size. For numerical arrays, the expression for that is [], for cell arrays {}.
For struct arrays, it is not so clear cut. For example initialization as struct() does not create a zero-size struct array, but a 1x1 struct array with no fields. This leads to the error
Error using horzcat
Number of fields in structure arrays being concatenated do not match.
Concatenation of structure arrays requires that these arrays have the same set of fields.
while growing, because the field structure of appended structs is incompatible with the "no fields" struct.
How to initialize a zero-size struct array for growing?

Growing an array by concatenation
x = [x, 0];
is very slow, see here and here. Instead, one should (if pre-allocation is not possible for whatever reason), grow an array by appending a new element like so:
x(end+1) = 0;
The reason is that [x,0] creates a new array, copying the old data into it, at every single loop iteration, whereas the other form extends the array, needing reallocation only occasionally (it doubles the underlying memory size when the array becomes too small).
To initialize an empty struct array (as suggested by #LuisMendo in a comment) one can do this:
s = struct('field1', {}, 'field2', {});
To append to it, do:
s(end+1) = struct('field1', 1, 'field2', 'x');
Alternatively, one can append by
s(end+1).field1 = 5;
s(end).field2 = 'y';
Note that in this case, end+1 only happens when first appending a new element to the array, subsequent fields are written to the last array element.

It turns out the answer is to initialize to a zero-size numeric array, x = []. Even though it is not a struct array, appending a struct a via x = [x, a] generates a 1x1 struct array, to which further structs with the same fields can be appended.
This unintuitive behavior seems to be a general quirk of the Matlab language, in that [] is of "flexible type". It is initially a zero-size array of doubles, but appending singles makes it a single array, and appending cell arrays makes it a cell array.
As #LuisMendo pointed out in the comments, struct([]) directly gives a zero-size struct array with no fields. This syntax may be preferred because it is less ambiguous. Moreover, s = struct('field1', {}, 'field2', {}) may be used to create a zero-size struct array with defined fields.
I just found that #CrisLuengo's proposal in the comments, appending by assignment to end + 1 instead of concatenation, does not work in the context of the question, "initialize for growing". Assignment cannot change the "type" of the struct, which is defined by its fields. Changing from no fields to the fields of whatever struct is to be appended leads to the error "Subscripted assignment between dissimilar structures."
(but see his comment and my answer for clarification)

Related

How to access array/cell element in a uniform way

In MATLAB, the i-th element of an array is accessed by a(i), while the i-th element of a cell is accessed by a{i}. So my code has to do different things in terms of whether a is a cell or not.
Is there a better way to do it? so we can access the i-th element in a 'same'? way.
You can access the ith element of any array in the same way, a(i), and the behaviour is completely uniform: you get back an object of the same class as a. So if a is a 5x5 double array, a(i) is a 1x1 double. Similarly, if a is a cell array, then a(i) is a 1x1 cell. So far, so logical and consistent.
Now, if you want to look inside a cell, you use a{i} but that is a fundamentally different operation. Your question seems to imply that it would be "better" if these two fundamentally different operations had the same syntax. It wouldn't: if this were the case, then the ability to slice a cell array into arbitrary shapes (including 1x1) would be overshadowed.
But you can always write a custom function. Among the many Matlab workarounds I carry with me everywhere, I have the following pair of functions:
function a = ascell(a)
if ~iscell(a), a = {a}; end
and
function a = uncell(a)
if iscell(a) & numel(a) == 1, a = a{1}; end
With uncell.m on your path, b = uncell(a(i)); would give you the ith element of a with the cell wrapping, if any, stripped off.
It is good to have the call to uncell visible in the code because it alerts you (or another maintainer) to the possibility that a might legally be a cell or a non-cell—this is by no means necessarily true in everybody's coding strategy. Nor will my code necessarily follow the same convention as yours when it comes to interpreting the meaning and correct treatment of a cell array where a non-cell was expected, or a non-cell where a cell was expected (and this is another way of explaining why there's no common syntax). This leads me to the question: if the design of your application is such that a can by its nature contain elements with mismatched shapes or types, then why not simply decree that it is always a cell, never a non-cell, and always access the ith element as a{i}?

"Reverse" of arrayfun

I have an array of "object" structures, OBJECT_ARRAY, that I must often transform into individual arrays for each element of the object structures. This can be done using arrayfun. It's more tedious than simply refereeing to OBJECT_ARRAY(k).item1, but that's how The Mathworks chose to do it.
In this case today, I have used those individual arrays and calculated a corresponding derived value, newItem, for each element and I need to add this to the original array of structures. So I have an array of newItems.
Is there a straightforward way to do an assignment for each object in OBJECT_ARRAY so that (effectively) OBJECT_ARRAY(k).newItem = newItems(k) for every index k?
I am using version 2015a.
You shouldn't need arrayfun for any of this.
To get values out, you can simply rely on the fact that the dot indexing of a non-scalar struct or object yields a comma-separated list. If we surround it with [] it will horizontally concatenate all of the values into an array.
array_of_values = [OBJECT_ARRAY.item1];
Or if they are all different sizes that can't be concatenated, use a cell array
array_of_values = {OBJECT_ARRAY.item1};
To do assignment, you can again use the comma separated list on the left and right side of the assignment. We first stick the new values in a cell array so that we can automatically convert them to a comma-separated list using {:}.
items = num2cell(newitems);
[OBJECT_ARRAY.item1] = items{:};

"some_variable = []" type of statement in matlab

Hello and thanks for the help,
I have seen that you can make this statement:
some_variable = []
from what I see, this makes like a void variable for later use.
The thing is I want to make it an array (and that is the problem). I made this
some_variable(:) = []
but there is an error:
In an assignment A(:) = B, the number of elements in A and B must be the same
Thanks
Actually everything in MATLAB is an array, sometimes the arrays are just 1x1, but they are arrays nevertheless (unlike in C/C++ where you have int or int*).
So, when you do var=[] you initialize an empty array, an array of size 0*.
After that, its up to you to initialize it with whatever size you want. var=0 will initialize it as an array of 1x1 size, but you can go bigger, using zeros(size).
Adittionally, if you want to create empty variables of another class, just use <classname>.empty, such as in var=uint32.empty;
*Note that in MATLAB, an array is also infinite dimensional. It's not size 0, its 0x0x0x0x0x.....x0x0. If you want to prove this, try size(var,999999999999) in MATLAB.

error in array of struct matlab

I want to train data on various labels using svm and want svm model as array of struct. I am doing like this but getting the error:
Subscripted assignment between dissimilar structures.
Please help me out
model = repmat(struct(),size);
for i=1:size
model(i) = svmtrain(train_data,labels(:,i),'Options', options);
end
A structure array in MATLAB can only contain structures with identical fields. By first creating an array of empty structures1 and then trying to fill it with SVMStruct structures, you try to create a mixed array with some empty structures, and some SVMStruct structures. This is not possible, thus MATLAB complains about "dissimilar structures" (not-equal structures: empty vs. SVMStruct).
To allocate an array of structs, you will have to specify all fields during initialization and fill them all with initial values - which is rather inconvenient in this case. A very simple alternative is to drop this initialization, and run your loop the other way around2,3:
for ii=sizeOfLabels:-1:1
model(ii) = svmtrain(train_data,labels(:,ii),'Options', options);
end
That way, for ii=sizeOfLabels, e.g. ii=100, MATLAB will call model(100)=..., while model doesn't exist yet. It will then allocate all space needed for 100 SVMStructs and fill the first 99 instances with empty values. That way you pre-allocate the memory, without having to worry about initializing the values.
1Note: if e.g. size=5, calling repmat(struct(),size) will create a 5-by-5 matrix of empty structs. To create a 1-by-5 array of structs, call repmat(struct(),1,size).
2Don't use size as a variable name, as this is a function. If you do that, you can't use the size function anymore.
3i and j denote the imaginary unit in MATLAB. Using them as a variable slows the code down and is error-prone. Use e.g. k or ii for loops instead.

Preallocating a structure with variables of different names, size and type

I am going to use an algorithm in a for loop as an iteration loop. I know that some of the calculations can be done once and the their results can be used as necessary inputs for the algorithm in the for loop so in this way there is no need to calculate the same things in each iteration. To do so, I calculate them once and put them in a structure.
I use structure since I have many variables to be kept to be used in the for loop and their name and size are different. I put them in the structure with the same name for example:
out.A = A;
out.myvector = myvector;
out.s = s;
out.Hx_l = Hx_l;
and so on. some of them are matrices, some of them cubes or variables with a forth dimension and some of them are cells.
Is there any way to preallocate this kind of structure?
You could initialise the structure the following way:
out = struct('A',[],'myvector',[],'s',[],'Hx',[]);
When you assign the variables afterwards, the structure fields will be already created. Usually the contents are not initialised upfront. Quoting Loren:
How important is it to initialize the contents of the struct. Of
course it depends on your specifics, but since each field is its own
MATLAB array, there is not necessarily a need to initialize them all
up front. The key however is to try to not grow either the struct
itself or any of its contents incrementally.