Vectorizing and assigning structs and fields - arrays

I would like to vectorize operations with structs and have spent some hours searching but I couldn’t find the solution.
I have a struct as follows:
clear all
n= 10;
for i=1:n
mystruct(i).x = i;
mystruct(i).y = i;
mystruct(i).z = 0;
end
I want to add all x to all y values and assign them to z.
This works:
test1 = [mystruct.x] + [mystruct.y];
test2 = arrayfun(#(a)mystruct(a).x+mystruct(a).y,1:n);
However, assigning the array doesn’t:
mystruct.z = [mystruct.x] + [mystruct.y];
mystruct.z = mystruct.x + mystruct.y;
I can generate a new struct:
mystruct2 = struct('z',num2cell ([mystruct.x]+[mystruct.y]));
But the next operation again doesn't work:
mystruct.z = mystruct2.z;
I could of course use a for loop:
for i=1:n
mystruct(i).z = mystruct(i).x + mystruct(i).y;
end
But that’s not what I want as this would make my code quite nasty and will not work for parallel operations using "parfor".
How can I perform this operation?

I believe you want to use the deal function, which is used to distribute inputs to outputs and works with structures starting with MATLAB 7.
With appropriate use of [square brackets], you can use this:
[mystruct2.z] = deal([mystruct(1:n).x] + [mystruct(1:n).y]);
which outputs:
ans =
2 4 6
Yay!

Related

How to return the leaves of a struct as vector in Matlab?

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.
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.
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)

Pythonic way in matlab to decompose array to variables

So I want to decompose array to multiple variables.
For example,
I have 'data' array of (136,9) size which is of double type.
I want to decompose the values of data(1,:) to multiple variables something like below:
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = data(1,:);
In python it was straightforward, but above code gives following error in matlab:
Insufficient number of outputs from right hand side of equal sign to satisfy
assignment.
I can go with something like
frm_id = data(1,1);
seq_id = data(1,2);
%ect
But I do believe there must be matlab (more neat) way to do this operation.
Thanks!
You can use num2cell to convert the matrix to a cell array then copy contents of the cell to each variable:
C = num2cell(data,1);
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = C{:};
I can only suggest you to create a function like this:
function [frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = myfunction (data)
frm_id = data(:,1);
seq_id = data(:,2);
xmin = data(:,3);
ymin = data(:,4);
w = data(:,5);
h = data(:,6);
temp1 = data(:,7);
temp2 = data(:,8);
temp3 = data(:,9);
so in your main code
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = myfunction(data);

Grow 3D array in Matlab

Is there a way to grow a 3D array in the third dimension using the end index in a loop in Matlab?
In 2D it can be done like
a = [];
for x = y
a(end + 1, :) = f(x);
end
But in 3D the same thing will not work as a(1,1,end) will try to index a(1,1,1) the first iteration (not a(1,1,0) as one might expect). So I can't do
im = [];
for x = y
im(:, :, end + 1) = g(x);
end
It seems the end of a in third dimension is handled a bit differently than in the first two:
>> a = [];
>> a(end,end,end) = 1
Attempted to access a(0,0,1); index must be a positive integer or logical.
Am I missing something about how end indexing works here?
What you're asking...
If you know the size of g(x), initialize im to an empty 3d-array:
im = zeros(n, m, 0); %instead of im = [];
I think your code should work now.
A better way...
Another note, resizing arrays each iteration is expensive! This doesn't really matter if the array is small, but for huge matrices, there can be a big performance hit.
I'd initialize to:
im = zeros(n, m, length(y));
And then index appropriately. For example:
i = 1;
for x = y
im(:, :, i) = g(x);
i = i + 1;
end
This way you're not assigning new memory and copying over the whole matrix im each time it gets resized!

Variable dimensions in file don't match with dimension of indexing subscript if one dimension is singleton

I want to test a function func(par1,par2,par3) with all combinations of the parameters par1, par2 and par3 and store the output in a .mat file. My code looks like this at the moment:
n1 = 3;
n2 = 1;
n3 = 2;
parList1 = rand(1,n1); % n1,n2,n3 is just some integer
parList2 = rand(1,n2); % the lists are edited by hand in the actual script
parList3 = rand(1,n3);
saveFile = matfile('file.mat','Writable',true);
% allocate memory
saveFile.output = NaN(numel(parList1),numel(parList2),numel(parList3));
counter1 = 0;
for par1 = parList1
counter1 = counter1 + 1;
counter2 = 0; % reset inner counter
for par2 = parList2
counter2 = counter2 + 1;
counter3 = 0; % reset inner counter
for par3 = parList3
counter3 = counter3 + 1;
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
end
end
This works except if parList3 has only one item, i.e. if n3 = 1. Then the saveFile.output has singleton dimensions and I get the error
Variable 'output' has 2 dimensions in the file, this does not match the 3 dimensions in the indexing subscripts.
Is there a elegant way to fix this?
The expression in the for statement needs to be a row array, not a column array as in your example. The loops will exit after the first value with your code. Set a breakpoint on the saveFile.output command to see what I mean. With a column array, par1 will not be a scalar as desired, but the whole parList1 column. With a row array, par1 will iterate through each value of parList1 as intended
Another thing is that you need to reset your inner counters (counter2 and counter2) or your second and third dimensions will blow up larger than you expected.
The n3=1 problem is expected behavior because matfile defines the variables with fixed number of dimensions and it will treat saveFile.output as 2D. Once you have fixed those issues, you can solve the n3=1 problem by changing the line,
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
to
if n3==1, saveFile.output(counter1,counter2) = sum([par1,par2,par3]);
else saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
By now I realized that actually in matfiles all the singleton dimensions, except for the first two are removed.
In my actual programm I decided to save the data in the file linearly and circumvent matfile's lack of linear indexing capability by using the functions sub2ind and ind2sub.

Referencing Structs inside a Cell Array in Matlab

I'm dealing with a Matlab data structure which is analagous to "MyCellArray" in the following example:
% Create a Struct of string values inside a Cell Array
myCellArray = cell(3,1)
myStruct1 = struct('valA','aaa111','valB','bbb111','valC','ccc111')
myStruct2 = struct('valA','aaa222','valB','bbb222','valC','ccc222')
myStruct3 = struct('valA','aaa333','valB','bbb333','valC','ccc333')
myCellArray{1} = myStruct1
myCellArray{2} = myStruct2
myCellArray{3} = myStruct3
I'd like to be able to efficiently extract some of the data into a new array:
% Extract all valA values from myCellArray
% ArrayOfValA = myCellArray(< somehow get all the valA values >)
DesiredResult = cellstr(['aaa111';'aaa222';'aaa333']) % Or something similar
I'm new to Matlab and I just can't get my head around the notation. I've tried things like:
ArrayOfValA = myCellArray{(:,1).valA} % This is incorrect notation!
The real data is over 500K lines long so I'd like to avoid for loops or other iterative functions if possible. Unfortunately I can't change the original data structure but I suppose I could take a copy and manipulate that (I tried using struct2cell but I just got into another mess!). Is it possible to do this in a fast and efficient way?
Many thanks.
The following appears to work in Octave. I assume it also works in MATLAB:
>> temp = {[myCellArray{:}].valA}
temp =
{
[1,1] = aaa111
[1,2] = aaa222
[1,3] = aaa333
}
Does
myCellAsMat = cell2mat(myCellArray);
ArrayOfValA = vertcat(myCellAsMat(:).valA);
work?
edit: or horzcat, depending on the dimension and desired output of your valA field.

Resources